其他答案和评论已经说明了字段不是多态的,以及如何根据实例引用的编译时类型解析字段访问表达式。下面,我解释字节码如何处理
this引用。
在关于 接收参数
的章节中,Java虚拟机规范指出
如果将n个参数传递给实例方法,则按照惯例,它们是在为新方法调用创建的框架的编号为1到n的局部变量中接收的。参数按传递顺序接收。例如:
int addTwo(int i, int j) { return i + j;}编译为:
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 stack3 ireturn // Return int result按照约定,实例方法将在局部变量0中传递对其实例的引用。在Java编程语言中,可通过
this关键字访问实例。类(静态)方法没有实例,因此对于它们而言,不需要使用局部变量0。一个类方法从索引0开始使用局部变量。如果addTwo方法是一个类方法,则其参数将以与第一个版本相似的方式传递:
static int addTwoStatic(int i, int j) { return i + j;}编译为:
Method int addTwoStatic(int,int)0 iload_01 iload_12 iadd3 ireturn唯一的区别是方法参数从局部变量0而不是1开始出现。
换句话说,您可以将其
this视为未在任何地方声明或被声明为每个实例方法的第一个参数。为每个实例方法创建一个局部变量表条目,并在每次调用时填充该条目。
关于
调用方法
的章节说明
实例方法的常规方法调用将分派对象的运行时类型。(使用C
++术语,它们是虚拟的。)这样的调用是使用invokevirtual指令实现的,该指令以运行时常量池条目的索引作为其参数,给出对象的类类型的二进制名称的内部形式,要调用的方法的名称,以及该方法的描述符(第4.3.3节)。要调用addTwo先前定义为实例方法的方法,我们可以编写:int add12and13() { return addTwo(12, 13);}编译为:
Method int add12and13()0 aload_0 // Push local variable 0 (this)1 bipush 12// Push int constant 123 bipush 13// Push int constant 135 invokevirtual #4 // Method Example.addtwo(II)I8 ireturn // Return int on top of operand stack; // it is the int result of addTwo()通过首先将对当前实例的引用推
this入操作数堆栈来建立调用。然后将方法调用的参数(int值12和13)推送。addTwo创建该方法的框架时,传递给该方法的参数将成为新框架的局部变量的初始值。
也就是说,this由调用者将对的引用和两个参数推入操作数堆栈,它们将成为被调用方法的局部变量0、1和2的初始值。



