接口和抽象类是否继承了Object
我们先看一下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的更多相关文章
- c#中接口、抽象类、继承综合小练习
		
namespace Test { class Program { static void Main(string[] args) { //作业:橡皮rubber鸭子.木wood鸭子.真实的鸭子real ...
 - 接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?
		
接口可以继承接口,而且支持多重继承.抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类.
 - 接口是否可继承(extends)接口?抽象类是否可实现  (implements)接口?抽象类是否可继承具体类(concrete  class)?
		
接口可以继承接口,而且支持多重继承.抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类.
 - [Think In Java]基础拾遗1 - 对象初始化、垃圾回收器、继承、组合、代理、接口、抽象类
		
目录 第一章 对象导论第二章 一切都是对象第三章 操作符第四章 控制执行流程第五章 初始化与清理第六章 访问权限控制第七章 复用类第九章 接口 第一章 对象导论 1. 对象的数据位于何处? 有两种方式 ...
 - Python开发基础-Day18继承派生、组合、接口和抽象类
		
类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...
 - python面向对象编程 继承 组合 接口和抽象类
		
1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均用点来访问自己的 ...
 - python开发面向对象基础:接口类&抽象类&多态&钻石继承
		
一,接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实 ...
 - Py修行路  python基础 (十五)面向对象编程 继承 组合 接口和抽象类
		
一.前提回忆: 1.类是用来描述某一类的事物,类的对象就是这一类事物中的一个个体.是事物就要有属性,属性分为 1:数据属性:就是变量 2:函数属性:就是函数,在面向对象里通常称为方法 注意:类和对象均 ...
 - python基础之继承派生、组合、接口和抽象类
		
类的继承与派生 经典类和新式类 在python3中,所有类默认继承object,但凡是继承了object类的子类,以及该子类的子类,都称为新式类(在python3中所有的类都是新式类) 没有继承obj ...
 
随机推荐
- C# WPF MVVM QQ密码管家项目(8,完结篇:自动输入QQ号、密码)
			
原文:C# WPF MVVM QQ密码管家项目(8,完结篇:自动输入QQ号.密码) 目录: 1,界面设计 2,数据模型的建立与数据绑定 3,添加QQ数据 4,修改QQ数据 5,删除QQ数据 6,密码选 ...
 - WPF图形/文字特别效果之一:交叉效果探讨(续)
			
原文:WPF图形/文字特别效果之一:交叉效果探讨(续) 在"WPF图形/文字特别效果之一:交叉效果探讨"(http://blog.csdn.net/johnsuna/archive ...
 - 简述WPF中的画刷(Brush)
			
原文:简述WPF中的画刷(Brush) -------------------------------------------------------------------------------- ...
 - Ubuntu logomaker  sh: 1: pngtopnm: not found 解决方案
			
暂时未找到logomaker的方法,来解决 命令替换,在文件夹: pngtopnm open_show.png > temp.ppm ppmquant 224 temp.ppm >temp ...
 - DLL动态库的创建,隐式加载和显式加载
			
动态库的创建 打开VS,创建如下控制台工程,工程命名为DllTest: 在弹出的对话框中选择"DLL"后单击"完成"按钮: 在工程中新建DllTest.h和Dl ...
 - sklearn、theano、TensorFlow 以及 theras 的理解
			
sklearn ⇒ 机器学习算法和模型: theras theano TensorFlow 1. 理解模型以及函数,参数返回值的实际意义 一定要注意模型的构造函数,接收的参数列表,以及该模型本身所要解 ...
 - Entity相互关系
			
查看 1.图表(.edmx) Model First可以2.代码 内部包含对方(回溯) 1:1 1 2 1:N 3 4 N:M 5 6
 - MVC HttpUtility.HtmlEncode是如何编码的
			
using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;us ...
 - 【C#】WindowsAPICodePack-Shell使用教程
			
原文:[C#]WindowsAPICodePack-Shell使用教程 1.首先在项目中添加WindowsAPICodePack的Nuget包. 点击安装即可. 2.获取<我的电脑>的 ...
 - 关于WPF XAML 中 Trigger的反向ExitActions
			
触发器,顾名思义,就是当满足一定条件时,会触发一些操作,比如:改变控件的透明度,显隐,宽高等等,触发器本身做了一些操作,就是触发器触发条件不符合的时候,会自动把在触发器中更改的属性还原.但,并不是所有 ...