咱們先看一下Java的幫助文檔對於Object的描述:html
Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.java
Object 類是類層次結構的根類。每一個類都使用 Object 做爲超類。全部對象(包括數組)都實現這個類的方法。數組
注意:描述是Every class(全部的類)。有這句話能夠猜測一下,抽象類是繼承了Object。jvm
對於繼承,咱們知道C++語言支持多繼承,Java語言只支持單繼承。那麼Java語言爲何不支持多繼承呢?咱們先看一看多繼承中最典型的鑽石問題(菱型缺陷),以下圖(圖片來源於https://www.cnblogs.com/sddai/p/6516668.html):ide
其中A、B、C、D是四個類,B繼承A,C也繼承A,D又同時繼承了B和C。若是B和C都有test方法,看以下代碼函數
D d = new D();
d.test();
第一句中當new D(); 的時候會不會調用兩次A的構造函數?this
第二句中調用的是B裏面的test方法仍是C裏面的test方法?命令行
爲了不以上的問題,Java採用了折衷的方法,只容許單繼承,但能夠實現多個接口。因此咱們能夠以java語言是單繼承這個前提,來推導一下接口和抽象類是否繼承Object。以下:code
對於抽象類而言:一個普通類確定是繼承了Object,若是一個抽象類再繼承這個普通類,這個時候抽象類確定也是繼承了Object的。而對於沒有繼承任何類的抽象類而言,若是它沒有繼承Object,那麼當一個普通類繼承這個抽象類的時候,這個普通類也確定沒有繼承Object,悖論。因此抽象類確定是繼承了Object。htm
對於接口而言呢:若是接口繼承了Object類。那麼當一個類實現多個接口的時候,那不就至關於繼承了多遍Object?又變成了多繼承?這個問題先放一放。
到目前爲止,以上的言論還都處於猜測階段,如今咱們就來深刻一點,找一下確鑿的「證據」。咱們都知道Java源文件會先編譯成class文件,而後再被jvm執行。那麼若是咱們可以知道父類在class文件中是怎麼存儲的,而後看一下接口編譯成的class文件,不就知道接口是否繼承Object了嗎?如下內容涉及字節碼,來源於《深刻理解Java虛擬機》第二版的6.3節(核心是6.3.4節)。
Java文件編譯而成的class文件是二進制文件,沒有任何分隔符,因此不管是順序仍是數量都是被嚴格規定的。
class文件開始的4個字節是 CAFEBABE,表示這是一個能被虛擬機接受的class文件;緊跟着4個字節表示class文件的版本號;緊接着後面是常量池,前兩個字節是常量中的常量數量,後面是常量池的內容;常量池後面的2個字節表明訪問標誌,好比是否public、接口、註解、枚舉等;緊接着2個字節表明類的索引;類索引後面兩個字節表明父類索引;父類索引後面是接口索引集合,前兩個字節表明集合的大小,後面跟具體的接口索引。以下圖所示:
注:
1. 因爲常量池中常量的數量是用兩個字節存儲的,也就是說單個class文件中的常量池中常量的個數不會超過2個字節。
2. 「索引」 是指在常量池中的第幾項常量(從1開始),佔兩個字節(和常量池中的常量數量佔用空間同樣)。好比類索引爲5,表示類的全類名在常量池中的第5個常量處。
3. 父類索引只使用了兩個字節,這也說明了在class文件中父類最多存在一個(除了Object類的父類索引爲0外,其餘都有值)。
可見,咱們只需找出常量池的結尾,便可找出父類索引,從而肯定一個類的父類是誰?jdk中有一個javap的命令(javap -v xxx)。能夠查看一個類的常量池,從而查看常量池中最後一個常量的值,而後再根據class文件找出對應的值,便可肯定常量池的末尾。
例:TestJ1.java 以下:
public class TestJ1 {
}
使用UltraEdit打開TestJ1.class文件,使用命令行輸入命令:「javap -v TestJ1」。以下圖所示:
由圖中可知常量池最後一個常量爲」java/lang/Object」 (Constant pool 爲常量池),在class文件中對應的位置爲0x0069~0X0078。因此訪問標誌的位置爲0x0079~0x007a,值爲:0x0021;同理類索引的值爲:0x0002;父類索引值爲:0x0003;接口索引集合長度爲:0x0000(該類沒有實現接口)。
類索引爲:0x0002,換算成10進制是2,找常量池中爲#02(#02 表示常量池中的第二項常量)的值,爲 #11,再找#11,爲Test1(此處爲類的全類名。因爲TestJ1類沒有包,因此是類名。格式如java/lang/Object)。同理父類爲:0x0003 --> #3 --> #12 --> java/lang/Object。因此TestJ1繼承Object類。
接下來咱們寫一個最簡單的接口以下:
public interface InterSuper1 {
}
class文件和常量池以下:
由上圖能夠看出在class文件中InterSuper1接口的父類標識符指向的也是Object類。不止如此,若是一個接口有父接口。那麼此接口的父類標識符指向的也是Object類。能夠說對於class文件而言全部接口的父類都是Object(同理也可證實Object類也是全部抽象類的父類)。
如今咱們再回過頭看一看上面遺留的問題:若是接口繼承了Object類。那麼當一個類實現多個接口的時候,那不就至關於繼承了多遍Object?又變成了多繼承?首先不會繼承多遍Object,由於在class文件而言,只能存儲一個父類。這個類仍是直接或者間接的繼承Object。也是單繼承,因爲接口不能實例化,因此也不會出現上面的菱形缺陷。
至於網上流傳的Java 的標準——「Java Language Specification」中的9.2節,以下(來源於http://www.cnblogs.com/softnovo/articles/4546418.html):
個人理解是:首先這段話沒有明確說明接口不繼承Object;其次它是出自於java語言規範中,因此它的目的是讓人們更加容易使用Java,因此故意省略了這個細節也是有可能的;再者若是接口繼承Object,上面的觀點也能說得通。
還有一個是以下代碼,爲何不輸出Object中的方法?這個我也沒法解釋。
複製代碼
public interface SuperInter {
public void test();
public String getString();
}
public static void main(String[] args) {
Method[] methods = SuperInter.class.getMethods(); for (Method method : methods) { System.out.println(method.getName());