30

考虑:

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. 由于它是new TestChild()调用该printName()方法的对象,因此根据调试器this,第 6 行中的变量被设置为一个对象 - {TestChild@428}。TestChild

  2. 但是,由于 Java 没有虚拟字段——我不完全确定这意味着什么,但我从概念上理解它与支持多态的 Java 方法相反——在编译时this.i设置为 100 。TestParent

  3. 所以不管是什么this,方法this.i中的变量TestParent总是类中的。iTestParent

我不确定我的理解是否正确,所以如果我错了,请纠正我。

而且,我的主要问题是,

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

4

5 回答 5

43

本质上没有区别

this.foo()

anyObject.foo()

因为两者都以相同的方式“实施”。请记住,“最终”“面向对象只是一种抽象,而在“现实”中发生的事情是这样的:

foo(callingObject)

换句话说:每当您使用某个对象引用来调用方法时……最终都不会调用某个对象。因为在汇编程序和机器代码的深处,不存在诸如“调用某事”之类的东西。

真正发生的是对函数的调用;第一个(源代码级别的隐式/不可见)参数是该对象。

顺便说一句:你实际上可以用 Java 写下来,比如:

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

和以后使用

new Bar().foo();

而对于this.fieldA,最后:你有一个内存中某个位置的引用;和一个表格,告诉您您将在哪个“偏移量”上找到 fieldA。

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

于 2016-09-16T12:34:23.970 回答
18

这里发生的是两个完全不同的字段,都称为i; 使用他们的全名,一个是TestParent::i,一个是TestChild::i

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

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

于 2016-09-16T12:46:23.247 回答
1

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

于 2016-09-16T12:35:03.450 回答
0

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

于 2016-09-16T14:25:08.003 回答
0

在@Tom Anderson 答案之上添加更多信息,很好地解释了隐藏概念。

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

如果您想i从 child ( TestChild) 中获取值,请覆盖 中的方法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:如果我评论super.printName()来自孩子的电话,孩子版本的 TestChild.printName()打印 i 的值TestChild

输出:

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

案例 2:TestChild.printName() 调用 super.printName() 作为 printName() 方法的第一行。在这种情况下,来自父母和孩子的 i 值显示在各自的方法中。

输出:

TestChild.i and TestParent.i:200:100
TestParent:printName()
TestChild@43cda81e
100
TestChild:printName()
TestChild@43cda81e
200
于 2016-10-15T13:32:52.760 回答