目录
羿先生的学习笔记[1]:Java的“万类起源”——Object类
0. 序言
本系列博客用以记录本人在学习HIT-CS 软件构造课程中的一些收获,内容会涉及到课堂内容、感悟和本人关于Java语言的一些认识。
1. Object类简介
(1) Object类是Java默认提供的一个类,位于Java.lang包。Java.lang包中包含了Java最基础、最核心的类,无需通过import显式地导入即可使用。
(2) Object类是Java中所有类的父类。所有的类都继承自Object类。即如下两种写法:
public class Person {
String name;
}
和
public class Person extends Object {
String name;
}
是等价的。
也因此,任何一个对象都是Object类的一个实例。运行以下代码,
Person tom = new Person();
System.out.println(tom instanceof Object);
输出:
true
(3) Object类中包含了一些基本的方法。较为常用的经常需要覆写的方法有equals()方法、toString()方法、hashCode()方法等。
2. Object类中常用方法介绍
先定义一个Person类:
public class Person {
String name;
int age;
Gender gender;
public Person(String name, int age, Enum gender){
this.name = name;
this.age = age;
this.gender = gender;
}
}
public enum Gender{
MALE, FEMALE
}
equals()方法
执行如下代码:
Person jimmy = new Person("Jimmy De Santa", 20, Gender.MALE);
Person son_of_mike = new Person("Jimmy De Santa", 20, Gender.MALE);
System.out.println(jimmy == son_of_mike);
System.out.println(jimmy.equals(son_of_mike));
输出:
false
false
Java中使用 == 运算符比较两个对象,实质是比较两个对象的引用地址是否相等,这段代码中虽然两个对象的内容相同 (都是麦克的带孝子) ,但实际是在内存中申请了两个不同的空间来分别存储两个对象,因此使用 == 的判断结果为false。
而equals()方法判断结果为什么为false呢,Object.equals()方法定义如下:
public boolean equals(Object obj) {
return this == obj;
}
我们在Person类中并没有覆写equals方法,因此实际上调用的是其父类Object中定义的equals方法,使用 == 运算符进行比较,因此结果为false。我们在Person类中覆写equals方法:
@Override
public boolean equals(Object obj){
return obj instanceof Person && ((Person) obj).name.equals(name) &&
((Person) obj).age == age && ((Person) obj).gender == gender;
}
并再次运行前述代码,结果为:
false
true
实现了对内容的实质性的比较。
简单总结:如果有比较对象实质性内容的需求,就要覆写equals方法。
toString()方法
toString方法通常被用来描述一个对象的基本信息。运行如下代码:
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
System.out.println(set);
println方法会调用toString()方法,得到如下输出:
[1, 2, 3]
列出了集合中的元素。
如果我们不覆写toString方法,运行以下代码:
Person jimmy = new Person("Jimmy De Santa",20, gender.MALE);
Person tracey = new Person("Tracey De Santa",22, gender.FEMALE);
System.out.println(jimmy);
System.out.println(tracey);
得到结果
person.Person@16b98e56
person.Person@7ef20235
按照 包名.类名@地址 的格式输出
我们为了可以更直观地看到对象的信息,覆写toSring()方法
@Override
public String toString(){
return "name:"+name+"\n"+"age:"+age+"\n"+gender;
}
再次运行前述代码,结果为:
name:Jimmy De Santa
age:20
MALE
name:Tracey De Santa
age:22
FEMALE
总结:toString方法通常用来直观的显示对象的基本信息,在System.out.println()等方法中会被调用,一般需要覆写。
hashCode()方法
执行如下代码:
Set<Person> DeSantas = new HashSet<>();
Person jimmy = new Person("Jimmy De Santa",20, gender.MALE);
Person son_of_mike = new Person("Jimmy De Santa",20, gender.MALE);
System.out.println(DeSantas.add(jimmy));
System.out.println(DeSantas.add(son_of_mike));
System.out.println(DeSantas.size());
得到结果:
true
true
2
两个对象Jimmy和Mike的儿子虽然内容相同,使用equals方法比较的返回值也是true,但仍然成功地被放入了同一个集合De Santa一家中,显然不符合数学上集合的定义。尽管数学上要求集合中每个元素互不相等,但如果每新加入一个元素都要与集合中所有元素进行一次比较,效率将会十分低下。假如De Santa家族有1,000,000个人,新加入一名成员就要调用一百万次equals()方法。因此HashSet等工具实际上运用了散列表技术。每一个对象都有一个对应的hash code,也就是哈希值(散列值)。通过这种方法使其插入、查找、删除操作均为O(1)的时间复杂度。
我们覆写hashCode方法:
@Override
public int hashCode(){
return name.hashCode();
}
并再次运行前述代码,得到结果:
true
false
1
可见只有jimmy被存储到了集合中。
总结:如果需要将对象存储在HashSet、HashList等数据结构中,覆写hashCode()方法是必要的,且通常相同的对象应当具有相同的哈希值,不同的对象应有不同哈希值。