java.lang.Object是如何成为默认父类的-阿里云开发者社区

开发者社区> geekori> 正文

java.lang.Object是如何成为默认父类的

简介: 本文为原创,如需转载,请注明作者和出处,谢谢! 经常有Java初学者会问为什么一个没有父类的Java类会自动从java.lang.Object类继承。
+关注继续查看
本文为原创,如需转载,请注明作者和出处,谢谢!

经常有
Java初学者会问为什么一个没有父类的Java类会自动从java.lang.Object类继承。如下面是一个普通的Java类:

public class Test    // 从Object类继承
{
    
public static void main(String[] args)
    {
        System.out.println(
new Test().toString());
    }
}

从上面的代码可以看出,实际上,Test类的父类就是Object,因此,在Test中可以使用Object类的publicprotected资源,如toString方法。那么Java编译器和JVM到底是如何做的呢?

了解这个原因其实并不需要知道JVM的实现细节。只要思考一下对于这种虚拟机程序的原理即可。一般对于这种靠虚拟机运行的语言(如JavaC#等)会有两种方法处理默认继承问题。

1. 在编译源代码时,当遇到没有父类的类时,编译器会将其指定一个默认的父类(一般为Object),而虚拟机在处理到这个类时,由于这个类已经有一个默认的父类了,因此,VM仍然会按着常规的方法来处理每一个类。对于这种情况,从编译后的二进制角度来看,所有的类都会有一个父类。

2. 编译器仍然按着实际代码进行编译,并不会做额外的处理。如果一个类没有显式地继承于其他的类,编译后的代码仍然没有父类。然后由虚拟机运行二进制代码时,当遇到没有父类的类时,就会自动将这个类看成是Object类的子类(一般这类语言的默认父类都是Object)。

从上面两种情况可以看出,第1种情况是在编译器上做的文章,也就是说,当没有父类时,由编译器在编译时自动为其指定一个父类。第2种情况是在虚拟机上做文章,也就是这个默认的父类是由虚拟机来添加的。那么Java是属性哪一种情况呢?其实这个答案很好得出。只需要随便找一个反编译工具,并.class文件进行反编译即可得知编译器是如何编译的。就以上面代码为例,如果是第1种情况,就算Test没有父类,但由于编译器已经为Test自动添加了一个Object父类,因此,在反编译后得到的源代码中的Test类是从Object类继承的。如果没是这种情况,那么就是第2种情况。

现在我们使用JDK带的反编译工具javap来反编译Test.class,先执行下面的命令:

javap Test > Test.txt

打开Test.txt文件后,会看到如下的代码:

public class Test extends java.lang.Object{
    
public Test();
    
public static void main(java.lang.String[]);
}

再使用下面的命令来得到bytecode代码:

javap -c Test >Test1.txt

打开Test1.txt后,会看到如下的代码:

public class Test extends java.lang.Object{
public Test();
  Code:
   
0:    aload_0
   
1:    invokespecial    #8//Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   
0:    getstatic    #16//Field java/lang/System.out:Ljava/io/PrintStream;
   3:    new    #1//class Test
   6:    dup
   
7:    invokespecial    #22//Method "<init>":()V
   10:    invokevirtual    #23//Method java/lang/Object.toString:()Ljava/lang/String;
   13:    invokevirtual    #27//Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:    return
}

    从上面两段代码可以看出,Test已经从Object继承了,因此,可以断定Java是属性第1种情况,也就是说由编译器为没有父类的类指定了Object作为其默认父类。如果读者还不确定,可以直接打开Test.class,看看里面有没有Object,图1Test.class的十六进制代码:



                                                图1

   大家可以看到,Java编译器已经为Test指定了一个默认的Object类作为其父类。目前大多数基于虚拟器的语言都是采用的第1种方法来处理默认父类的,如下面的C#代码:

using System;

namespace ConsoleApplication1
{
    class Test
    {
       
static void Main(string[] args)
        {
            Console.WriteLine(
new Test().ToString());
        }
    }
}

 使用ildasm.exe将上面的代码反编译后,得到的MSIL代码如下:

.class private auto ansi beforefieldinit ConsoleApplication1.Test
       extends [mscorlib]System.Object
{
// end of class ConsoleApplication1.Test

    从上面的代码可以清楚地看到,Test类已经有一个System.Object作为父类了。




国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Elasticsearch 默认配置 IK 及 Java AnalyzeRequestBuilder 使用
一、什么是 Elasticsearch-analysis-ik 二、默认配置 IK 三、使用 AnalyzeRequestBuilder 获取分词结果 四、小结
4595 0
JavaScript停止冒泡和阻止浏览器默认行为
原文链接:http://caibaojian.com/javascript-stoppropagation-preventdefault.html 事件兼容 function myfn(e){ var evt = e ? e:window.
524 0
java8新特性 (λ、stream 与 默认接口方法)
1.lambda λ表达式本质上是一个匿名方法,用来方便地实现函数接口。也就是说,λ表达式主要用于替换以前广泛使用的内部匿名类。  让我们来看下面这个例子:     public int add(int x, int y) {         return x + y;     } 转成λ表达式后是这个样子:     (int x, int y) -&gt; x + y;
1378 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4423 0
+关注
geekori
欧瑞科技创始人&amp;CEO,东北大学计算机专业硕士,拥有超过20年软件开分经验。欧瑞学院金牌讲师、51CTO学院金牌讲师、CSDN学院特约讲师、畅销书作者,企业内训讲师。曾出版过超过30本IT畅销书,涉猎移动开发、跨平台开发、机器学习、区块链、大数据、编译器等领域。
419
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载