天天看點

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類中這個方法的源代碼是?

下一篇:内部類