"这" keyword:Java中的工作机制 [英] "this" keyword: Working mechanism in Java

查看:127
本文介绍了"这" keyword:Java中的工作机制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在学习Java一段时间之后,第一次使用这个关键字让我非常困惑。



<这是我如何感到困惑。我编写了以下代码:

  class BasicInheritanceTest3Base {
private int x = 0;
public int y;

public void a(){
x ++;
this.x ++;
System.out.println(BasicInheritanceTest3Base.a());
b();
this.b();
System.out.println(x);
System.out.println(y);
}

public void b(){
System.out.println(BasicInheritanceTest3Base.b());
}
}

公共类BasicInheritanceTest3扩展BasicInheritanceTest3Base {
private int x = 3;
public int y = 2;

public void b(){
System.out.println(BasicInheritanceTest3.b());
}

public static void main(String [] args){
BasicInheritanceTest3 bit2 = new BasicInheritanceTest3();
bit2.a();
}
}

我得到以下输出:

  BasicInheritanceTest3Base.a()
BasicInheritanceTest3.b()
BasicInheritanceTest3.b()
2
0

现在第一个问题是:为什么 x this.x 指向基类的 x 而不是Child类?如果 this.x 指向基类的 x ,为什么 this.b ()调用子类的 b()?字段和方法的行为是否不同?



然而,主要关注的是这个关键字的机制。
我的意思是你知道,这个点(引用)当前对象。如果你想一想,它不是一种神奇的行为。在某个地方必须有这个字段。例如,类的 .class 文字是不可见的,但存在于发出的字节码中。类似地,这个引用应该出现在字节码中。



好的,假设上面是真的,这个应该是一个 public final (一个空白的final),每次构造对象并实例化其字段时都会实例化。这意味着它是一个实例变量而不是静态变量。



现在,如果将其实例化为当前对象的引用(仅限于特定对象),为什么上面使用这个对于字段和方法有什么不同?总而言之,这个背后的机制是什么?该机制是否也适用于超级关键字?



编辑:每个人都在阅读问题,然后评论,我想问一下,编译器声明的这个字段在哪里,它的限定符是什么。结果行为是如何在幕后发生的?

解决方案

其他答案和评论解释了字段不是多态的以及字段访问的方式表达式根据实例引用的编译时类型进行解析。下面,我解释字节代码如何处理这个引用。



在关于接收参数的章节中 Java虚拟机规范状态


如果将n个参数传递给实例方法,则按
约定接收它们为新方法调用创建的帧
的编号为1到n的局部变量。参数以
的形式收到。例如:

  int addTwo(int i,int j){
return i + j;
}

编译为:

 方法int addTwo(int,int)
0 iload_1 //推送局部变量1的值(i)
1 iload_2 //推送局部变量2的值(j)
2 iadd // Add;在操作数堆栈上保留int结果
3 ireturn //返回int结果

按约定,实例方法在本地变量0中传递对其$ b​​ $ b实例的引用。在Java编程语言中,
实例可通过访问关键字。



类(静态)方法没有实例,因此对于它们,使用
的局部变量0是不必要的。类方法在索引0处开始使用本地
变量。如果addTwo方法是类方法,则其
参数将以与第一个版本类似的方式传递:

  static int addTwoStatic(int i,int j){
return i + j;
}

编译为:

 方法int addTwoStatic(int,int)
0 iload_0
1 iload_1
2 iadd
3 ireturn

唯一的区别是方法参数出现在
本地变量0而不是1开始。


换句话说,您可以查看,因为没有在任何地方声明或被声明为每个实例方法的第一个参数。为每个实例方法创建一个局部变量表条目,并在每次调用时填充。



关于 调用方法 状态


正常的方法调用实例方法调度对象的
运行时类型。 (它们是虚拟的,用C ++术语。)这样的
调用是使用 invokevirtual 指令实现的,
将其作为参数作为a的索引。运行时常量池条目
给出
对象的类类型的二进制名称的内部形式,要调用的方法的名称,以及该方法的描述符
(§4.3。 3)。要调用之前定义为实例
方法的 addTwo 方法,我们可能会写:

  int add12and13(){
return addTwo(12,13);
}

这个编译为:

 方法int add12and13()
0 aload_0 //推送局部变量0(this)
1 bipush 12 //推送int常量12
3 bipush 13 //推送int常量13
5 invokevirtual#4 //方法Example.addtwo(II)I
8 ireturn //在操作数堆栈顶部返回int;
//它是addTwo()的整数结果

调用是通过首先将对当前
实例的引用 this 推送到操作数堆栈来设置。
方法调用的
参数,然后推送 int 值12和13。当
的框架创建 addTwo 方法时,传递给方法
的参数将成为新框架局部变量的初始值。 也就是说,
这个的引用和两个参数,被调用者推送到操作数
堆栈,将成为初始值本地
变量0,1和2的调用方法。



After learning Java for sometime, its the first time the use of this keyword has confused me so much.

Here is how I got confused. I wrote the following code:

class BasicInheritanceTest3Base{
    private int x = 0;
    public int y;

    public void a() {
        x++;
        this.x++;
        System.out.println("BasicInheritanceTest3Base.a()");
        b();
        this.b();
        System.out.println(x);
        System.out.println(y);
    }

    public void b(){
        System.out.println("BasicInheritanceTest3Base.b()");
    }
}

public class BasicInheritanceTest3 extends BasicInheritanceTest3Base {
    private int x = 3;
    public int y = 2;

    public void b() {
        System.out.println("BasicInheritanceTest3.b()");
    }

    public static void main(String[] args){
        BasicInheritanceTest3 bit2 = new BasicInheritanceTest3();
        bit2.a();
    }
}

I got the following output:

BasicInheritanceTest3Base.a()
BasicInheritanceTest3.b()
BasicInheritanceTest3.b()
2
0

Now the first question here is: Why x and this.x point to the x of base class and not the Child class? And if this.x points to the x of the base class, why this.b() calls the b() of child class? Is the behavior of this different for fields and methods?

However, the primary concern is regarding the this keyword's mechanism. I mean you know, this points (refers) to the current object. If you think about, its not a magical behavior. There is got to be this field somewhere. For example, the .class literal for a class is invisible but is present in the emitted bytecode. Similarly, this reference should be present in the bytecode.

Alright, assuming above is true, this should be a public final (a blank final) which gets instantiated every time the object is constructed and its fields are instantiated. This implies that it is an instance variable not a static variable.

Now, if this gets instantiated to the current object's reference (which is a specific object only), how come above use of this is different for fields and methods? So all in all, what is the mechanism behind this? Does the mechanism also holds for the super keyword?

EDIT: Everyone reading the question and then the comments, I want to ask about, Where is this field declared by the compiler and what are its qualifiers. How does the resulting behavior happens behind the scenes?

解决方案

Other answers and comments have explained how fields are not polymorphic and how field access expressions are resolved based on the compile time type of the instance reference. Below, I explain how the byte code handles the this reference.

In the chapter about Receiving Arguments, the Java Virtual Machine Specification states

If n arguments are passed to an instance method, they are received, by convention, in the local variables numbered 1 through n of the frame created for the new method invocation. The arguments are received in the order they were passed. For example:

int addTwo(int i, int j) {
    return i + j;
}

compiles to:

Method int addTwo(int,int)
0   iload_1        // Push value of local variable 1 (i)
1   iload_2        // Push value of local variable 2 (j)
2   iadd           // Add; leave int result on operand stack
3   ireturn        // Return int result

By convention, an instance method is passed a reference to its instance in local variable 0. In the Java programming language the instance is accessible via the this keyword.

Class (static) methods do not have an instance, so for them this use of local variable 0 is unnecessary. A class method starts using local variables at index 0. If the addTwo method were a class method, its arguments would be passed in a similar way to the first version:

static int addTwoStatic(int i, int j) {
    return i + j;
}

compiles to:

Method int addTwoStatic(int,int)
0   iload_0
1   iload_1
2   iadd
3   ireturn

The only difference is that the method arguments appear starting in local variable 0 rather than 1.

In other words, you can either view this as not being declared anywhere or as being declared as the first parameter of every instance method. A local variable table entry is created for each instance method and populated on each invocation.

The chapter on Invoking methods states

The normal method invocation for a instance method dispatches on the run-time type of the object. (They are virtual, in C++ terms.) Such an invocation is implemented using the invokevirtual instruction, which takes as its argument an index to a run-time constant pool entry giving the internal form of the binary name of the class type of the object, the name of the method to invoke, and that method's descriptor (§4.3.3). To invoke the addTwo method, defined earlier as an instance method, we might write:

int add12and13() {
    return addTwo(12, 13);
}

This compiles to:

Method int add12and13()
0   aload_0             // Push local variable 0 (this)
1   bipush 12           // Push int constant 12
3   bipush 13           // Push int constant 13
5   invokevirtual #4    // Method Example.addtwo(II)I
8   ireturn             // Return int on top of operand stack;
                        // it is the int result of addTwo()

The invocation is set up by first pushing a reference to the current instance, this, on to the operand stack. The method invocation's arguments, int values 12 and 13, are then pushed. When the frame for the addTwo method is created, the arguments passed to the method become the initial values of the new frame's local variables. That is, the reference for this and the two arguments, pushed onto the operand stack by the invoker, will become the initial values of local variables 0, 1, and 2 of the invoked method.

这篇关于&QUOT;这&QUOT; keyword:Java中的工作机制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆