Object
的默认定义
也就是 Java
中所有的对象都会包含这两个方法:
1 | public boolean equals(Object obj) { |
HashCode
哈希码是一种数据结构的算法,从对象计算出来的一个能代表该对象的整数值,这样可以根据哈希码对两个对象做快速比对。
通俗来讲,一个对象的哈希码允许算法和数据结构将对象放入一个隔间,这个隔间可以关联相同的对象,也可以关联多个不同对象,相当于将一堆数据做了一个大致的分类,根据哈希码可以快速缩小范围。
需要遵循的规则:
- 在一个运行的进程中,相等的对象必须要有相同的哈希码
- 相同的哈希码,对象不一定相等
- 重写
equals
,必须重写hashcode
- 不要把哈希码误用为
Key
两个对象比较时,我们可以先比对哈希码,然后再使用 equals
来比对,提高比对效率。
hashcode()
重写,可以根据类中各个对象实例的哈希码加权:
1 |
|
在 Object
类中,hashCode
方法是通过 Object
对象的地址计算出来的,所以同一个对象的哈希码总是相同的。
equals
和 ==
的区别
数据类型的存储:栈和堆
- 基本数据类型只涉及一个存储区:是存在栈内存中的,保存的是数据值本身。
栈内存用于存放基本类型的变量和引用变量,当超过变量的作用域后,java
会自动释放掉为该变量分配的栈内存空间。
- 引用数据类型涉及到两块存储区:对象本身是存储在堆内存中;引用变量是存储在栈内存中,并存放指向该对象堆内存的首地址。
堆内存用于存放由 new
创建的对象和数组,堆中分配的内存由 java
虚拟机自动垃圾回收器来管理。数组和对象在没有引用变量指向它的时候才变成垃圾,不能再被使用,但是仍然占着内存。在随后的一个不确定的时间被垃圾回收器释放掉,这个也是 java
比较占内存的主要原因
如下例所示:引用变量 a
在栈中存储的是,对象 String("AA")
在堆内存中的首地址。
1 | String a = new String("AA"); |
操作符 ==
==
操作符比较的是变量在栈内存中存储的值。比如基本数据类型变量比较的是存储的值;引用数据类型的引用变量比较的是存储的首地址。
操作符 equals
equals
操作符的作用和目标是:比较引用变量指向的对象在堆内存中的值,所有对象都需要通过该操作符来比对是否相等。
1 | public boolean equals(Object obj) { |
但是,回过头来看 Object
中默认的 eqauls
方法中直接使用了 ==
操作符,即需要两个引用变量必须指向同一个对象时才认为是相等的。所以通常自定义类中 eqauls
操作符需要重写,才能确保类对象比较的是堆内存中的值。
区别
综上,可以看到上例两个对象 String("AA")
和 String("AA")
申请了两块堆内存,存储的值都是 AA
;但两块存储区首地址不一样,也就是两个引用变量 a
和 b
在栈内存中存储的值不同。
所以, a==b
的比对结果是 false
,但 a.equals(b)
的比对结果是 true
。
String
的特殊性
查看 String
源码,可以理解它为不可改变的常量,平时使用的字符串拼接或者重新赋值,内部实现其实是重新申请一块存储区存放新字符串,并将首地址返回给引用对象。如下示例:
1 | String c = "StringValue"; |
上例中 StringValue
在编译时就分配了一个常量区存放该对象的值,引用变量 c
存放了该存储区的首地址;在下次出现相同的常量时 StringValue
,会共享同一个存储区,而不会重新申请空间,所以应用变量 d
存放了同一个首地址。即两次输出都为 true
。