本文為原創,如需轉載,請注明作者和出處,謝謝!
經常有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作為父類了。
新浪微博:http://t.sina.com.cn/androidguy 昵稱:李寧_Lining