JDK源碼解讀:Object類

1. private static native void registerNatives();

  registerNatives方法主要作用:將C/C++中的方法映射到Java中的native方法,實現方法命名的解耦
  用native關鍵字修飾的函數表明該方法的實現並不是在Java中完成,而是由C/C++去完成,並被編譯成.dll文件,由Java去調用。
  方法的具體實現在dll文件中。不同的平臺,其具體實現有所不同。
  用native修飾,表示操作系統需要提供此方法,需要提供此方法,Java本身需要使用

爲了加深對java本地方法的理解,在網上找到了該方法的C源碼部分,如下:
static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

3.protected native Object clone() throws CloneNotSupportedException;

看,clone()方法又是一個被聲明爲native的方法,因此,我們知道了clone()方法並不是Java的原生方法,具體的實現是有C/C++完成的。clone英文翻譯爲"克隆",其目的是創建並返回此對象的一個副本。形象點理解,這有一輛科魯茲,你看着不錯,想要個一模一樣的。你調用此方法即可像變魔術一樣變出一輛一模一樣的科魯茲出來。配置一樣,長相一樣。但從此刻起,原來的那輛科魯茲如果進行了新的裝飾,與你克隆出來的這輛科魯茲沒有任何關係了。你克隆出來的對象變不變完全在於你對克隆出來的科魯茲有沒有進行過什麼操作了。Java術語表述爲:clone函數返回的是一個引用,指向的是新的clone出來的對象,此對象與原對象分別佔用不同的堆空間。

調用clone方法必須要實現Cloneable接口

4.public final native Class<?> getClass();

getClass()也是一個native方法,返回的是此Object對象的類對象/運行時類對象Class<?>。效果與Object.class相同。

首先解釋下"類對象"的概念:在Java中,類是是對具有一組相同特徵或行爲的實例的抽象並進行描述,對象則是此類所描述的特徵或行爲的具體實例。作爲概念層次的類,其本身也具有某些共同的特性,如都具有類名稱、由類加載器去加載,都具有包,具有父類,屬性和方法等。於是,Java中有專門定義了一個類,Class,去描述其他類所具有的這些特性,因此,從此角度去看,類本身也都是屬於Class類的對象。爲與經常意義上的對象相區分,在此稱之爲"類對象"。

此處主要大量涉及到Java中的反射知識,關於反射相關知識後續也會給出相關博文。

5.public boolean equals(Object obj);

與equals在Java中經常被使用,大家也都知道與equals的區別:

==表示的是變量值完成相同(對於基礎類型,地址中存儲的是值,引用類型則存儲指向實際對象的地址);

equals表示的是對象的內容完全相同,此處的內容多指對象的特徵/屬性。

實際上,上面說法是不嚴謹的,更多的只是常見於String類中。首先看一下Object類中關於equals()方法的定義:

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

由此可見,Object原生的equals()方法內部調用的正是==,與==具有相同的含義。既然如此,爲什麼還要定義此equals()方法?

equlas()方法的正確理解應該是:判斷兩個對象是否相等。那麼判斷對象相等的標尺又是什麼?

如上,在object類中,此標尺即爲==。當然,這個標尺不是固定的,其他類中可以按照實際的需要對此標尺含義進行重定義。如String類中則是依據字符串內容是否相等來重定義了此標尺含義。如此可以增加類的功能型和實際編碼的靈活性。當然了,如果自定義的類沒有重寫equals()方法來重新定義此標尺,那麼默認的將是其父類的equals(),直到object基類。

如下場景的實際業務需求,對於User bean,由實際的業務需求可知當屬性uid相同時,表示的是同一個User,即兩個User對象相等。則可以重寫equals以重定義User對象相等的標尺。

複製代碼
1 package com.corn.objectsummary;
2
3 public class User {
4
5 private int uid;
6 private String name;
7 private int age;
8
9 public int getUid() {
10 return uid;
11 }
12
13 public void setUid(int uid) {
14 this.uid = uid;
15 }
16
17 protected String getName() {
18 return name;
19 }
20
21 public void setName(String name) {
22 this.name = name;
23 }
24
25 public int getAge() {
26 return age;
27 }
28
29 public void setAge(int age) {
30 this.age = age;
31 }
32
33 @Override
34 public boolean equals(Object obj) {
35 if (obj == null || !(obj instanceof User)) {
36 return false;
37 }
38 if (((User) obj).getUid() == this.getUid()) {
39 return true;
40 }
41 return false;
42 }
43 }
複製代碼
複製代碼
1 package com.corn.objectsummary;
2
3 public class ObjectTest implements Cloneable {
4
5 public static void main(String[] args) {
6 User u1 = new User();
7 u1.setUid(111);
8 u1.setName(“張三”);
9
10 User u2 = new User();
11 u2.setUid(111);
12 u2.setName(“張三丰”);
13
14 System.out.println(u1.equals(u2)); //返回true
15 }
16
17 }
複製代碼
ObjectTest中打印出true,因爲User類定義中重寫了equals()方法,這很好理解,很可能張三是一個人小名,張三丰纔是其大名,判斷這兩個人是不是同一個人,這時只用判斷uid是否相同即可。

如上重寫equals方法表面上看上去是可以了,實則不然。因爲它破壞了Java中的約定:重寫equals()方法必須重寫hasCode()方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章