摘要:所以在对象没有重写这个方法时,默认使用此方法,即比较对象的内存地址值。结果为可以发现不管对象的内存地址是否相同并不影响其结果,所以类型比较的是数据值而不是内存地址值。
今天朋友突然问到一个问题:
两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode可以不同吗?
在Java编程中,判断两个对象是否相等常常使用equals()或是==,但是其中的区别和原理可能很多人并不完全清楚。今天就借着上面这个问题来看看equals()和==的区别和原理。
1. 数据类型与==的含义Java中的数据类型分为基本数据类型和引用数据类型:
基本类型:编程语言中内置的最小粒度的数据类型。它包括四大类八种类型
4种整数类型:byte、short、int、long
2种浮点数类型:float、double
1种字符类型:char
1种布尔类型:boolean
引用类型:引用也叫句柄,引用类型,是编程语言中定义的在句柄中存放着实际内容所在地址的地址值的一种数据形式
类
接口
数组
对于基本类型来说,== 比较的是它们的值
对于引用类型来说,== 比较的是它们在内存中存放的地址(堆内存地址)
例:
public void test(){ int num1 = 100; int num2 = 100; String str1 = "James"; String str2 = "James"; String str3 = new String("James"); String str4 = new String("James"); System.out.println("num1 == num2 : " + (num1 == num2)); System.out.println("str1 address : " + System.identityHashCode(str1) + "; str2 address : " + System.identityHashCode(str1) + "; str1 == str2 : " + (str1 == str2)); System.out.println("str3 address : " + System.identityHashCode(str3) + "; str4 address : " + System.identityHashCode(str4) + "; str3 == str4 : " + (str3 == str4)); }
运行上面的代码,可以得到以下结果:
num1 == num2 : true str1 address : 1174290147; str2 address : 1174290147; str1 == str2 : true str3 address : 1289696681; str4 address : 1285044316; str3 == str4 : false
可以看到str1和str2的内存地址都是1174290147,所以使用==判断为true,但是str3和str4的地址是不同的,所以判断为false。
2. equals() 方法解析在Java语言中,所有类都是继承于Object这个超类的,在这个类中也有一个equals()方法,那么我们先来看一下这个方法。
public boolean equals(Object obj) { return (this == obj); }
可以看得出,这个方法很简单,就是比较对象的内存地址的。所以在对象没有重写这个方法时,默认使用此方法,即比较对象的内存地址值。但是类似于String、Integer等类均已重写了equals()。下面以String为例。
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = length(); if (n == anotherString.length()) { int i = 0; while (n-- != 0) { if (charAt(i) != anotherString.charAt(i)) return false; i++; } return true; } } return false; }
很明显,String的equals()方法仅仅是对比它的 数据值,而不是对象的 内存地址 。
以 String 为例测试一下。
public void test() { String str1 = "James"; String str2 = "James"; String str3 = new String("James"); String str4 = new String("James"); System.out.println("str1 address : " + System.identityHashCode(str1) + "; str2 address : " + System.identityHashCode(str1) + "; str1.equals(str2) : " + str1.equals(str2)); System.out.println("str3 address : " + System.identityHashCode(str3) + "; str4 address : " + System.identityHashCode(str4) + "; str3.equals(str4) : " + str3.equals(str4)); }
结果为:
str1 address : 1174290147; str2 address : 1174290147; str1.equals(str2) : true str3 address : 1289696681; str4 address : 1285044316; str3.equals(str4) : true
可以发现不管对象的内存地址是否相同并不影响其结果,所以String类型比较的是 数据值, 而不是 内存地址值。
所以总结一下equals() 和 == 的区别:
==
基本类型:对比它们的值是否相等
引用类型:对比它们的内存地址是否相等
equals()
基本类型:使用==进行比较
引用类型:默认情况下,对比它们的地址是否相等;如果equals()方法被重写,则根据重写的要求来比较。
3. equals() 与 hashCode()在详细的了解了==和equals()的作用和区别后,现在来研究一下之前的那个问题:
两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode可以不同吗?
首先我们需要知道hashCode到底是什么?还是从Object这个超类来看一下。
public int hashCode() { return identityHashCode(this); // 此处返回对象的内存地址值 }
代码也很简单,看来默认情况下,hashCode就等于对象的 内存地址值(注:System.identityHashCode(Object obj)方法用于获取对象的内存地址,之前的样例代码中有使用)。和equals()方法一样重写之后,hashCode()方法方法也是可以被重写的,而且两者一般情况下都是成对出现。
简单测试一下String类型重写hashCode()方法之后有什么变化。
public void test() { String str1 = "James"; System.out.println("str1 address : " + System.identityHashCode(str1) + " str1 hashCode : " + str1.hashCode()); }
结果为:
str1 address : 1174290147 str1 hashCode : 71338276
很明显,hashCode 已经不是内存地址了。
那么总结一下:
equals():默认情况下比较的是对象的 内存地址值,被重写后按照重写要求进行比较,一般是比较对象的 数据值
hashCode(): 默认情况下为对象的 内存地址值,被重写后按照重写要求生成新的值。
到此对于刚开始提出的问题应该很好解决了。对于这两个对象,只要我们重写equals()方法,就可以比较对象的 数据值,而不重写hashCode()方法,此时两个对象的 hashCode 就默认为内存地址值了,只要将两个对象指向不同的地址即可。
验证环节,先创建一个类:
public class CustomBean { private String name; private int age; public CustomBean(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CustomBean that = (CustomBean) o; return age == that.age && Objects.equals(name, that.name); } // @Override // public int hashCode() { // return Objects.hash(name, age); // } }
创建测试方法:
@Test public void test() { CustomBean x = new CustomBean("James", 18); CustomBean y = new CustomBean("James", 18); System.out.println("x.hashCode: " + x.hashCode()); System.out.println("x address : " + System.identityHashCode(x)); System.out.println("y.hashCode: " + y.hashCode()); System.out.println("x address : " + System.identityHashCode(y)); System.out.println("x and y is equals : " + x.equals(y)); }
运行结果为:
x.hashCode: 1174290147 x address : 1174290147 y.hashCode: 1289696681 x address : 1289696681 x and y is equals : true
很明显,这就是问题中所描述的那种情况:两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode不相同。
4. 总结至此,==和equals()的区别及作用,equals()和hashCode的关系及使用已经了解清楚了。下面再总结一下:
对于equals() 和 == 的区别:
==
基本类型:对比它们的值是否相等
引用类型:对比它们的内存地址是否相等
equals()
基本类型:使用==进行比较
引用类型:默认情况下,对比它们的地址是否相等;如果equals()方法被重写,则根据重写的要求来比较
对于equals()和hashCode()的关系:
根据Object超类中的文档说明,equals()和hashCode()两个方法应该 同进同退。上面的例子只是举例说明存在那种情况,但那并不是一个很好的应用。
所以一定要记住equals()和hashCode()两个方法应该 同进同退。
所以一定要记住equals()和hashCode()两个方法应该 同进同退。
所以一定要记住equals()和hashCode()两个方法应该 同进同退。
重要的事情说三遍。
欢迎您关注我的博客主页:James Blog
阅读原文
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73908.html
摘要:所以也就是说在没有的基础上,执行代码会在串池中创建一个,也会在堆内存中再出来一个。不可变性的优点安全性字符串不可变安全性的考虑处于两个方面,数据安全和线程安全。 摘要: String基本特性,String源码,为什么String不可变? 前言 基于字符串String在java中的地位,关于String的常识性知识就不多做介绍了,我们先来看一段代码 public class Test {...
摘要:前言我们知道在使用时,我们需要通过去创建实例,譬如为的配置文件那么我们看下方法的具体实现创建实例并执行解析主要通过执行对配置文件的解析,具体实现如下文配置文件解析解析标签解析标签解析别名标签解析插件标签解析标签解析标签解析标签从的方法实现我 前言 我们知道在使用 Mybatis 时,我们需要通过 SqlSessionFactoryBuild 去创建 SqlSessionFactory ...
摘要:最近由于项目需要,研究了一下如何用实现视频转换,着实废了点心思,整理整理,写出给自己备忘下。支持的类型有,,,,,,,,等,这些类型,可以利用进行直接转换。 旧文,源地址见这里。 最近由于项目需要,研究了一下如何用Java实现视频转换,着实废了点心思,整理整理,写出给自己备忘下。 思路 由于之前没有没法过相关功能的经验,一开始来真不知道从哪里入手。当然,这个解决,google...
阅读 2561·2019-08-30 10:53
阅读 3157·2019-08-29 16:20
阅读 2915·2019-08-29 15:35
阅读 1724·2019-08-29 12:24
阅读 2846·2019-08-28 18:19
阅读 1821·2019-08-23 18:07
阅读 2262·2019-08-23 15:31
阅读 1125·2019-08-23 14:05