我们先看一下Java的帮助文档对于Object的描述:

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.

Object 类是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

  注意:描述是Every class(所有的类)。有这句话可以猜想一下,抽象类是继承了Object。

  对于继承,我们知道C++语言支持多继承,Java语言只支持单继承。那么Java语言为什么不支持多继承呢?我们先看一看多继承中最典型的钻石问题(菱型缺陷),如下图(图片来源于https://www.cnblogs.com/sddai/p/6516668.html):

  其中A、B、C、D是四个类,B继承A,C也继承A,D又同时继承了B和C。如果B和C都有test方法,看如下代码

D d = new D();
d.test();

  第一句中当new D(); 的时候会不会调用两次A的构造函数?

  第二句中调用的是B里面的test方法还是C里面的test方法?

  为了避免以上的问题,Java采用了折衷的方法,只允许单继承,但可以实现多个接口。所以我们可以以java语言是单继承这个前提,来推导一下接口和抽象类是否继承Object。如下:

  对于抽象类而言:一个普通类肯定是继承了Object,如果一个抽象类再继承这个普通类,这个时候抽象类肯定也是继承了Object的。而对于没有继承任何类的抽象类而言,如果它没有继承Object,那么当一个普通类继承这个抽象类的时候,这个普通类也肯定没有继承Object,悖论。所以抽象类肯定是继承了Object。

  对于接口而言呢:如果接口继承了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());
}
}

参考资料:

  《深入理解Java虚拟机》第二版

  https://www.cnblogs.com/sddai/p/6516668.html

  http://www.cnblogs.com/softnovo/articles/4546418.html

  https://blog.csdn.net/xidiancoder/article/details/78011148

  https://blog.csdn.net/tengfeixiaoao/article/details/79586949

接口和抽象类是否继承了Object的更多相关文章

  1. c#中接口、抽象类、继承综合小练习

    namespace Test { class Program { static void Main(string[] args) { //作业:橡皮rubber鸭子.木wood鸭子.真实的鸭子real ...

  2. 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?

    接口可以继承接口,而且支持多重继承.抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类.

  3. 接口是否可继承(extends)接口?抽象类是否可实现 (implements)接口?抽象类是否可继承具体类(concrete class)?

    接口可以继承接口,而且支持多重继承.抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类.

  4. [Think In Java]基础拾遗1 - 对象初始化、垃圾回收器、继承、组合、代理、接口、抽象类

    目录 第一章 对象导论第二章 一切都是对象第三章 操作符第四章 控制执行流程第五章 初始化与清理第六章 访问权限控制第七章 复用类第九章 接口 第一章 对象导论 1. 对象的数据位于何处? 有两种方式 ...

  5. Python开发基础-Day18继承派生、组合、接口和抽象类

    类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...

  6. python面向对象编程 继承 组合 接口和抽象类

    1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均用点来访问自己的 ...

  7. python开发面向对象基础:接口类&抽象类&多态&钻石继承

    一,接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实 ...

  8. Py修行路 python基础 (十五)面向对象编程 继承 组合 接口和抽象类

    一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...

  9. python基础之继承派生、组合、接口和抽象类

    类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...

随机推荐

  1. C# WPF MVVM QQ密码管家项目(8,完结篇:自动输入QQ号、密码)

    原文:C# WPF MVVM QQ密码管家项目(8,完结篇:自动输入QQ号.密码) 目录: 1,界面设计 2,数据模型的建立与数据绑定 3,添加QQ数据 4,修改QQ数据 5,删除QQ数据 6,密码选 ...

  2. WPF图形/文字特别效果之一:交叉效果探讨(续)

    原文:WPF图形/文字特别效果之一:交叉效果探讨(续) 在"WPF图形/文字特别效果之一:交叉效果探讨"(http://blog.csdn.net/johnsuna/archive ...

  3. 简述WPF中的画刷(Brush)

    原文:简述WPF中的画刷(Brush) -------------------------------------------------------------------------------- ...

  4. Ubuntu logomaker sh: 1: pngtopnm: not found 解决方案

    暂时未找到logomaker的方法,来解决 命令替换,在文件夹: pngtopnm open_show.png > temp.ppm ppmquant 224 temp.ppm >temp ...

  5. DLL动态库的创建,隐式加载和显式加载

    动态库的创建 打开VS,创建如下控制台工程,工程命名为DllTest: 在弹出的对话框中选择"DLL"后单击"完成"按钮: 在工程中新建DllTest.h和Dl ...

  6. sklearn、theano、TensorFlow 以及 theras 的理解

    sklearn ⇒ 机器学习算法和模型: theras theano TensorFlow 1. 理解模型以及函数,参数返回值的实际意义 一定要注意模型的构造函数,接收的参数列表,以及该模型本身所要解 ...

  7. Entity相互关系

    查看 1.图表(.edmx) Model First可以2.代码 内部包含对方(回溯) 1:1   1   2 1:N   3   4 N:M   5   6

  8. MVC HttpUtility.HtmlEncode是如何编码的

    using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;us ...

  9. 【C#】WindowsAPICodePack-Shell使用教程

    原文:[C#]WindowsAPICodePack-Shell使用教程 1.首先在项目中添加WindowsAPICodePack的Nuget包.   点击安装即可. 2.获取<我的电脑>的 ...

  10. 关于WPF XAML 中 Trigger的反向ExitActions

    触发器,顾名思义,就是当满足一定条件时,会触发一些操作,比如:改变控件的透明度,显隐,宽高等等,触发器本身做了一些操作,就是触发器触发条件不符合的时候,会自动把在触发器中更改的属性还原.但,并不是所有 ...