Object类简介及equals方法和hashCode方法详解

1、概述

类层次的根类,每个类直接或间接继承Object类,所有对象(包括数组)都实现了这个类的方法(java.lang)

2、构造方法

public Object(){}
//所有子类都默认访问父类的无参构造,是因为所有子类的共同父类Object,只有一个构造方法,并且是一个空构造

3、成员方法

以下方法均用此代码作为示例:

class Student{
    public String name;
    public int age;
    
    public Student(){};
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    } 
}

public int hashCode()//返回对象的哈希码
注意:hashcode是对象的内部地址经过地址计算法转换过来的,并不是实际地址

Student s1 = new Student();
Student s2 = new Student();
Student s3 = d1;
System.out.println(s1.hashCode()+"--"+s2.hashCode()+"--"+s3.hashCode());
//这里s1和s3的哈希地址值一样,这两个引用指向的是一个对象

public final Class getClass()//返回此Object的运行时类,返回值是一个类名,其实返回的是该类的对象
注意:Class:保存字节码文件的类

Student s1 = new Student();
Class c = s1.getClass();    
String str = c.getName();   //getName()方法是Class类的,public String getClass
System.out.println(str);    //打印出的是当前正在运行类的全路径名称(包名和类名)

等效于

//用链式编程改进
String str =d1.getClass.getName(); //对象调完方法后的结果仍是一个对象,可继续调用
System.out.println(str);

public String toString() //返回该对象的字符串表示
源码:

public String toString(){
    return getClass().getName()+'@'+Interger.toHexString(hashCode);
}

从源码种可以看出,返回的是一个对象的运行时全路径+@+以十六进制输出的哈希地址,
其实这个结果是没有什么意义的。建议所有的子类都重写此方法。
//重写此方法,就是把类中所有的成员变量值组成返回

public boolean equals(Object obj) //默认比较的是对象的地址值是否相同,意义不大,一般子类要重写此方法
源码:

public boolean equals(Object obj){
    return this == obj;
}

子类重写比较对象的内容:
第一版:

public boolean equals(Object obj){
    //在本类中比较的是对象的name和age,而name又是引用类型的,不能用==
    //通过查手册,String中重写了equals,比较的是字符串内容
    //由于obj是父类的引用,要调用的是子类特有的name和age,所以必须要向下转型
    Student s = (Student)obj;
    if (this.name.equals(s.name) && this.age == s.age){
        return true;
    } else {
        return false;
    }  
}

问题:考虑到如果接收的是同一对象的引用,其实没必要浪费转型的时间
第二版:提高效率

public boolean equals(Object obj){
    if(this == obj){
        return true;
    }
    Student s = (Student) obj;
    return this.name.equals(s.name) && this.age == s.age;
}

问题:如果传过来的独享不是Student类里的呢?转型就会报错啊,也就是

Demo d = new Demo();
s1.equals(d);//由于Object类是所有类的父类,所以什么类型的独享都可以传过来(多态),这样下一步转型就会有问题,

第三版:提高程序的健壮性

public boolean equals(Object obj){
    if(this == obj){
        return true;
    }
    //为了提高健壮性
    //先判断以下,该对象是否是该类的:对象名 instanceof 类名
    if(! (obj instanceof)Student)){
        return false;
    }
    Student s = (Student) obj;
    return this.name.equals(s.name) && this.age == a.age;
} 

对比底层的实际实现

public boolean equals(Object obj){
	if(this == obj){
		return true;
	}
	if(obj == null){
		return false;
	}
	//同一个类的字节码文件只有一个并且只加载一次
	if(getClass() != obj.getClass)){
		return false;
	}
	Student other = (Student) obj;
	if(age != other){
		return false;
	}
	if(name == null){
		if(other.name != null)
			return false;
	} else if(!name.equals(other.name)){
		return false;
	}
	return true;
}

protected void finalize()//当垃圾回收器确定不存在对该对象的更多引用时,由
对象的垃圾回收器调用此方法,用于垃圾回收,但是什么时候回收并不确定

protected Object clone()//创建并返回对象的一个副本(protected修饰的方法可以在其子类访问,不能在其他类访问,所以要用这个方法需要重写的)
//重写该方法必须实现 Cloneable接口,以指示Object.clone()方法可以合法的对该类实例进行字段复制
//并且这个接口是标记接口,并没有方法,告诉我们实现该接口的类就可以实现对对象的复制了。
eg:

//创建学生对象
	Student s = new Student();
	s.setName("小黑");
	s.setage(20);
	//克隆学生对象
	Student obj = s.clone();
	Student s2 = (Student)obj;
	//以前的做法
	student s3 = s;
	//这两种做法有区别哦(一个是两个引用指向一个对象,另外一个是浅克隆,其实还有一个深克隆,因为一个类有子父关系、接口,继承体系很庞大)
发布了52 篇原创文章 · 获赞 6 · 访问量 1460

猜你喜欢

转载自blog.csdn.net/qq_40488936/article/details/103595964
今日推荐