1、Object類的結構java
上圖爲Object類的結構樹,由此能夠清晰的看到整個Object的架構。其中我的通過搜索、平常開發的總結,認爲Object、clone、equals(Object)、hashCode、getClass、toString這幾個方法相對重要(僅屬我的意見,若有不一樣之見,歡迎討論)。可能有人認爲notify、wait等線程有關的方法也很重要,可是從我的角度出發,我認爲這些方法更應該放在線程裏去研究和討論。程序員
2、native方法介紹面試
咱們都知道,Java的底層是經過C、C++等語言實現的,那麼Java是如何區分這些方法並能準確地去調用的呢?架構
2.1 native關鍵字ide
在Java中,若是一個方法使用native關鍵字來修飾,即代表該方法並非由Java實現的,它是由non-java即C、C++負責實現。其具體的實現方法被編譯在了dll文件中,由Java調用。由native修飾的方法有:測試
2.2 registerNatives()this
registerNatives(),顧名思義,註冊native修飾的方法,其做用是將C、C++等方法映射到Java中由native修飾的方法,這也是Java爲什麼能作到準確地去調用non-java方法。其源碼以下:.net
private static native void registerNatives();
能夠看到,這裏並無執行此方法。Java使用的是靜態代碼塊去執行registerNatives(),源碼以下:線程
static {
registerNatives();
}
2.3 clone()code
protected native Object clone() throws CloneNotSupportedException;
經過上述源碼,咱們知道,clone不是Java原生的方法,且Object提供的複製是淺複製,不是深度複製。
淺複製是指只複製對象的引用,而深度複製則是將原來複制的對象完徹底全的複製出來,此時被複制的對象與複製出來的對象已經沒有任何關係,歸納起來就是,淺複製複製引用與值,引用與值都不變;深度複製複製值,引用變值不變。
能夠看到,clone方法顯式拋出不支持複製的異常,這說明,實現對象的複製是有條件的。
1) 方法由protected修飾,說明若要實現複製,須要繼承Object(默認都是繼承Object的...)
2)返回類型爲Object,代表若要獲得咱們想要複製的結果須要進行類型轉換
3)實現Cloneable接口,不然會拋出不支持複製的異常
如下爲一段測試clone的代碼:
class Son {
private Integer sonAge;
private String sonName;
public Son(Integer sonAge, String sonName) {
super();
this.sonAge = sonAge;
this.sonName = sonName;
}
public Son() {
super();
}
public Integer getSonAge() {
return sonAge;
}
public void setSonAge(Integer sonAge) {
this.sonAge = sonAge;
}
public String getSonName() {
return sonName;
}
public void setSonName(String sonName) {
this.sonName = sonName;
}
}
public class Parent {
public static void main(String[] args) throws CloneNotSupportedException {
Son son = new Son(10,"清然");
Parent parent = new Parent(20,"安然",son);
Parent temp1 = parent.clone();
}
private Integer age;
private String name;
private Son son;
public Parent(Integer age, String name, Son son) {
super();
this.age = age;
this.name = name;
this.son = son;
}
public Parent() {
super();
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
}
此時編譯器提示須要進行類型轉換:
完成類型轉換後,提示須要對異常進行處理,即CloneNotSupportedException:
將異常拋出後,沒有發現任何編譯錯誤,咱們運行main方法,控制檯出現以下錯誤:
Exception in thread "main" java.lang.CloneNotSupportedException: jdkreader.java.lang.object.Parent
at java.lang.Object.clone(Native Method)
at jdkreader.java.lang.object.Parent.main(Parent.java:16)
異常顯示Parent並不支持clone,此時咱們須要實現Cloneable接口:
public class Parent implements Cloneable
咱們能夠看一下複製後的兩個對象的關係:
Son son = new Son(10,"清然");
Parent parent = new Parent(20,"安然",son);
Parent temp1 = (Parent) parent.clone();
Parent temp2 = (Parent) parent.clone();
System.out.println("temp1 == temp2 : " + (temp1 == temp2));
運行結果爲:
temp1 == temp2 : false
很明顯,結果爲false,由於複製出來是一個新的對象,引用不一樣。咱們再對他們的各個變量進行一一比較:
System.out.println("temp1Age == temp2Age : " + (temp1.getAge() == temp2.getAge()));
System.out.println("temp1Name == temp2Name : " + (temp1.getName() == temp2.getName()));
System.out.println("temp1Son == temp2Son : " + (temp1.getSon() == temp2.getSon()));
運行結果爲:
temp1Age == temp2Age : true
temp1Name == temp2Name : true
temp1Son == temp2Son : true
很奇怪,他們的內部屬性卻所有是相同的,這是爲何呢?
由於Object提供的clone是淺複製,若是是基本類型,則複製其值,若是是引用內容,則複製其引用。因此二者指向的地址是相同的,故相等。
2.4 getClass
public final native Class<?> getClass();
此方法返回的是運行時類對象,這點從註釋可明顯看出:
Returns the runtime class of this {@code Object}.
什麼是類對象?咱們知道,在Java中,一切皆對象。在Java中,類是是對具備一組相同特徵或行爲的實例的抽象並進行描述,對象則是此類所描述的特徵或行爲的具體實例。做爲概念層次的類,其自己也具備某些共同的特性,如都具備類名稱、由類加載器去加載,都具備包,具備父類,屬性和方法等。因而,Java中有專門定義了一個類,Class,去描述其餘類所具備的這些特性。
2.5 hashCode
public native int hashCode();
這也是由non-java實現的方法,返回的是一個對象的散列值,類型爲int。通常狀況下,在當前程序的運行期間,一個對象屢次調用hashCode方法,其返回的散列值是相同的。這裏須要注意的是:
若兩個對象相等,則它們的散列值必定相等,反之,散列值相等,兩個對象不必定相等;
若兩個對象不相等,則它們的散列值不必定相等,反之,散列值不一樣,兩個對象必定不相等。
在Java中,有許多地方都應用到了hash,如集合set、map,equals等,這裏不作過多研究。
2.6 equals
public boolean equals(Object obj) {
return (this == obj);
}
關於「==」與equals的區別,面試的時候多數人都被問到過。咱們知道「==」比較基本數據類型時,比較的是值,當比較對象時,比較的是其引用;而equals比較的是兩個對象是否相等。
在Object類中,equals與「==」實際上是等價的,可是咱們在不少狀況下是須要重寫equals方法的,例如比較兩個學生是否爲同一我的,咱們直接使用equals方法確定是不可行的。一般狀況下,咱們認爲若是學號相同,那麼這兩個學生就是同一我的。重寫的示例以下:
Student類
/**
* 用於測試重寫Object類equals方法
*
* @author xuyong
*
*/
public class Student {
private String no; //學號
private String name; //姓名
public Student(String no, String name) {
super();
this.no = no;
this.name = name;
}
public Student() {
super();
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
測試類
Student stu1 = new Student("1", "路人甲");
Student stu2 = new Student("1", "路人乙");
System.out.println(stu1.equals(stu2));
運行,控制檯打印false。此時,咱們重寫一下Student類的equals方法:
@Override
public boolean equals(Object obj) {
if (obj instanceof Student) {
Student stu = (Student)obj;
return no.equals(stu.getNo());
}
return super.equals(obj);
}
再次運行剛剛的代碼,控制檯打印true。因而,咱們便實現了經過學號判斷是否爲同一我的的業務。
不過,騷年們,覺得這就結束了嗎?NO!
因爲Java須要維護hash的通常規律,沒錯就是剛剛標紅的內容:兩個對象相等,那麼他們的散列值必須相等。
可是此時,咱們測試一下他們的hash是否相等,能夠明顯發現,他們是不相等的。
System.out.println(stu1.hashCode() == stu2.hashCode());
因此,咱們在重寫equals方法時,必需要重寫hashCode方法,因爲咱們是經過學號判斷的,因此最好也是使用學號的散列值替代原有的散列值:
@Override
public int hashCode() {
return no.hashCode();
}
此時再運行方法,就會發現返回的是true了。
2.7 finalize
protected void finalize() throws Throwable { } 該方法用於垃圾回收,通常由 JVM 自動調用,通常不須要程序員去手動調用該方法。 ———————————————— 版權聲明:本文爲CSDN博主「t1heluosh1」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/PostersXu/article/details/81947715