前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试题:Java中this和super关键字的底层实现原理

面试题:Java中this和super关键字的底层实现原理

作者头像
业余草
发布2020-06-16 10:47:09
1K0
发布2020-06-16 10:47:09
举报
文章被收录于专栏:业余草业余草

编辑:业余草

来源:https://www.xttblog.com/?p=5028

今天,我们就一起来讨论一个这个网友遇到的面试题,Java 中 this 和 super 关键字的底层实现原理!

众所周知,在 Java 中,this 具有以下含义。

  • 在 Java 中,每当一个对象创建后,Java 虚拟机都会给这个对象分配一个指向自身的引用,也就是 this。同时如果对象是子类对象,则还会有一个 super 引用指向当前对象的父类对象。
  • 在类的方法定义中使用 this 关键字,表示使用该方法的对象的引用。
  • 在一个类中,this 可以表示该类的当前实例。
  • this 是对当前类对象的引用,对象只有被实例化才存在。

根据 this 的这些含义,面试官就想知道 this 是怎么出来的?先看下面的代码。

代码语言:javascript
复制
public class Xttblog {
    private String name;

    public void setName(String name){
        this.name = name;
    }

    public static void main(String[] args) {
        Xttblog xttblog = new Xttblog();
        xttblog.setName("业余草");
    }
}

在 setName 方法中,可以使用 this 的本质是:编译器在调用某个实例方法时,实际上会把当前的实例对象的引用作为第一个参数传递给方法。

经过编译器的处理,xttblog.setName(“业余草”) 这行代码实际上变成 xttblog.setName(xttblog,”业余草”)。而如果想从方法内部获取当前对象的引用,就有一个专门的关键字 this,表示“调用方法的那个对象”的引用(如 xttblog)。

这一点,我能想到有 3 种方法来验证它。

第一种是我们通过 Bytecode 工具查看字节码的时候,会发现有 setName 方法有两个 LOCALVARIABLE,即两个局部变量,一个是 String 的,一个是 Xttblog 对象。

代码语言:javascript
复制
OCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE xttblog Lcom/xttblog/Xttblog; L1 L3 1

第二种就更简单了,我们给 setName 方法在第一位再加一个 Xttblog this 参数,你会发现编译器不会报错,方法调用都能够成功。

代码语言:javascript
复制
public class Xttblog {
    private String name;

    public void setName(Xttblog this,String name){
        this.name = name;
    }

    public static void main(String[] args) {
        Xttblog xttblog = new Xttblog();
        xttblog.setName("业余草");
    }
}

第三种验证方法,我们借助 Java8 提供的函数接口来验证。比如下面的代码。

代码语言:javascript
复制
public class Xttblog {
    private String name = "业余草";
    private String sayStr;

    public void setName(String name){
        this.name = name;
    }

    private String say(String sayStr){
        this.sayStr = sayStr;
        return this.name + " say : " + this.sayStr;
    }

    public static void main(String[] args) {
        Xttblog xttblog = new Xttblog();
        xttblog.setName("业余草");

        BiFunction<Xttblog,String,String> function = Xttblog::say;
        String str = function.apply(xttblog, "hello~");
        System.out.println(str);
    }
}

以 BiFunction 为例,它要求的是以两个输入参数,一个输出参数。而我们调用的 say 只需要一个字符串类型的输入参数,但是我们的示例代码中,却传递了两个参数。一个是 Xttblog 类型,一个是 String 类型,编译器没有报错,可以正常执行。

这也验证了 this 的底层实现原理。

同理,super 的本质是:当我们 new 一个子类对象的时候,子类除了生成一个 this 引用指向自身,还会生成一个指向其直接父类对象的引用 super。如果子类在继承父类的非私有的法和成员变量时,没有同名的方法和变量,可以直接通过名称使用父类的方法和变量。如果子类存在与父类同名的方法和成员变量时,要想区别访问父类和自身的变量和方法,需要使用 super 关键字调用父类的方法和属性。

这些在字节码中都有体现。以下面的代码为例:

代码语言:javascript
复制
public class Codedq extends Xttblog{
    private String skill;

    public void setSkill(String skill){
        this.skill = skill;
        System.out.println(super.say("hello world"));
        System.out.println(super.getName() + "会:" + this.skill);
    }
}

我们在字节码中会看到,在 Codedq 的构造函数中,调用了父类 init 方法。

代码语言:javascript
复制
public <init>()V
   L0
    LINENUMBER 9 L0
    ALOAD 0
    INVOKESPECIAL com/xttblog/Xttblog.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/xttblog/Codedq; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

注意上面的 INVOKESPECIAL 指令。super 关键字的底层原理就是靠 INVOKESPECIAL 指令。

在 Codedq 的代码中,super 就是靠 INVOKESPECIAL 指令来调用父类方法的。

invokespecial 指令的主要作用是,用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。(Invoke instance method; special handling for superclass, private, and instance initialization method invocations )。

最后,我们来个简单的总结,this 的底层实现是默认传值,super 是靠 INVOKESPECIAL 指令

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-06-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档