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

Kev*_*ark 30 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道有类似的问题已被提出,但我无法对Java中的'this'变量有一个明确的理解.

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

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

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

  3. 所以不管是什么this,this.i在一个TestParent方法中总是会i成为TestParent类中的变量.

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

而且,我的主要问题是,

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

Gho*_*ica 42

从本质上讲,两者之间没有区别

this.foo()
Run Code Online (Sandbox Code Playgroud)

anyObject.foo()
Run Code Online (Sandbox Code Playgroud)

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

foo(callingObject)
Run Code Online (Sandbox Code Playgroud)

换句话说:无论何时使用某个对象引用来调用方法......最后都没有某个对象进行调用.因为在汇编程序和机器代码的深处,不存在诸如"对某事物的调用"之类的东西.

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

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

class Bar {
   void foo(Bar this) { ... }
Run Code Online (Sandbox Code Playgroud)

以后用

new Bar().foo();
Run Code Online (Sandbox Code Playgroud)

对于this.fieldA,最后:你有一个对内存中某个位置的引用; 和一个表,告诉你哪个"偏移"你会找到fieldA.

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

  • 究竟.有些语言使这个机制更加明确(比如Python,你*在*中指定方法中的`self`参数) (9认同)
  • 什么!我不知道你可以像这样写一个`this`参数(虽然我知道它在内部使用了内部类).尽管如此,该示例仍然不合法,因为它缺少返回类型(和方法体). (9认同)
  • @Pokechu22感谢您让我知道; 修好我的榜样.是的,很少有人知道你可以写下这样的方法. (2认同)
  • 整洁,我今天学到了新东西.这种语法对Java级别的任何内容都有用吗?换句话说,你能用"this"参数做任何事情,没有它你不能做吗? (2认同)
  • @KevinWorkman你刚看到这个结构有一个有用的东西:它是一个好的书呆子派对堵嘴; 因为你可以让很多人惊讶.但是,严肃地说,请参阅我的另一个答案http://stackoverflow.com/questions/39439308/java-lambda-expression-parameters/39439458#39439458,它显示了这个微妙的细节实际上如何帮助某些lambda表达式.如果你喜欢你在那里学到的东西,请考虑提出一个答案,因为这将有助于我今天达到200个上限.我喜欢每日上限;-) (2认同)

Tom*_*son 18

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

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

而当你设置i为200时TestChild,两个被调用i的字段都是可见的,但是因为它们具有相同的名称,所以TestChild::i 隐藏 TestParent::i,并且最终设置TestChild::i并保持TestParent::i不变.

  • 这是这个问题的真正答案.我不知道为什么其他答案都在谈论方法论证. (5认同)