我对这个问题(写在下面)感到困惑,我认为它的答案应该是20,但为什么是10。如果我错了,那么有人能给我指引它的流向吗?
class A
{
int i = 10;
}
class B extends A
{
int i = 20;
}
public class MainClass
{
public static void main(String[] args)
{
A a = new B();
System.out.println(a.i); //Output of this line
}
}
要深入了解表达式是如何求解的,有必要参考" Java®虚拟机规范">,例如Java SE 7版。
5.4.3.2字段分辨率
解析从D到a中的字段的未解析的符号引用类或接口C,由字段给出的对C的符号引用引用必须先…
但是,基本上,每个类型都有自己的分辨率表.
如果知道一个对象的类型是A
,那么将使用解析表A
。如果已知是B
类型,则使用B
表。
在示例中:
static class A { int i = 10; }
static class B extends A { int i = 20; }
public static void main(String[] args) {
B b = new B();
A a = b;
A c = new A();
System.out.println("#1 A: " + a.i);
System.out.println("#2 A: " + ((B)a).i); // (correct) downcast!
System.out.println("#3 B: " + b.i);
System.out.println("#4 C: " + ((B)c).i); // (wrong) downcast!
}
与输出
#1 A: 10
#2 A: 20
#3 B: 20
Exception in thread "main" java.lang.ClassCastException: Test$A cannot be cast to Test$B
10或20取决于是否使用这张表或另一张表。
一个重要的问题是知道如何区分何时已知类型被认为是在编译时和在运行时考虑类型时!输出#1和#3在编译时分别为A
和B
。这就是为什么得到10或20的原因.
让我展示一下编译时和运行时的区别。
在<<p> strong>运行时a
变量实际上是一个B
对象,你可以运行
检查它System.out.println(a.getClass());
与输出
Test$B
即,虽然a
被定义为类型,但A
实际上是B
类型的对象。
在编译时,您说这是A a = b
,然后,A解析表被写入(要使用)到JVM字节码,这就是为什么你得到10而不是20。
(附加说明)
在内部,对象A
将被存储(非常粗略地))像
@ +--------+
| i = 10 |
+--------+
带分辨率表
A::i := @+0 // to get i from the start position plus 0
对象B
将包含它们的基类和自身,如
@ +--------+
| i = 10 |
+--------+
| i = 20 |
+--------+
带分辨率表
B::i := @+1 // to get i from the start position plus 1
JVM当被要求访问*::i
字段时,根据表A
或B
使用+0
或+1
。
B b = new B();
A a = b;
System.out.println("#1: " + a.i++);
System.out.println("#2: " + ((B)a).i++);
System.out.println("#3: " + a.i);
System.out.println("#4: " + ((B)a).i);
与输出
#1: 10
#2: 20
#3: 11
#4: 21
显示10和20有两个不同的变量,即使它们是同一个对象。
new B()返回B类型对象的引用,该对象是A的子类,因此可以将变量或类型A赋值给B类型的引用
现在当我们在变量a上调用方法时我们可以访问哪个方法
- 方法只在A类中定义
- 特殊情况如果我们在类B中重载了方法那么对该方法的任何调用都会调用该方法的类B版本
尝试阅读继承、重载和多态性