默认方法
一、toString()方法
每个类都继承自 Object
类,而 Object
类中有一个默认的 toString()
方法。默认情况下,toString()
方法返回的是 类的全名(包括包名) 和 该类实例的哈希值(以十六进制表示)。
默认的 toString()
方法:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
解释 :
getClass().getName()
:返回当前对象所属的类的全名(包括包名)。hashCode()
:返回对象的哈希值,它是基于对象的内存地址生成的,默认情况下,hashCode()
的值与该对象的内存地址相关,但它并不直接等于内存地址。hashCode()
是一个整数值,用十六进制表示。Integer.toHexString(hashCode())
:将哈希值转换为十六进制字符串。
示例:
public class Demo {
public static void main(String[] args) {
Object obj = new Object(); // 创建一个 Object 类的实例
System.out.println(obj.toString()); // 调用默认的 toString 方法
}
}
输出:
java.lang.Object@1b6d3586
自定义 toString()
方法:
通常,开发者会重写 toString()
方法,以便返回更有意义的对象描述(而不仅仅是类名和哈希值)。比如,返回对象的字段值:
示例:重写 toString()
方法
class Dog {
String name;
int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Dog{name='" + name + "', age=" + age + "}";
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("Buddy", 5);
System.out.println(dog); // 调用重写后的 toString 方法
}
}
输出:
Dog{name='Buddy', age=5}
二、equals()方法
equals()
方法是 Object
类的一个方法,它用于比较两个对象是否相等。所有的类都继承了 Object
类,因此每个类都有一个 equals()
方法。如果没有在子类中重写 equals()
方法,那么将使用 Object
类中的默认实现。
1. equals()
方法的默认实现:
Object
类中的默认 equals()
方法是通过比较两个对象的 引用地址 来判断它们是否相等的。默认的实现如下:
public boolean equals(Object obj) {
return (this == obj); // 判断引用是否相同
}
解释:
this == obj
:比较的是对象引用的地址,即this
(当前对象)和obj
(传入的对象)是否指向同一个内存地址(即它们是否是同一个对象)。
2. 默认 equals()
方法的行为:
- 引用相同的对象:如果两个引用变量指向的是同一个对象(即它们的内存地址相同),则
equals()
返回true
。 - 引用不同的对象:即使两个对象的内容完全相同,只要它们是不同的对象实例(即内存地址不同),默认
equals()
方法会返回false
。
示例代码(使用 Object
类的默认 equals()
方法):
class Dog {
String name;
Dog(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("Buddy");
Dog dog2 = new Dog("Buddy");
// 使用默认的 equals 方法比较 dog1 和 dog2
System.out.println(dog1.equals(dog2)); // 输出 false
}
}
解释:虽然 dog1
和 dog2
都是 Dog
类的对象,并且它们的 name
属性都为 "Buddy"
,但是因为它们是两个不同的对象(即内存地址不同),所以默认的 equals()
方法返回 false
。
3. 为什么 equals()
方法默认比较引用地址?
默认的 equals()
方法的设计是基于对象的引用地址,因为在没有重写 equals()
方法的情况下,Java 默认假定对象的相等性是由它们的内存地址(引用)来决定的。这是因为 Object
类的 equals()
方法只知道对象的引用,没有关于对象内容的其他信息。
4. 如何重写 equals()
方法?
如果你希望比较两个对象的内容是否相等(而不仅仅是引用是否相同),你应该在类中 重写 equals()
方法。重写 equals()
方法时,通常会比较对象的 字段值 是否相等。
重写 equals()
方法的规则:
- 自反性(Reflexive):对于任何非
null
的引用值x
,x.equals(x)
应该返回true
。 - 对称性(Symmetric):对于任何非
null
的引用值x
和y
,如果x.equals(y)
返回true
,则y.equals(x)
也应该返回true
。 - 传递性(Transitive):对于任何非
null
的引用值x
、y
和z
,如果x.equals(y)
返回true
,且y.equals(z)
返回true
,那么x.equals(z)
应该返回true
。 - 一致性(Consistent):对于任何非
null
的引用值x
和y
,如果在x.equals(y)
多次调用中没有发生改变,那么应该一直返回相同的结果。 - 对
null
的处理:对于任何非null
的引用值x
,x.equals(null)
应该返回false
。
示例:重写 equals()
方法
class Dog {
String name;
Dog(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
// 如果是同一个对象,直接返回 true
if (this == obj) {
return true;
}
// 如果传入的对象为 null 或类型不一致,返回 false
if (obj == null || getClass() != obj.getClass()) {
return false;
}
// 强制转换为 Dog 类型
Dog dog = (Dog) obj;
// 比较 name 字段
return name != null && name.equals(dog.name);
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("Buddy");
Dog dog2 = new Dog("Buddy");
// 使用重写后的 equals 方法比较 dog1 和 dog2
System.out.println(dog1.equals(dog2)); // 输出 true
}
}
在上面的代码中,我们重写了 equals()
方法,确保两个 Dog
对象的 name
字段相同就被认为是相等的。由于 dog1
和 dog2
的 name
都是 "Buddy"
,所以 dog1.equals(dog2)
返回 true
。
5. equals()
方法的常见应用:
- 对象内容比较:
equals()
方法常用于比较两个对象是否具有相同的内容。例如,在比较两个字符串、两个自定义对象时,重写equals()
方法是必不可少的。 - 集合操作:在使用
HashSet
、HashMap
等集合类时,通常会用到equals()
方法来判断对象是否已经存在于集合中。
6. 总结:
- 默认的
equals()
方法:比较的是对象的引用地址,即判断两个对象是否是同一个实例(引用是否相同)。 - 重写
equals()
方法:用于比较两个对象的内容,通常会根据对象的字段值来判断它们是否相等。 - 重写
equals()
方法时,通常还需要重写hashCode()
方法,这是因为equals()
和hashCode()
在集合操作中通常是一起使用的。如果两个对象相等(equals()
返回true
),它们的哈希值也应该相同。
三、hashCode()方法
hashCode()
是 Object
类的一个方法。每个 Java 对象都有一个与之关联的哈希码(hash code
),它是一个整数值,用于表示对象的内存地址的某种方式。hashCode()
方法的默认实现返回的是对象的 内存地址 的某种整数表示。
1. hashCode()
方法的定义和默认实现
hashCode()
方法是 Object
类的一部分,它返回一个 32 位的整数,该整数通常是对象的内存地址的哈希值。Object
类的默认实现如下:
public int hashCode() {
return System.identityHashCode(this);
}
System.identityHashCode(this)
返回当前对象的内存地址的哈希码,它是通过 JNI(Java Native Interface)来实现的。这就是为什么 hashCode()
默认返回的是基于内存地址的哈希值。
2. hashCode()
方法的作用
hashCode()
方法的主要作用是在基于哈希表的数据结构中(如 HashMap
、HashSet
、Hashtable
等)查找、存储和删除对象时提高效率。哈希码帮助哈希表将对象映射到一个特定的桶中,从而优化查找操作。
3. hashCode()
和 equals()
的关系
在 Java 中,hashCode()
方法和 equals()
方法有着紧密的关系,特别是在使用集合类(如 HashMap
和 HashSet
)时。根据 Java 的规定:
- 如果两个对象通过
equals()
比较相等,那么它们的hashCode()
必须相等。 - 如果两个对象的
hashCode()
相等,equals()
不一定返回true
,但是如果equals()
返回true
,则它们的hashCode()
必须相等。
4. 为什么 hashCode()
需要与 equals()
一起重写?
如果你在自定义类中重写了 equals()
方法,那么为了避免潜在的问题(特别是在使用哈希集合类时),你通常也需要重写 hashCode()
方法。否则,集合类(如 HashSet
)可能无法正常工作,因为它们使用 hashCode()
来高效查找对象。
5. hashCode()
的默认行为示例
让我们先看一下 hashCode()
方法的默认实现如何工作:
示例:使用默认 hashCode()
方法
class Dog {
String name;
Dog(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("Buddy");
Dog dog2 = new Dog("Buddy");
System.out.println(dog1.hashCode()); // 输出 dog1 的哈希值
System.out.println(dog2.hashCode()); // 输出 dog2 的哈希值
}
}
输出(每次运行可能不同):
2073669225
1398586099
解释:dog1
和 dog2
是两个不同的 Dog
对象,它们的内存地址不同,因此它们的 hashCode()
也不同。默认情况下,hashCode()
返回的是对象的内存地址的某种整数表示,因此每次运行时值可能会有所不同。
6. 重写 hashCode()
方法的建议
如果你重写了 equals()
方法来比较两个对象的内容,那么你通常也应该重写 hashCode()
方法来确保它们的哈希值一致。下面是重写 hashCode()
方法的常见做法。
重写 hashCode()
方法的规则:
- 相等的对象必须具有相等的哈希值:如果
x.equals(y)
返回true
,那么x.hashCode()
必须等于y.hashCode()
。 - 不相等的对象不要求具有不同的哈希值:不同的对象可能有相同的哈希值,这被称为哈希冲突。
- 一致性:在一个程序的运行期间,如果对象的属性没有改变,
hashCode()
的值应始终保持一致。
示例:重写 hashCode()
方法
class Dog {
String name;
int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Dog dog = (Dog) obj;
return age == dog.age && name.equals(dog.name);
}
@Override
public int hashCode() {
int result = 17; // 任意非零常数
result = 31 * result + (name != null ? name.hashCode() : 0); // name 字段的哈希值
result = 31 * result + age; // age 字段的哈希值
return result;
}
}
public class Test {
public static void main(String[] args) {
Dog dog1 = new Dog("Buddy", 5);
Dog dog2 = new Dog("Buddy", 5);
System.out.println(dog1.hashCode()); // dog1 的哈希值
System.out.println(dog2.hashCode()); // dog2 的哈希值
System.out.println(dog1.equals(dog2)); // 输出 true,因为 name 和 age 相等
}
}
输出:
1998091196
1998091196
true
解释:在重写 hashCode()
方法后,dog1
和 dog2
都有相同的哈希值(因为它们的 name
和 age
字段相同),而且 equals()
方法也返回 true
,表示这两个对象是相等的。
7. hashCode()
和集合类
hashCode()
在使用集合类时非常重要,尤其是 HashMap
、HashSet
、Hashtable
等,它们使用哈希码来优化查找、存储和删除操作。例如:
示例:使用 HashSet
和 hashCode()
import java.util.HashSet;
class Dog {
String name;
int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Dog dog = (Dog) obj;
return age == dog.age && name.equals(dog.name);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
}
public class Test {
public static void main(String[] args) {
HashSet<Dog> set = new HashSet<>();
Dog dog1 = new Dog("Buddy", 5);
Dog dog2 = new Dog("Buddy", 5);
set.add(dog1);
set.add(dog2);
System.out.println(set.size()); // 输出 1,因为 dog1 和 dog2 被认为是相等的
}
}
在这个例子中,dog1
和 dog2
被认为是相等的,因为它们的 name
和 age
字段相等,且它们的 hashCode()
相同。因此,它们被认为是相同的对象,不会被添加到 HashSet
中两次。
8. 总结
hashCode()
方法返回对象的哈希码,默认实现基于对象的内存地址。- 在自定义类中,如果重写了
equals()
方法,通常需要重写hashCode()
方法。 - 重写
hashCode()
方法时,必须保证 相等的对象有相同的哈希值。 hashCode()
方法广泛应用于集合类,如HashMap
和HashSet
,以便快速查找和存储对象。
在不使用hash集合的时候,可以仅需要重写equals(),否则一定重写hashCode()