咱们先看一下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());