java.lang.Object是如何成爲默認父類的

經常有Java初學者會問爲什麼一個沒有父類的Java類會自動從java.lang.Object類繼承。如下面是一個普通的Java類:

public class Test    // 從Object類繼承
{
    public static void main(String[] args)
    {
        System.out.println(new Test().toString());
    }
}

 

從上面的代碼可以看出,實際上,Test類的父類就是Object,因此,在Test中可以使用Object類的public或protected資源,如toString方法。那麼Java編譯器和JVM到底是如何做的呢?

瞭解這個原因其實並不需要知道JVM的實現細節。只要思考一下對於這種虛擬機程序的原理即可。一般對於這種靠虛擬機運行的語言(如Java、C#等)會有兩種方法處理默認繼承問題。

1. 在編譯源代碼時,當遇到沒有父類的類時,編譯器會將其指定一個默認的父類(一般爲Object),而虛擬機在處理到這個類時,由於這個類已經有一個默認的父類了,因此,VM仍然會按着常規的方法來處理每一個類。對於這種情況,從編譯後的二進制角度來看,所有的類都會有一個父類。

2. 編譯器仍然按着實際代碼進行編譯,並不會做額外的處理。如果一個類沒有顯式地繼承於其他的類,編譯後的代碼仍然沒有父類。然後由虛擬機運行二進制代碼時,當遇到沒有父類的類時,就會自動將這個類看成是Object類的子類(一般這類語言的默認父類都是Object)。

從上面兩種情況可以看出,第1種情況是在編譯器上做的文章,也就是說,當沒有父類時,由編譯器在編譯時自動爲其指定一個父類。第2種情況是在虛擬機上做文章,也就是這個默認的父類是由虛擬機來添加的。那麼Java是屬性哪一種情況呢?其實這個答案很好得出。只需要隨便找一個反編譯工具,並.class文件進行反編譯即可得知編譯器是如何編譯的。就以上面代碼爲例,如果是第1種情況,就算Test沒有父類,但由於編譯器已經爲Test自動添加了一個Object父類,因此,在反編譯後得到的源代碼中的Test類是從Object類繼承的。如果沒是這種情況,那麼就是第2種情況。

現在我們使用JDK帶的反編譯工具javap來反編譯Test.class,先執行下面的命令:

javap Test > Test.txt

打開Test.txt文件後,會看到如下的代碼:

public class Test extends java.lang.Object{
    public Test();
    public static void main(java.lang.String[]);
}

 

再使用下面的命令來得到bytecode代碼:

javap -c Test >Test1.txt

打開Test1.txt後,會看到如下的代碼:

public class Test extends java.lang.Object{
public Test();
  Code:
   0:    aload_0
   1:    invokespecial    #8; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    getstatic    #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:    new    #1; //class Test
   6:    dup
   7:    invokespecial    #22; //Method "<init>":()V
   10:    invokevirtual    #23; //Method java/lang/Object.toString:()Ljava/lang/String;
   13:    invokevirtual    #27; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:    return
}


    從上面兩段代碼可以看出,Test已經從Object繼承了,因此,可以斷定Java是屬性第1種情況,也就是說由編譯器爲沒有父類的類指定了Object作爲其默認父類。如果讀者還不確定,可以直接打開Test.class,看看裏面有沒有Object,圖1是Test.class的十六進制代碼:



                                                圖1

   大家可以看到,Java編譯器已經爲Test指定了一個默認的Object類作爲其父類。目前大多數基於虛擬器的語言都是採用的第1種方法來處理默認父類的,如下面的C#代碼:

using System;

namespace ConsoleApplication1
{
    class Test
    {
        static void Main(string[] args)
        {
            Console.WriteLine(new Test().ToString());
        }
    }
}


 使用ildasm.exe將上面的代碼反編譯後,得到的MSIL代碼如下:

.class private auto ansi beforefieldinit ConsoleApplication1.Test
       extends [mscorlib]System.Object
{
} // end of class ConsoleApplication1.Test


    從上面的代碼可以清楚地看到,Test類已經有一個System.Object作爲父類了。

發佈了2 篇原創文章 · 獲贊 2 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章