"this"关键字:Java中的工作机制

pul*_*ion 14 java this

在学习Java一段时间之后,它第一次使用this关键字让我如此困惑.

这是我如何感到困惑.我写了以下代码:

class BasicInheritanceTest3Base{
    private int x = 0;
    public int y;

    public void a() {
        x++;
        this.x++;
        System.out.println("BasicInheritanceTest3Base.a()");
        b();
        this.b();
        System.out.println(x);
        System.out.println(y);
    }

    public void b(){
        System.out.println("BasicInheritanceTest3Base.b()");
    }
}

public class BasicInheritanceTest3 extends BasicInheritanceTest3Base {
    private int x = 3;
    public int y = 2;

    public void b() {
        System.out.println("BasicInheritanceTest3.b()");
    }

    public static void main(String[] args){
        BasicInheritanceTest3 bit2 = new BasicInheritanceTest3();
        bit2.a();
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

BasicInheritanceTest3Base.a()
BasicInheritanceTest3.b()
BasicInheritanceTest3.b()
2
0
Run Code Online (Sandbox Code Playgroud)

现在第一个问题是:为什么xthis.x指向x基类而不是Child类?如果this.x指向x基类,为什么要this.b()调用b()子类?字段和方法的行为是否不同?

但是,主要关注的是this关键字的机制.我的意思是你知道,this指向(引用)当前对象.如果你想一想,它不是一个神奇的行为.在this某个地方必须有场地.例如,.class类的文字是不可见的,但存在于发出的字节码中.同样,此引用应存在于字节码中.

好吧,假设上面是真的,this应该是public final(一个空白的最终),每次构造对象并实例化其字段时,它都会被实例化.这意味着它是一个实例变量而不是静态变量.

现在,如果将其实例化为当前对象的引用(仅限于特定对象),this对于字段和方法,上面的使用方法有何不同?总而言之,背后的机制是this什么?该机制是否也适用于super关键字?

编辑:每个人都在阅读问题,然后是评论,我想问一下,this编译器声明的字段在哪里以及它的限定符是什么.结果行为是如何在幕后发生的?

Sot*_*lis 5

其他答案和评论解释了字段如何不是多态的,以及如何根据实例引用的编译时类型解析字段访问表达式。下面,我将解释字节码如何处理this引用。

在关于接收参数的章节中,Java 虚拟机规范指出

如果将 n 个参数传递给实例方法,则按照约定,它们将在为新方法调用创建的框架的编号为 1 到 n 的局部变量中接收。参数按传递的顺序接收。例如:

int addTwo(int i, int j) {
    return i + j;
}
Run Code Online (Sandbox Code Playgroud)

编译为:

Method int addTwo(int,int)
0   iload_1        // Push value of local variable 1 (i)
1   iload_2        // Push value of local variable 2 (j)
2   iadd           // Add; leave int result on operand stack
3   ireturn        // Return int result
Run Code Online (Sandbox Code Playgroud)

按照惯例,实例方法在局部变量 0 中传递对其实例的引用。在 Java 编程语言中,该实例可通过this关键字访问。

类(静态)方法没有实例,因此对于它们来说,不需要使用局部变量 0。类方法在索引 0 处开始使用局部变量。如果 addTwo 方法是类方法,则其参数将以与第一个版本类似的方式传递:

static int addTwoStatic(int i, int j) {
    return i + j;
}
Run Code Online (Sandbox Code Playgroud)

编译为:

Method int addTwoStatic(int,int)
0   iload_0
1   iload_1
2   iadd
3   ireturn
Run Code Online (Sandbox Code Playgroud)

唯一的区别是方法参数出现在局部变量 0 而不是 1 中。

换句话说,您可以将其this视为未在任何地方声明,也可以视为被声明为每个实例方法的第一个参数。为每个实例方法创建一个局部变量表条目,并在每次调用时填充。

调用方法一章指出

实例方法的正常方法调用在对象的运行时类型上分派。(它们是虚拟的,在 C++ 术语中。)这样的调用是使用invokevirtual指令实现的,该指令将运行时常量池条目的索引作为其参数,给出对象的类类型的二进制名称的内部形式,要调用的方法的名称,以及该方法的描述符(第 4.3.3 节)。要调用addTwo之前定义为实例方法的方法,我们可以编写:

int add12and13() {
    return addTwo(12, 13);
}
Run Code Online (Sandbox Code Playgroud)

这编译为:

Method int add12and13()
0   aload_0             // Push local variable 0 (this)
1   bipush 12           // Push int constant 12
3   bipush 13           // Push int constant 13
5   invokevirtual #4    // Method Example.addtwo(II)I
8   ireturn             // Return int on top of operand stack;
                        // it is the int result of addTwo()
Run Code Online (Sandbox Code Playgroud)

通过首先将当前实例 的引用推this送到操作数堆栈来设置调用。int然后推送方法调用的参数值 12 和 13。addTwo创建方法的框架时,传递给方法的参数成为新框架局部变量的初始值。也就是说,this被调用者压入操作数栈的for和两个参数的引用将成为被调用方法的局部变量 0、1 和 2 的初始值。