在网上搜索之后,我还没有找到关于实例变量在Java内存模型中的确切位置的完整答案。例如,我们有这个代码(带变量的阴影声明):
class A {
int var = 1;
void m() {
System.out.println("\'m()\' is called from class A");
}
}
class B extends A {
int var = 5;
void m() {
System.out.println("\'m()\' is called from class B");
}
}
public class Class1 {
public static void main(String args[]) {
A aref = new B();
aref.m();
String s = (aref.var)==1?"A":"B";
System.out.println("\'var\' is called from class " + s);
}
}
此代码的输出为:
'm()' is called from class B
'var' is called from class A
现在的问题不是继承如何在Java中工作,而是Java实例变量所在的Java内存模型中的位置?请说明你的答案。
由于
答案 0 :(得分:4)
一个Object保存在堆内,但是作为一个内存块等于所有变量组合的大小+一些额外的字节来存储虚拟方法表(VMT)和Object的原型位置(可能更多取决于JVM实现)。
所以你的示例对象在(32位)内存中看起来像这样(指针值仅用于表示):
[0000] 0154 // pointer to prototype
[0004] 3625 // pointer to virtual method table
[0008] 0001 // int var
现在在上面的示例中,没有访问任何成员,因此JVM所做的就是找到该原型的VMT,跳转到那里写下的函数地址并执行它。
如果你的var = 1
代码实际通过优化器,那么生成的汇编代码就不会知道这个“var”的东西,而是使用直接内存访问。像这样:
set [4924 + 8], 1
其中4924是实例的内存位置,+ 8是变量var
的偏移量。请记住,这是(使人类可读的)程序集而不是字节代码,所以基本上在JIT完成之后还剩下什么。
由于你的两个对象大小相同,甚至可以“向上”A到B,这是它不起作用的唯一原因,因为Java禁止这种不安全的操作。在其他不那么安全的语言中,比如C ++,你可以很容易地做到这一点并且可能会侥幸逃脱。
答案 1 :(得分:1)
变量与继承时的方法不同。你的方法m()在扩展A时会在你的B类中被覆盖。但是,B不能覆盖其父类的局部变量,因为它对它们没有任何管辖权。