可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Consider:
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();
}
}
I know that similar questions have been asked, but I couldn't get a firm understanding of the 'this' variable in Java.
Let me try to explain how I understand the result of the above image.
Since it's a new TestChild()
object that's calling the printName()
method, the this
variable in line 6 is set to a TestChild
object - {TestChild@428} according to the Debugger.
However, since Java doesn't have a virtual field - I'm not completely sure what this means, but I conceptually understand it as being the opposite of Java methods, which support Polymorphism - this.i
is set to 100 of TestParent
at compile time.
So no matter what this
is, this.i
in a TestParent
method will always be the i
variable in the TestParent
class.
I'm not sure that my understanding is correct so please correct me if I'm wrong.
And also, my main question is,
How is the this
variable set to the current object that's calling the method? How is it actually implemented?
回答1:
In essence, there is no difference between
this.foo()
and
anyObject.foo()
as both are "implemented" the same way. Keep in mind that "in the end" "object orientation is only an abstraction, and in "reality" what happens is something like:
foo(callingObject)
In other words: whenever you use some object reference to call a method ... in the end there isn't a call on some object. Because deep down in assembler and machine code, something like "a call on something" doesn't exist.
What really happens is a call to a function; and the first (implicit/invisible on the source code level) parameter is that object.
BTW: you can actually write that down in Java like:
class Bar {
void foo(Bar this) { ... }
and later use
new Bar().foo();
And for this.fieldA, in the end: you have a reference to some location in memory; and a table that tells you on which "offset" you will find fieldA.
Edit - just for the record. If you are interested in more details about foo(Bar this) - you can turn to this question; giving the details in the Java spec behind it!
回答2:
What's happening here is that there are two completely different fields both called i
; to use their full names, one is TestParent::i
and one is TestChild::i
.
Because the method printName
is defined in TestParent
, when it refers to i
, it can only see TestParent::i
, which is set to 100.
Whereas when you set i
to 200 in TestChild
, both fields called i
are visible, but because they have the same name, TestChild::i
hides TestParent::i
, and you end up setting TestChild::i
and leaving TestParent::i
untouched.
回答3:
Well when a new object is created that object has an address in memory so you can think of it as if the object had a private member this
that is set to the address when the object is created. You can also think of it like this: obj.method(param)
is just syntactic sugar for method(obj, param);
and this
is actually a parameter of method
.
回答4:
To directly address what you see in the output: The call to print 'this.i' is passing as argument to 'print()' the value of the field 'i' in the current scope, which is the scope of the parent class. By contrast, the call to print 'this' is getting translated under the hood to a call to print 'this.getClass().getName()' [roughly speaking], and the 'getClass()' call gets the actual class object, which is for the child class.
回答5:
Adding some more info on top of @Tom Anderson answer, which explains hiding concept nicely.
I have added one more constructor in Child ( TestChild
) which prints values of i in both parent and child.
If you want get value of i
from child (TestChild
), override the method in 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();
}
}
Case 1: If I comment super.printName()
call from child, Child version of TestChild.printName()
prints the value of i in TestChild
Output:
TestChild.i and TestParent.i:200:100
TestChild:printName()
TestChild@43cda81e
200
Case 2: TestChild.printName() calls super.printName() as the first line in printName() method. In this case, i value from both parent and child are displayed in respective methods.
Output:
TestChild.i and TestParent.i:200:100
TestParent:printName()
TestChild@43cda81e
100
TestChild:printName()
TestChild@43cda81e
200