Java 中的 'this' 变量实际上是如何设置为当前对象的?

How is the 'this' variable in Java actually set to the current object?

考虑:

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println(this); //{TestChild@428} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
}

public class ThisTest {
  public static void main(String[] args) {
    new TestChild().printName();
  }
}

我知道有人问过类似的问题,但我无法深入了解Java中的'this'变量。

让我试着解释一下我是如何理解上图的结果的。

  1. 因为调用 printName() 方法的是 new TestChild() 对象,所以第 6 行中的 this 变量设置为 TestChild 对象 - {TestChild@428} 根据调试器。

  2. 但是,由于 Java 没有虚拟字段 - 我不完全确定这是什么意思,但我在概念上将其理解为 [=52= 的对立面] 方法,支持多态性 - this.i 在编译时设置为 TestParent 的 100。

  3. 所以无论 this 是什么,TestParent 方法中的 this.i 始终是 [=18= 方法中的 i 变量] class.

我不确定我的理解是否正确,如有错误请指正。

另外,我的主要问题是,

如何将 this 变量设置为调用该方法的当前对象?它是如何实际实施的?

本质上和

没有区别
this.foo()

anyObject.foo()

因为两者都是"implemented"相同的方式。请记住 "in the end" "object orientation is only an abstraction, and in " 现实”发生的事情是这样的:

foo(callingObject)

换句话说:无论何时您使用某个对象引用来调用方法...最终都不会调用 某个对象。因为在汇编程序和机器代码的深处, "a call on something" 之类的东西并不存在。

真正发生的是对函数的调用;第一个(源代码级别的implicit/invisible)参数是那个对象。

顺便说一句:您实际上可以将其写在 Java 中,例如:

class Bar {
   void foo(Bar this) { ... }

以后使用

new Bar().foo();

而对于 this.fieldA,最后:您引用了内存中的某个位置;和一个 table 告诉你在哪个 "offset" 上你会找到 fieldA。

编辑 - 仅作记录。如果你对 foo(Bar this) 的更多细节感兴趣 - 你可以转向这个 ;在其背后的 Java 规范中提供详细信息!

好吧,当创建一个新对象时,该对象在内存中有一个地址,因此您可以认为该对象有一个私有成员 this,该成员设置为创建对象时的地址。你也可以这样想:obj.method(param)只是method(obj, param);的语法糖,this其实是method的一个参数。

这里发生的事情是有两个完全不同的字段都叫 i;要使用他们的全名,一个是 TestParent::i,一个是 TestChild::i.

因为方法printName定义在TestParent中,当它引用i时,只能看到TestParent::i,设置为100。

而当您在 TestChild 中将 i 设置为 200 时,名为 i 的两个字段都可见,但由于它们具有相同的名称,TestChild::i hides TestParent::i,你最终设置 TestChild::i 并保持 TestParent::i 不变。

要直接解决您在输出中看到的内容:对 print 'this.i' 的调用将作为参数传递给 'print()' 当前范围中字段 'i' 的值,这是parentclass的范围。相比之下,对 print 'this' 的调用在后台被转换为对 print 'this.getClass().getName()' [粗略地说] 的调用,而 'getClass()' 调用得到实际的 class object,这是 child class.

在@Tom Anderson 回答的基础上添加更多信息,这很好地解释了隐藏概念。

我在 Child ( TestChild) 中添加了一个构造函数,它在 parent 和 child 中打印 i 的值。

如果您想从 child (TestChild) 获取 i 的值,请覆盖 TestChild 中的方法。

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println("TestParent:printName()");
    System.err.println(this); //{TestChild@SOME_NUM} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
  public TestChild(){
    System.out.println("TestChild.i and TestParent.i:"+this.i+":"+super.i);
  }
  public void printName(){
      //super.printName();
      System.err.println("TestChild:printName()");
      System.err.println(this); //{TestChild@SOME_NUM} according to the Debugger.
      System.err.println(this.i); //this.i is 200.
  }
}

public class ThisTest {
  public static void main(String[] args) {
    TestParent parent = new TestChild();
    parent.printName();
  }
}

案例 1:如果我评论来自 child 的 super.printName() 调用,TestChild.printName() 的 Child 版本打印 TestChild[=20 中 i 的值=]

输出:

TestChild.i and TestParent.i:200:100
TestChild:printName()
TestChild@43cda81e
200

情况 2:TestChild.printName() 调用 super.printName() 作为 printName() 方法的第一行。在这种情况下,parent 和 child 中的 i 值都显示在各自的方法中。

输出:

TestChild.i and TestParent.i:200:100
TestParent:printName()
TestChild@43cda81e
100
TestChild:printName()
TestChild@43cda81e
200