目录
概念
Object是java默认提供的一个类。Java中除了Object类,所有类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。Object类是所有类的父类,哪怕你没有显示去继承这个类。
举个例子:
class Person {}
class Student {}
class Women extends Person{}
public class Test10_3 {
public static void func1(Object object) {
System.out.println("object");
}
public static void main(String[] args) {
func1(new Student());
func1(new Women());
func1(new Person());
}
}
我们可以简单看一下Object中有哪些方法:
本篇博客我们主要来熟悉toString()方法、equals()方法、hashcode()方法
获取对象信息 toString方法
我们新建一个类Person
class Person {
public String name;
public int age;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
}
在main方法中实例化一个Person对象,并输出
public static void main(String[] args) {
Person person = new Person("张三",10);
System.out.println(person);
}
我们知道输出的是一个地址:
这个地址是怎么来的呢,我们来看一下代码
在代码执行时,输出会调用println方法,我们来看看println方法的代码
println又会调用valueof这个方法,那么我们再来看valueof方法的代码
而valueof中又有一个toString方法,我们来看看他的代码
getClass().getName()就是获取当前路径(全路径),再加上一个@符号,再加上一个类似地址的东西并调用toHexString方法,将这个地址以16进制输出。
如果我们直接把toString方法写到我们的代码上,程序就不会调用默认的toString方法,而是会执行我们自己写的toString方法。
现在我们在Person类中 右键-->Generate-->toString-->全选-->确定 ,生成toString方法
class Person {
public String name;
public int age;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
//toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
此时再运行程序,输出的就不是一个地址了
如果我们把toString方法里的代码修改以下,输出结果也会跟着改变。
//toString方法
@Override
public String toString() {
return "ok";
}
那么为什么程序会执行我们写的toString方法呢。我们知道,println方法会调用valueof方法,valueof方法才会调用toString方法输出结果,我们再来看一下valueof的代码
Object是所有类的父类,这里发生了动态绑定,本来调用的是父类的toString方法,可我们子类重写了toString方法,因此会执行子类重写的toString方法。
对象比较equals方法
我们先把刚刚生成的toString方法屏蔽,在main方法中实例化两个对象person1和person2,分别用“==”和equals比较person1和person2
public static void main(String[] args) {
Person person1 = new Person("张三",10);
System.out.println(person1);
Person person2 = new Person("张三",10);
System.out.println(person2);
System.out.println("============================");
System.out.println(person1 == person2);
System.out.println(person1.equals(person2));
}
我们知道“==”比较的是person1和person2的值,那equals比较的到底是什么呢?我们来看一下equals的代码
this是谁:谁调用当前方法,谁就是this,这里person1就是this。 我们传入的参数是person2,所以此时equals比较的就是person1 == person2。和上面toString原理相同,如果我们重写equals方法,那么就会执行我们写的equals方法了。
@Override
public boolean equals(Object obj) {
return true;
}
如果我们这样重写equals方法,那么输出结果永远都是true
实际上我们可以按照我们需要的逻辑来重写equals方法,这里展示一个例子
@Override
public boolean equals(Object obj) {
//如果传入的对象为空 返回false
if (obj == null) {
return false;
}
//如果两个引用指向同一个对象 返回true
if (this == obj) {
return true;
}
//如果传入对象不是Person类实例 返回false
if (!(obj instanceof Person)) {
return false;
}
//如果是Person类实例
//将传入对象强转成Person类
Person person = (Person) obj;
//如果名字和年龄都相同 返回true 因为name是String型所以用equals比较
return this.name.equals(person.name) && this.age== person.age;
}
此时运行输出结果就是:
当然你也可以让编译器自动帮你生成:右键Generate-->equals() and hashcode()-->一路next-->完成。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
//马上讲到
@Override
public int hashCode() {
return Objects.hash(name, age);
}
如果以后是自定义的类型,那么一定记住重写equals方法。
hashCode方法
回忆一下,我们刚刚在讲解toString方法时遇到了hashCode()方法,他帮我们计算了一个具体的对象位置,这里涉及数据结构,我们暂时不讲解,只是说他是一个内存地址。
hashCode()方法是一个native方法,底层是用C/C++代码写的。我们看不到。
如果我们没有重写hashCode()方法的话,直接用person1和person2调用hashCode()方法会输出什么结果呢?
public static void main(String[] args) {
Person person1 = new Person("张三",10);
System.out.println(person1.hashCode());
Person person2 = new Person("张三",10);
System.out.println(person2.hashCode());
}
输出结果:
逻辑上,我们现在认为,如果两个对象的姓名年龄都一样的话,在内存中存放时只存一份即可。现在我们用生成的重写的hashCode()方法在运行一次代码
现在我们发现输出结果一样了。后期学到哈希表的时候就知道,两个一样的对象我们想要放在同一个位置,此时就可以利用重写hashCode()方法来做。