这个'怎么样? Java中的变量实际设置为当前对象吗?

时间:2016-09-16 12:26:38

标签: java oop this-keyword

考虑:

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中的变量。

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

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

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

  3. 因此,无论TestParent是什么,this方法中的this.i始终是TestParent类中的i变量。< / p>

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

    而且,我的主要问题是,

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

5 个答案:

答案 0 :(得分:42)

实质上,

之间没有区别
this.foo()

anyObject.foo()

两者是&#34;已实施&#34;一样的方法。请记住&#34;最后&#34; &#34;面向对象只是一个抽象,而在&#34;现实&#34;会发生什么样的事情:

foo(callingObject)

换句话说:每当你使用某个对象引用来调用方法时...最终都没有在某个对象上调用 。因为在汇编程序和机器代码的深处,类似于&#34;对某事物的调用&#34;不存在。

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

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

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

以后再使用

new Bar().foo();

对于this.fieldA,最后:你有一个对内存中某个位置的引用;和一个表,告诉你哪个&#34;偏移&#34;你会发现fieldA。

编辑 - 仅供记录。如果您对foo(Bar this)的更多细节感兴趣,可以转向question;在Java规范中提供详细信息!

答案 1 :(得分:18)

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

直接解决你在输出中看到的内容:对'this.i'的调用是作为参数传递给'print()'当前范围中字段'i'的值,这是父类。相比之下,打印'this'的调用正在被翻译成一个调用打印'this.getClass()。getName()'[粗略说],并且'getClass()'调用获取实际的类对象,这是为了儿童班。

答案 4 :(得分:0)

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

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

如果您想从子(i)获取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()的儿童版会在TestChild

中打印i的值

输出:

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