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

JAVA herman 219浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:codedq,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:codedq,之前的微信号好友位已满,备注:返现
饿了么大量招人,我内推!Java 方向!薪资不设上限,工作年龄不限!工作地点限魔都,可电话面试!简历,发我微信:codedq
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

最近一个粉丝加我说,接到了阿里的面试,问问我阿里会面试哪方面的东西。我说,阿里的套路基本上是,你会什么就问你什么,直到问得你怀疑人生。正所谓,你知道的越多,不知道的就越多,业余的像一颗小草!

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

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

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

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

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 对象。

LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE xttblog Lcom/xttblog/Xttblog; L1 L3 1
Java 中 this 关键字底层原理

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

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 提供的函数接口来验证。比如下面的代码。

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 关键字调用父类的方法和属性。

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

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 方法。

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 指令。

以上内容,若有疑问,欢迎公众号评论区留言指正!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:xttblog(5000人已满),微信号3:codedq(超3800)。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 阿里面试题:Java中this和super关键字的底层实现原理