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

最近一个粉丝加我说,接到了阿里的面试,问问我阿里会面试哪方面的东西。我说,阿里的套路基本上是,你会什么就问你什么,直到问得你怀疑人生。

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

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

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

OCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE xttblog Lcom/xttblog/Xttblog; L1 L3 1

  阿里面试题:Java中this和super关键字的底层实现原理_第1张图片

 

 第二种就更简单了,我们给 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 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 ()V
   L0
    LINENUMBER 9 L0
    ALOAD 0
    INVOKESPECIAL com/xttblog/Xttblog. ()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 指令

 

我是一名程序员,java开发工程师,在java中呢,有很多java概念的知识点,我都有总结和案例,喜欢java,想学习java的朋友可以来和我交流学习,不管你是零基础还是想转行,还是学生,都可以。在这里为了大家在一起讨论交流,我建立一个我的java编程技术扣扣交流峮,有很多志同道合的伙伴在里面,有需要的可以加:前108中062后1881 。希望你是带着真诚的学习的心来的。

 

你可能感兴趣的:(阿里面试题:Java中this和super关键字的底层实现原理)