天天看点

Java进阶——Object类的toString()方法、equals方法、finalize()方法、hashCode方法

JDK类库的根类:Object

  • 这个老祖宗类中的方法我们需要先研究一下,因为这些方法都是所有子类通用的,任何一个类默认继承Object,就算没有直接继承,最终也会间接继承
  • Object类当中的有哪些常用的方法?

    我们去哪里找这些方法呢?

    第一种方法:去源代码当中。(但是这种方式比较麻烦,源代码也比较难)

    第二种方法:去查阅java的类库的帮助文档(java8 API帮助文档)

    什么是API?

    应用程序编程接口(Application Program Interface)

    整个JDK的类库就是一个javase的API

    每一个API都会配置一套API帮助文档

    SUN公司提前写好的这套类库就是API(一般每一份API都对应一份API帮助文档)

    目前为止我们只需要知道这几个方法即可:

    protected Object clone() // 负责对象克隆的。

    int hashCode() // 获取对象哈希值的一个方法。

    boolean equals(Object obj) // 判断两个对象是否相等

    String toString() // 将对象转换成字符串形式

    protected void finalize() // 垃圾回收器负责调用的方法

关于Object类中的toString()方法

  • 源代码长什么样?
public String toString() {
				return this.getClass().getName() + "@" + Integer.toHexString(hashCode());
			}
           
  • SUN公司设计toString()方法的目的是什么?

    toString()方法的作用是什么?

    toString()方法的设计目的是:通过调用这个方法可以将一个“Java对象”转换成“字符串表示形式”

  • 其实SUN公司开发java语言的时候,建议所有的子类都去重写toString()方法

    toString()方法应该是一个简洁的、详实的、易阅读的。

public class Test01{
	public static void main(String[] args){
		MyTime t1 = new MyTime(1970, 1, 1);
		// 一个日期对象转换成字符串形式的话,我可能还是希望能看到具体的日期信息。
		String s1 = t1.toString();

		//MyTime类重写toString()方法之前
		//System.out.println(s1); // [email protected]
		
		//MyTime类重写toString()方法之后
		System.out.println(s1); // 1970年1月1日

		
		//System.out.println(t1.toString()); //1970年1月1日

		// 注意:输出引用的时候,会自动调用该引用的toString()方法。
		System.out.println(t1);
	}
}
class MyTime{
	int year;
	int month;
	int day;

	public MyTime(){
	
	}

	public MyTime(int year, int month, int day){
		this.year = year;
		this.month = month;
		this.day = day;
	}

	// 重写toString()方法
	// 这个toString()方法怎么重写呢?
	// 越简洁越好,可读性越强越好。
	// 向简洁的、详实的、易阅读的方向发展
	public String toString(){
		//return this.year + "年" + this.month + "月" + this.day + "日";
		return this.year + "/" + this.month + "/" + this.day;
	}
}
           

关于Object类中的equals方法

  • equals方法的源代码
public boolean equals(Object obj) {
				return (this == obj);
			}
           

以上这个方法是Object类的默认实现

  • SUN公司设计equals方法的目的是什么?

    以后编程过程当中,都要通过equals方法来判断两个对象是否相等。

    equals方法是判断两个对象是否相等的。

  • 我们需要研究一下Object类给的这个默认的equals方法够不够用!!!

    在Object类中的equals方法当中,默认采用的是“==” 判断两个java对象是否相等。而“ = =”判断的是两个java对象的内存地址,我们应该判断两个java对象的内容是否相等。所以老祖宗的equals方法不够用,需要子类重写equals。

  • 判断两个java 对象是否相等,不能使用“==”,因为“= =”比较的是两个对象的内存地址。
public class Test02{
	public static void main(String[] args){

		// 判断两个基本数据类型的数据是否相等直接使用“==”就行。
		int a = 100;
		int b = 100;
		// 这个“==”是判断a中保存的100和b中保存的100是否相等。
		System.out.println(a == b); //true(相等) false(不相等)

		// 判断两个java对象是否相等,我们怎么办?能直接使用“==”吗?
		// 创建一个日期对象是:2008年8月8日。
		MyTime t1 = new MyTime(2008, 8, 8); //MyTime t1 = 0x1234;
		// 创建了一个新的日期对象,但表示的日期也是:2008年8月8日。
		MyTime t2 = new MyTime(2008, 8, 8); //MyTime t2 = 0x3698;
		
		//测试以下,比较两个对象是否相等,能不能使用“==”???
		// 这里的“==”判断的是:t1中保存的对象内存地址和t2中保存的对象内存地址是否相等。
		System.out.println(t1 == t2); // false
		
		// 重写Object equals方法之前(比较的是对象内存地址)
		/*
		boolean flag = t1.equals(t2);
		System.out.println(flag); //false
		*/
		

		// 重写Object equals方法之后(比较的是内容。)
		boolean flag = t1.equals(t2);
		System.out.println(flag); //true

		// 再创建一个新的日期
		MyTime t3 = new MyTime(2008, 8, 9);
		// 两个日期不相等,就是false。
		System.out.println(t1.equals(t3)); // false

		// 我们这个程序有bug吗?可以运行,但是效率怎么样?低(怎么改造。)
		MyTime t4 = null;
		System.out.println(t1.equals(t4)); //false
	}
}

class MyTime { //extends Object{
	int year;
	int month;
	int day;

	public MyTime(){
	
	}
	public MyTime(int year, int month, int day){
		this.year = year;
		this.month = month;
		this.day = day;
	}

	// 默认的equals方法
	/*
	public boolean equals(Object obj) {
		return (this == obj);
	}
	*/

	/*
	// 重写Object类的equals方法
	// 怎么重写?复制粘贴。相同的返回值类型、相同的方法名、相同的形式参数列表。
	// equals到底应该怎么重写?你自己定,你认为两个对象什么相等的时候表示相等,你就怎么重写。
	public boolean equals(Object obj) {
		// 当年相同,月相同,并且日也相同的时候,表示两个日期相同。两个对象相等。
		// 获取第一个日期的年月日
		int year1 = this.year;
		int month1 = this.month;
		int day1 = this.day;

		// 获取第二个日期的年月日
		//int year2 = obj.year;
		//int month2 = obj.month;
		//int day2 = obj.day;

		if(obj instanceof MyTime){
			MyTime t = (MyTime)obj;
			int year2 = t.year;
			int month2 = t.month;
			int day2 = t.day;
			if(year1 == year2 && month1 == month2 && day1 == day2){
				return true;
			}
		}
		// 程序能够执行到此处表示日期不相等。
		return false;
	}
	*/

	/*
	// 改良equals方法
	public boolean equals(Object obj) {
		// 如果obj是空,直接返回false
		if(obj == null){
			return false;
		}
		// 如果obj不是一个MyTime,没必要比较了 ,直接返回false
		if(!(obj instanceof MyTime)){
			return false;
		}
		// 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。
		// 内存地址相同的时候指向的堆内存的对象肯定是同一个。
		if(this == obj){
			return true;
		}
		// 程序能够执行到此处说明什么?
		// 说明obj不是null,obj是MyTime类型。
		MyTime t = (MyTime)obj;
		if(this.year == t.year && this.month == t.month && this.day == t.day){
			return true;
		}

		// 程序能到这里返回false
		return false;
	}
	*/

	
	//再次改良。
	/*
	public boolean equals(Object obj) {
		// 如果obj是空,直接返回false
		if(obj == null){
			return false;
		}
		// 如果obj不是一个MyTime,没必要比较了 ,直接返回false
		if(!(obj instanceof MyTime)){
			return false;
		}
		// 如果this和obj保存的内存地址相同,没必要比较了,直接返回true。
		// 内存地址相同的时候指向的堆内存的对象肯定是同一个。
		if(this == obj){
			return true;
		}
		// 程序能够执行到此处说明什么?
		// 说明obj不是null,obj是MyTime类型。
		MyTime t = (MyTime)obj;
		return this.year == t.year && this.month == t.month && this.day == t.day ;
	}
	*/

	public boolean equals(Object obj) {
		if(obj == null || !(obj instanceof MyTime)){
			return false;
		}
		if(this == obj){
			return true;
		}
		MyTime t = (MyTime)obj;
		return this.year == t.year && this.month == t.month && this.day == t.day ;
	}

}

/*
class Person{
	private String idCard;
}
*/
           

Java语言当中的字符串String有没有重写toString()方法,有没有重写equals方法???

总结:

  • String类已经重写了equals方法,比较两个字符串不能使用==,必须使用equals,equals是通用的。
  • String类已经重写了toString()方法。

大结论:

  • java中什么类型的数据可以使用”= =“判断

    Java中基本数据类型比较是否相等,使用==

  • java中什么类型的数据需要使用equals判断

    java中所有的引用数据类型统一使用equals方法来判断是否相等。【这是规矩!!】

public class Test03{
	public static void main(String[] args){

		// 大部分情况下,采用这样的方式创建字符串对象
		String s1 = "hello";
		String s2 = "abc";

		// 实际上String也是一个类。不属于基本数据类型。
		// 既然String是一个类,那么一定存在构造方法。
		String s3 = new String("Test1");
		String s4 = new String("Test1");
		// new两次,两个对象内存地址,s3保存的内存地址和s4保存的内存地址不同。
		// == 判断的是内存地址。不是内容。
		System.out.println(s3 == s4); // false

		// 比较两个字符串能不能使用双等号?
		// 不能,必须调用equals方法。
		// String类已经重写equals方法了。
		System.out.println(s3.equals(s4)); // true

		// String类有没有重写toString方法呢?
		String x = new String("动力节点");
		// 如果String没有重写toString()方法,输出结果:java.lang.String@十六进制的地址
		// 经过测试:String类已经重写了toString()方法。
		System.out.println(x.toString()); //动力节点
		System.out.println(x); //动力节点
	}
}
           
// String对象比较的时候必须使用equals方法。
public class Test04{
	public static void main(String[] args){
		/*
		Student s1 = new Student(111, "北京大兴亦庄二小");
		Student s2 = new Student(111, "北京大兴亦庄二小");
		System.out.println(s1 == s2); // false
		System.out.println(s1.equals(s2)); // true
		*/

		Student s1 = new Student(111, new String("北京大兴亦庄二小"));
		Student s2 = new Student(111, new String("北京大兴亦庄二小"));
		System.out.println(s1 == s2); // false
		System.out.println(s1.equals(s2)); // true
	}
}

class Student{
	// 学号
	int no; //基本数据类型,比较时使用:==
	// 所在学校
	String school; //引用数据类型,比较时使用:equals方法。

	public Student(){}
	public Student(int no,String school){
		this.no = no;
		this.school = school;
	}

	// 重写toString方法
	public String toString(){
		return "学号" + no + ",所在学校名称" + school;
	}

	// 重写equals方法
	// 需求:当一个学生的学号相等,并且学校相同时,表示同一个学生。
	// 思考:这个equals该怎么重写呢?
	// equals方法的编写模式都是固定的。架子差不多。
	public boolean equals(Object obj){
		if(obj == null || !(obj instanceof Student)) return false;
		if(this == obj) return true;
		Student s = (Student)obj;
		return this.no == s.no && this.school.equals(s.school);

		//字符串用双等号比较可以吗?
		// 不可以
		//return this.no == s.no && this.school == s.school;
	}
}

           
// equals方法重写的时候要彻底。

public class Test05{
	public static void main(String[] args){
		
		// 多态(自动类型转换。)
		Object o1 = new String("hello world!");
		Object o2 = new User();
		Object o3 = new Address();

		User u1 = new User("zhangsan", new Address("北京","大兴区","11111"));
		User u2 = new User("zhangsan", new Address("北京","大兴区","11111"));

		System.out.println(u1.equals(u2)); // true

		User u3 = new User("zhangsan", new Address("北京","朝阳区","11112"));
		System.out.println(u1.equals(u3)); // false
	}
}

class User{
	// 用户名
	String name; 
	// 用户的住址
	Address addr;

	public User(){
	}
	public User(String name, Address addr){
		this.name = name;
		this.addr = addr;
	}

	// 重写equals方法
	// 重写规则:当一个用户的用户名和家庭住址都相同,表示同一个用户。
	// 这个equals判断的是User对象和User对象是否相等。
	public boolean equals(Object obj){
		// 用户名和用户名相同,住址和住址相同的时候,认定是同一个用户。
		if(obj == null || !(obj instanceof User)) return false;
		if(this == obj) return true;
		
		User u = (User)obj;
		if(this.name.equals(u.name) && this.addr.equals(u.addr)){
			return true;
		}
		return false;
	}
}

class Address{
	String city;
	String street;
	String zipcode;

	public Address(){
	
	}
	public Address(String city,String street,String zipcode){
		this.city = city;
		this.street = street;
		this.zipcode = zipcode;
	}

	// 注意:这里并没有重写equals方法。
	// 这里的equals方法判断的是:Address对象和Address对象是否相等。
	public boolean equals(Object obj){
		if(obj == null || !(obj instanceof Address)) return false;
		if(this == obj) return true;
		// 怎么算是家庭住址相同呢?
		// 城市相同,街道相同,邮编相同,表示相同。
		Address a = (Address)obj;
		if(this.city.equals(a.city) 
			&& this.street.equals(a.street) 
			&& this.zipcode.equals(a.zipcode)){
			return true;
		}
		return false;
	}
}
           

关于Object类中的finalize()方法(非重点 非重点 非重点 了解即可)

  • 在Object类中的源代码:

GC:负责调用finalize()方法。

  • finalize()方法只有一个方法体,里面没有代码,而且这个方法是protected修饰的。
  • 这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法

    不像equals toString , equals和toString()方法是需要程序员写代码调用的。finalize()只需要重写,重写完将来自动会有程序来调用。

  • finalize()方法的执行时机:

    当一个java对象即将被垃圾回收器回收的时候,垃圾回收器负责调用finalize()方法。

  • finalize()方法实际上是SUN公司为java程序员准备的一个时机,垃圾销毁时机。如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法当中。
  • 静态代码块的作用是什么?

    static{

    }

    静态代码块在类加载时刻执行,并且只执行一次。

    这是一个SUN准备的类加载时机。

finalize()方法同样也是SUN为程序员准备的一个时机。

这个时机是垃圾回收时机。

  • 提示:

    java中的垃圾回收器不是轻易启动的,

    垃圾太少,或者时间没到,种种条件下,

    有可能启动,也有可能不启动。

public class Test06{
	public static void main(String[] args){
		/*
		// 创建对象
		Person p = new Person();

		// 怎么把Person对象变成垃圾?
		p = null;
		*/

		// 多造点垃圾
		/*
		for(int i = 0; i < 100000000; i++){
			Person p = new Person();
			p = null;
		}
		*/
		
		for(int i = 0; i < 1000; i++){
			Person p = new Person();
			p = null;

			// 有一段代码可以建议垃圾回收器启动。
			if(i % 2 == 0){
				System.gc(); // 建议启动垃圾回收器。(只是建议,可能不启动,也可能启动。启动的概率高了一些。)
			}
		}		

	}
}

// 项目开发中有这样的业务需求:所有对象在JVM中被释放的时候,请记录一下释放时间!!!
// 记录对象被释放的时间点,这个负责记录的代码写到哪里?
// 写到finalize()方法中。
class Person{

	// 重写finalize()方法
	// Person类型的对象被垃圾回收器回收的时候,垃圾回收器负责调用:p.finalize();
	protected void finalize() throws Throwable {
		// this代表当前对象
		System.out.println(this + "即将被销毁!");
	}

}
           

hashCode方法:

在Object中的hashCode方法是怎样的?

这个方法不是抽象方法,带有native关键字,底层调用C++程序。

hashCode()方法返回的是哈希码:

实际上就是一个java对象的内存地址,经过哈希算法,得出的一个值。

所以hashCode()方法的执行结果可以等同看做一个java对象的内存地址。

public class Test07{
	public static void main(String[] args){
		Object o = new Object();
		int hashCodeValue = o.hashCode();

		// 对象内存地址经过哈希算法转换的一个数字。可以等同看做内存地址。
		System.out.println(hashCodeValue); //798154996

		MyClass mc = new MyClass();
		int hashCodeValue2 = mc.hashCode();
		System.out.println(hashCodeValue2); //1392838282

		MyClass mc2 = new MyClass();
		System.out.println(mc2.hashCode()); // 523429237
	}
}

class MyClass
{
}
           

回顾总结:

toString()方法

以后所有类的toString()方法是需要重写的。

重写规则,越简单越明了就好。

System.out.println(引用); 这里会自动调用“引用”的toString()方法。

String类是SUN写的,toString方法已经重写了。

equals()方法

以后所有类的equals方法也需要重写,因为Object中的equals方法比较的是两个对象的内存地址,我们应该比较内容,所以需要重写。

重写规则:自己定,主要看是什么和什么相等时表示两个对象相等。

基本数据类型比较实用:==

对象和对象比较:调用equals方法

String类是SUN编写的,所以String类的equals方法重写了。

以后判断两个字符串是否相等,最好不要使用==,要调用字符串对象的equals方法。

注意:重写equals方法的时候要彻底。

finalize()方法。

这个方法是protected修饰的,在Object类中这个方法的源代码是?

下一篇:内部类