Java 的toString() 和 equals()函数简单介绍
toString() 和 equals() 都是java面試中經(jīng)常問到的地方.
特別是1條經(jīng)典問題:? equals 和 "==" 的區(qū)別...
本文就從簡單介紹下這兩個object中的函數(shù).
一. toString()函數(shù)
我們首先看1個簡單的代碼例子:
package Object_kng.Object_baseMethod;class Ob_1{}public class Ob_tostring_1{public static void f(){Ob_1 a = new Ob_1();System.out.printf("%s\n", a.toString());//System.out.printf("%s\n", a); //ok same as below//System.out.println(a); //ok same as below//System.out.printf("%s\n", a.getClass().getName());//System.out.printf("%s\n", a.getClass().toString());} }上面的例子定義了1個空的類 Ob_1. 在下面的啟動類中首先實例化類Ob_1 的一個對象.? 然后調(diào)用了a.toString()函數(shù).
上面代碼是能正確編譯和執(zhí)行的.
問題是既然Ob_1是1個空類, 里面并沒有定義toString()這個函數(shù), 為何還能調(diào)用呢.
1.1 java中所有的類都默認(rèn)繼承基類Object.
原因很簡單, 因為Ob_1的toString()方法是繼承自類Object的.?
Java中所有類都是基類Object的派生子孫類.
如果1個類A在定義中沒有指明繼承哪個類, 那么類A就繼承了類Object!
如果類A extends B呢,? 因為B肯定也是Object類的子孫類, 所以無論如何類A肯定是基類Object的派生類or子孫類.
1.2 toString()是類Object內(nèi)定義的一個方法.
打開jdk api文檔, 我們可以查到toString()函數(shù)是如下介紹的:
toString
public String toString()
返回該對象的字符串表示。通常,toString 方法會返回一個“以文本方式表示”此對象的字符串。結(jié)果應(yīng)是一個簡明但易于讀懂的信息表達(dá)式。建議所有子類都重寫此方法。
Object 類的 toString 方法返回一個字符串,該字符串由類名(對象是該類的一個實例)、at 標(biāo)記符“@”和此對象哈希碼的無符號十六進(jìn)制表示組成。換句話說,該方法返回一個字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
?
返回:
該對象的字符串表示形式。
根據(jù)上面的的介紹, 我們簡單地得出如下的信息:
1. toString()函數(shù)返回1個String對象.
2. toString()函數(shù)可以被重寫, 而且jdk介紹中建議我們?yōu)樗蓄愔貙戇@個方法.
3. 不重寫這個方法的話, 返回值是 getClass().getName() + '@' + Integer.toHexString(hashCode())
4. toString()不是靜態(tài)方法, 必須實例化對象才能調(diào)用.
1.3 不重寫 toString()方法, 其返回值解釋
例如上面的代碼是沒有重寫toString()方法的.
執(zhí)行結(jié)果如下:
[java] Object_kng.Object_baseMethod.Ob_1@19c8f4Object_kng.Object_baseMethod.Ob_1@19c8f45 就是a.toString()的返回值.
根據(jù)上面的解析, 我們得知
Object_kng.Object_baseMethod.Ob_1 是由? a.getClass().getName() 來的.? 這里實際上是類Ob_1的完整類名(包括所在包名).
而19c8f45 是 Integer.toHexString(a.hashCode()) 獲得的.
1.3.1 getClass()方法
這里順便解析一下getClass(),? getClass()方法也是屬于基類Object的1個方法, 它返回的是1個對象所屬的運行時類.
至于什么是運行時呢, 就是那個對象引用當(dāng)前狀態(tài)指向的對象的所屬類.? 這個跟多態(tài)有關(guān).
本人語言表達(dá)能力有限, 還是用例子說明的吧:
package Object_kng.Object_baseMethod;class Ob_2{}class Ob_3 extends Ob_2{}public class Ob_getclass_1{public static void f(){Ob_2 a = new Ob_2();System.out.printf("%s\n", a.getClass());a = new Ob_3();System.out.printf("%s\n", a.getClass());} }本例子有兩個空類, 其中Ob_3 繼承自 Ob_2
下面首先實力化1個Ob_2 的對象a, 然后輸出a.getClass() (實際上輸出a.getClass().toString())
然后把引用a 指向 Ob_3 的另1個對象(多態(tài)), 然后再輸出1次a.getClass().
執(zhí)行結(jié)果:
[java] class Object_kng.Object_baseMethod.Ob_2 [java] class Object_kng.Object_baseMethod.Ob_3可以見到兩次輸出是不同的, 第一次是Ob_2, 第二次是Ob_3.
也就是說運行時類不是指 引用a是屬于哪個類, 而是指a 內(nèi)存指向的對象屬于哪個類, 這個需要多態(tài)的知識.
至于getClass().getName()就是獲取當(dāng)前運行時類的完整類名了.
1.3.2 Integer.toHexString(a.hashCode())
那么"@"后面那串字符是什么呢, 實際上它是該對象的hash碼的16進(jìn)制表示.
java中,每1個對象都有1個唯一的hashcode, 它跟這個對象的內(nèi)存地址有關(guān).
1.4 重寫方法的1個簡單例子:
package Object_kng.Object_baseMethod;class Point_1{private int x;private int y;public Point_1(int x, int y){this.x = x;this.y = y;}public String toString(){return "[" + x +"," + y +"]" ;} }public class Ob_tostring_2{public static void f(){Point_1 a = new Point_1(2,4);System.out.printf("%s\n", a.toString());System.out.printf("%s\n", a); //ok same as belowSystem.out.println(a); //ok same as below} }上面定義了1個關(guān)于點的類, 有x, y的兩個int類型成員(坐標(biāo))
然后重寫了toString()方法. 不在輸出類名和hashcode, 改成輸出x和y的值, 并左一定的格式化.
1.5 重寫方法toString()方法的好處:
1. 方便自定義輸出對象的信息.?
2. 由上面例子中, System.out.prinft(), 和 System.out.println()里面的參數(shù)只放對象名a的話, 實質(zhì)上會自動調(diào)用a.toString().
?? 這樣在coding當(dāng)中就可以利用這兩個函數(shù)方便調(diào)試.
3. 實際上肯定java的自帶的類都重寫了toString()方法, 例如java.util.Date 日期這個類
1.6 toString()方法的一些小結(jié).
通過上面的例子, 我相信讀者最起碼清楚如下1點:
toString()是定義在類Object的1個非靜態(tài)方法.
這意味這個方法必須需要1個已實例化對象才能調(diào)用.
也就是為什么我們定義1個整形變量 int x時, 不能利用x.toString()方法把x轉(zhuǎn)化為字符串.
因為x只是1個基本變量, 而不是1個對象啊.
二.equals()函數(shù)
2.1 "==" 符號比較的是指向?qū)ο蟮膬?nèi)存地址
下面開始講另1個函數(shù)equals(), 在這之前我們看一下,另1個判斷是否相等的符號"=="
接下來又是1個例子:
輸出結(jié)果:
[java] false [java] true
上例子中定義了類Ob_4, 它有兩個成員id 和 name.
下面啟動類中,實例化了類Ob_4的兩個對象a 和 a2, a和a2擁有相同的id和name成員.
但是用 "==" 來比較a 和 a2的話, 它們兩者不相等的, 返回了false
接下來執(zhí)行a2 = a; 這個語句意思就是把引用a2 指向a所指向的對象, 這樣的話a2 和 a就指向Heap區(qū)的同一塊地址了.
再用"==" 比較a 和 a2的話,就返回了true.
由此可見, 用"=="來比較兩個對象的話, 實際是比較對象是否hashcode(), 只有兩個對象"完全"相同, 才會返回true.
2.2 equals默認(rèn)比較的是指向?qū)ο蟮膬?nèi)存地址
實際上equals()也是基類Object的一個方法.
public boolean equals(Object obj)
由此可見:
1. 非靜態(tài)方法, 必須利用已實例化對象調(diào)用.
2. 返回boolean類型
3. 需要1個對象(Object)參數(shù),? 由于Object是java里所有其他類的超類, 這里也是運用了多態(tài).
4. 冇final關(guān)鍵字, equals方法可以被重寫.
我們將上面的代碼改一下, 將" == " 改成 equals:
package Object_kng.Object_baseMethod;class Ob_5{private int id;private String name;public Ob_5(int id, String name){this.id = id;this.name = name;} }public class Ob_equals_2{public static void f(){Ob_5 a = new Ob_5(1,"Kent");Ob_5 a2 = new Ob_5(1,"Kent");System.out.println((a.equals(a2)));a2 = a;System.out.println((a.equals(a2)));} }輸出: [java] false [java] true
見到輸出結(jié)果跟上面例子是一樣的, 所以基類Object的equals方法也是比較內(nèi)存的地址.
實際上equals()方法在基類Object的源代碼如下:
public boolean equals(Objectobj){
??? return (this == obj)
}
也就是equals調(diào)用了 "==", 基本上是等價的.
2.3 equals 和 " == " 并非比較對象的hashCode().
因為hashCode()跟內(nèi)存地址有關(guān), 所以也有人講equals 和 "=="比較的是對象的hashCode().
但這種說法是錯誤的, 因為hashCode()也是基類Object的方法, 也可以重寫.
我們來嘗試重寫hashCode()方法, 來證明觀點:
package Object_kng.Object_baseMethod;class Ob_6{private int id;private String name;public Ob_6(int id, String name){this.id = id;this.name = name;} public int hashCode(){return 1;} }public class Ob_equals_3{public static void f(){Ob_6 a = new Ob_6(1,"Kent");Ob_6 a2 = new Ob_6(2,"David");[System.out.println(a);System.out.println(a2);System.out.println((a.equals(a2)));System.out.println((a == a2));a2 = a;System.out.println((a.equals(a2)));System.out.println((a == a2));} }輸出:
[java] Object_kng.Object_baseMethod.Ob_6@1[java] Object_kng.Object_baseMethod.Ob_6@1[java] false[java] false[java] true[java] true上面例子中, 我們在類Ob_6里重寫了hashCode的方法, 都返回1.
下面實例化兩個對象a 和 a2, 而且兩個對象的 id, name成員都不一樣.
但是輸出對象信息(toString()) 方法, 見到@后面的數(shù)字都是1, 也就是它們的hashCode已經(jīng)相等.
但是后面的比較還是跟之前的例子一樣.
也就證明了 equals默認(rèn)情況下 和 "=="都是比較對象的內(nèi)存地址, 而非hashCode().
2.4 重寫equals()方法.
但是實際生產(chǎn)中, 我需要比較兩個不同的對象, 如果兩者的所有成員相等, 我們就認(rèn)為兩者相等.這是我們就可以重寫equals()方法, 改變它的機(jī)制以適用我們的業(yè)務(wù)需要.
最常見的改寫方法:
package Object_kng.Object_baseMethod;class Ob_7{private int id;private String name;public Ob_7(int id, String name){this.id = id;this.name = name;} public boolean equals(Object obj){Ob_7 ob;try{ob = (Ob_7)obj;}catch(ClassCastException e){System.out.printf("warning: the object is not belonged to Class Ob_7!!\n");return false;}catch(Exception e){e.printStackTrace();return false;}if (this.id == ob.id && this.name == ob.name){return true;}return false;} }public class Ob_equals_4{public static void f(){Ob_7 a = new Ob_7(1,"Kent");Ob_7 a2 = new Ob_7(1,"Kent");Ob_7 a3 = new Ob_7(2,"David");System.out.println((a.equals(a2)));System.out.println((a.equals(a3)));System.out.println((a.equals(new Ob_6(1,"kent"))));a2 = a;System.out.println((a.equals(a2)));} }這個例子在類Ob_7 中重寫了equals方法.
注意,重寫這個方法涉及了多態(tài)的知識, 必須將形參強(qiáng)制轉(zhuǎn)化為對應(yīng)的類, 才可以比較成員.
為防止傳錯了其他類的對象的異常, 最好加上try{}語句來捕捉.
輸出結(jié)果:
符合我們的需要了, 只要id和name相等, 我們就認(rèn)為這兩個對象相等:
[java] hello ant, it's the my meeting with ant![java] true[java] false[java] warning: the object is not belonged to Class Ob_7!![java] false[java] true2.5 Java里有一些自帶的類也重寫了equals()方法.
最明顯的例子就是String類. 下面例子:
package Object_kng.Object_baseMethod;public class Ob_equals_5{public static void f(){String s1 = "abc";String s2 = new String("abc");System.out.println((s1.equals(s2)));System.out.println((s1 == s2));} }輸出: [java] true[java] false
上面定義了兩個字符串對象, 值都是"abc"
用equals比較兩者是相等的.
用 "==" 則不等.
由此可見String類改寫了equals方法, 當(dāng)然更可能是String的其中1個超類改寫了equals方法.
2.5 Java里equals 和 "==" 的區(qū)別
以后大家可以這樣回答面試官:
1. "==" 可以比較基本類型(如 int 和 boolean)或?qū)ο?
2. equals是對象的非靜態(tài)方法, 只能用于比較對象, 不能比較基本類型.
3. "==" 用于對象時, 比較的是對象內(nèi)存地址, equals() 通常情況下也是會比較內(nèi)存地址.
4. 一些類如String() 改寫了equals()方法, 比較的是String對象的值, 而不是內(nèi)存地址.
5. 我們可以改寫equals()方法, 根據(jù)需要來比較兩個對象.
總結(jié)
以上是生活随笔為你收集整理的Java 的toString() 和 equals()函数简单介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java同步机制简单介绍
- 下一篇: Java里的字符串, String类简单