作者:walkinger
链接: https:// juejin.im/post/5ca1e8ad e51d454e6a300048
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
前言
学过Java
的人都知道,Object
是所有类的父类。但是你有没有这样的疑问,我并没有extends Object
,它是怎么默认继承Object的呢?
那么今天我们就来看看像Java这种依赖于虚拟机的编程语言是怎样实现默认继承Object的,以及Java编译器
和JVM
到底是如何做的?
继承自Object验证
首先我们来验证一下Object是不是所有类的父类,随便新建一个Java类,如下图:
![19651c9d37b48884656ea9dfafa73ce3.png](https://i-blog.csdnimg.cn/blog_migrate/5678c820b7f2e23b1bd146ac65c49a88.jpeg)
从上面的代码可以看出,new MyClass()打点之后可以选择调用的方法有很多,我们定义的MyClass类里面只有一个main方法,那这些方法哪来的,显然是Object里声明的,故MyClass类的父类就是Object,因此,在MyClass中可以使用Object类的public或protected资源。
另外,当A类继承MyClass类时,通过打点也可以调到Object内的方法,这是继承的传递,好比Object是MyClass的“父亲”,MyClass是A类的“父亲”,Object是A类的“爷爷”,间接的继承了Object。
因此,Object是超类,是所有类的父类。
推测可能的原因
要了解Java类是如何默认继承Object的?
的原因其实并不需要知道JVM的实现细节。只需了解一下对于这种虚拟机程序的基本原理即可。一般对于这种靠虚拟机运行的语言(如Java、C#等)会有两种方法处理默认继承问题。
编译器处理
在编译源代码时,当一个类没有显式标明继承的父类时,编译器会为其指定一个默认的父类(一般为Object),而交给虚拟机处理这个类时,由于这个类已经有一个默认的父类了,因此,VM仍然会按照常规的方法像处理其他类一样来处理这个类。对于这种情况,从编译后的二进制角度来看,所有的类都会有一个父类(后面可以以此依据来验证)。
JVM处理
编译器仍然按照实际代码进行编译,并不会做额外的处理,即如果一个类没有显式地继承于其他类时,编译后的代码仍然没有父类。然后由虚拟机运行二进制代码时,当遇到没有父类的类时,就会自动将这个类看成是Object类的子类(一般这类语言的默认父类都是Object)。
验证结论
从上面两种情况可以看出,第1种情况是在编译器上做的文章,也就是说,当没有父类时,由编译器在编译时自动为其指定一个父类。第2种情况是在虚拟机上做文章,也就是这个默认的父类是由虚拟机来添加的。
那么Java是属于哪一种情况呢?其实这个答案很好得出。只需要随便找一个反编译工具,将.class文件进行反编译即可得知编译器是如何编译的。
就以上面代码为例,如果是第1种情况,就算MyClass
没有父类,但由于编译器已经为MyClass
自动添加了一个 Object
父类,所以,在反编译后得到的源代码中的MyClass
类将会继承Object
类的。如果不是这种情况,那么就是第2种情况。
那么实际情况是什么样的呢?现在我们就将MyClass.class反编译看看到底如何。
jd-gui反编:
![767fb0a315cc4af7ab8ae4e6072d4b0e.png](https://i-blog.csdnimg.cn/blog_migrate/724c99d25dd62c13eec178a3ae142485.png)
使用JDK自带的工具(javap)反编译
CMD命令行下执行:javap MyClass>MyClass.txt
![91b2dc491139358fbea00c4303711ce9.png](https://i-blog.csdnimg.cn/blog_migrate/4a46f60647062d5c358f63adf651e739.png)
可以看出实际的反编译后的文件中并没有extends Object
,使用排除法,因此是第2情况。
这样来推导出的结论是第2种情况,但事实真的如此吗?为什么网上还有说反编译后的是有extends Object
字样?
JDK版本问题?
猜想是JDK版本的问题,于是把JDK版本切换到7,使用jd-gui和javap反编译,接果和使用JDK8反编译后的结果一样,也都没有extends Object
。
继续换版本,昨晚在宿舍准备到Oracle官网下载JDK 6,但是死活下不来,今早到公司后第一件事就是下载,很顺利,安装后把JDK版本切换到JDK 6。
仍然在CMD窗口执行javap MyClass>MyClass.txt
,得到的TXT文件内容如下:
![221546899bf456a192c188b7b09b8909.png](https://i-blog.csdnimg.cn/blog_migrate/c33a40d654b49537ce8f030a045a75db.png)
what?竟然有extends Object
,jd-gui反编译后的依然没有。 即,JDK 6之前使用javap反编译后的MyClass类显式的继承Object,JDK 7以后没有;jd-gui反编译后的不管JDK版本如何始终没有。我们以java自带的工具为准。
总结
那么就是说JDK 6之前是编译器
处理,JDK 7之后是虚拟机
处理。
但是仔细想想我们在编辑器
里(IDE)打点时就能列出Object类下的方法,此时还没轮到编译器和jvm,编辑器就已经知道MyClass类的父类是Object类了,这是因为编辑器为我们做了一些智能处理。(记得区分编辑器和编译器)
(之前发过这篇文章现在怎么又发一遍呢?因为我没有在开头写文章的来源^~^被删除了,其实这篇文章是我首发在掘金的。知乎现在对原创的保护力度很大~)
----------------------------续----------------------------
2019年8月9日10:17:54
既然评论说我论证的不够严谨,那今天来更新一下。
这里是一个简单的测试类:
![db821070df64bee29df756b46b2b013c.png](https://i-blog.csdnimg.cn/blog_migrate/0c03fae15d38ac33b5d714ea9e251b6d.png)
我们直接看字节码文件,切换不同版本,javac编译,javap -c查看字节码文件
![5c6509fb60eb40945212f3b89cfafe26.png](https://i-blog.csdnimg.cn/blog_migrate/fb434a5f08e38ff1dc14ce48d14b1dc5.jpeg)
![c977eae3d5011761efb32faaa2f8850e.png](https://i-blog.csdnimg.cn/blog_migrate/38f0f5a257d67187daba7213fd298033.png)
直接看字节码文件,因为有评论说反编译可能会优化阅读,看的不准特别是第三方反编译工具,这位网友说的很好,感谢。
看到这里,我们的结论只能说,JDK6及之前是编译器处理的,JDK6之后不是编译器处理的。如果问,JDK6之后是JVM处理的吗?我只能说,除了编译器和JVM,还有谁动了这个文件?如果没了,那可以说明是JVM处理的,但论据不够严谨,因为你并没有明确的看到在JVM的源码里,具体在哪个地方处理的!如果追求严谨性(较真)的话,那没办法,作者能力有限,还没能力和精力去JVM源码里找到具体是在哪里处理的。如果有大佬知道或找到了,欢迎评论分享哦。同时感谢那位网友的提醒,谢谢!