接口和抽象类是否继承了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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439745.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.2)深度学习笔记:机器学习策略(2)

目录 1)Carrying out error analysis 2)Cleaning up Incorrectly labeled data 3)Build your first system quickly then iterate 4)Training and testing on different distributios 5)Bias and Variance with m…

4.1)深度卷积网络:卷积神经网络基础

目录 1)Computer vision 2)Edge detection example(理解) 3)More edge detection 4)Padding(重点) 5)Strided convolutions(重点) 6&#x…

4.2)深度卷积网络:实例研究

目录 1)Why look at case studies? 2)Classic networks(理解) 3)ResNets(理解) 4)Why ResNets work?(经典) 5)Networks in Networks and 1…

10种常见的软件架构模式

有没有想过要设计多大的企业规模系统?在主要的软件开发开始之前,我们必须选择一个合适的体系结构,它将为我们提供所需的功能和质量属性。因此,在将它们应用到我们的设计之前,我们应该了解不同的体系结构。 什么是架构模…

4.3)深度卷积网络:目标检测

目录 1)Object localization(重点) 2)Landmark detection 3)Object detection 4)Convolutional implementation of sliding windows 5)Bounding box prediction(重点&#xff0…

4.4)深度卷积网络:人脸识别和神经风格转换

目录 1)What is face recognition? 2)One-shot learning 3)Siamese network 4)Triplet Loss(重点) 5)Face Verification and Binary Classification 6)What is neural style …

一步步编写操作系统 35 内存为何要分页

一直以来我们都直接在内存分段机制下工作,目前未出问题看似良好,的确目前咱们的应用过于简单了,就一个loader在跑,能出什么问题呢。可是想像一下,当我们物理内存不足时会怎么办呢?比如系统里的应用程序过多…

《python深度学习》代码中文注释

《python深度学习》由Keras之父、现任Google人工智能研究员的弗朗索瓦•肖莱(François Chollet)执笔,详尽介绍了用Python和Keras进行深度学习的探索实践,包括计算机视觉、自然语言处理、生成式模型等应用。书中包含30多个代码示…

【BZOJ - 4754】独特的树叶(树哈希)

题干: JYY有两棵树A和B:树A有N个点,编号为1到N;树B有N1个点,编号为1到N1。JYY知道树B恰好是由树A加上一个叶 节点,然后将节点的编号打乱后得到的。他想知道,这个多余的叶子到底是树B中的哪一个…

一步步编写操作系统 36 一级页表与虚拟地址1

为了给大家说清楚分页机制,我们先在宏观上说下cpu地址变换过程,先让大家有个直观的印象,如果有不明白的地方也不要着急,适时地不求甚解,有助于从全局上将知识融会贯通(这句话是我即兴说的,说得多…

动手学无人驾驶(4):基于激光雷达点云数据3D目标检测

上一篇文章《动手学无人驾驶(3):基于激光雷达3D多目标追踪》介绍了3D多目标追踪,多目标追踪里使用的传感器数据为激光雷达Lidar检测到的数据,本文就介绍如何基于激光雷达点云数据进行3D目标检测。 论文地址&#xff1a…

一步步编写操作系统 37 一级页表与虚拟地址2

接上节,分页机制是建立在分段机制之上,与其脱离不了干系,即使在分页机制下的进程也要先经过逻辑上的分段才行,每加载一个进程,操作系统按照进程中各段的起始范围,在进程自己的4GB虚拟地址空间中寻找可有空间…

PointNet:3D点集分类与分割深度学习模型

之前的一篇博客《动手学无人驾驶(4):基于激光雷达点云数据3D目标检测》里介绍到了如何基于PointRCNN模型来进行3D目标检测,作者使用的主干网是PointNet,而PointNet又是基于PointNet来实现的。今天写的这篇博客就是对Po…

计算机视觉那些事儿(1):基本任务

本文主要介绍深度学习在计算机视觉领域(Computer vision)基本任务中的应用,包括分类、检测、分割(语义与实体)。 目录 引言 分类(Classification) 目标检测(Object Detection) T…

一步步编写操作系统 38 一级页表与虚拟地址3

接上,页是地址空间的计量单位,并不是专属物理地址或线性地址,只要是4KB的地址空间都可以称为一页,所以线性地址的一页也要对应物理地址的一页。一页大小为4KB,这样一来,4GB地址空间被划分成4GB/4KB1M个页&a…

《Python编程:从入门到实践》速查表

本文是Python畅销书《Python:从入门到实践》速查表。 随书配套视频观看地址:https://www.bilibili.com/video/av35698354 目录 1.Overview 2.Lists 3.Dictionaries 4.If and While Loops 5.Functions 6.Classes 7.Files and Exceptions 8.Testin…

一步步编写操作系统 39 二级页表1

前面讲述了页表的原理,并以一级页表做为原型讲述了地址转换过程。既然有了一级页表,为什么还要搞个二级页表呢?理由如下: 一级页表中最多可容纳1M(1048576)个页表项,每个页表项是4字节&#xf…

PointNet++详解与代码

在之前的一篇文章《PointNet:3D点集分类与分割深度学习模型》中分析了PointNet网络是如何进行3D点云数据分类与分割的。但是PointNet存在的一个缺点是无法获得局部特征,这使得它很难对复杂场景进行分析。在PointNet中,作者通过两个主要的方法…

一步步编写操作系统 40 内存分页下用户程序与操作系统的关系

分页的第一步要准备好一个页表,我们的页表是什么样子呢?现在我们要设计一个页表啦。 设计页表其实就是设计内存布局,不过在规划内存布局之前,我们需要了解用户进程与操作系统之间的关系。 前面讲保护模式时,我们知道…

一步步编写操作系统 41 快表tlb 简介

分页机制虽然很灵活,但您也看到了,为了实现虚拟地址到物理地址的映射,过程还是有些麻烦的。先要从CR3寄存器中获取页目录表物理地址,然后用虚拟地址的高10位乘以4的积做为在页目录表中的偏移量去寻址目录项pde,从pde中…