Java 指向子类对象的超类ref的类类型是什么?

Java 指向子类对象的超类ref的类类型是什么?,java,class,reference,subclass,superclass,Java,Class,Reference,Subclass,Superclass,我有以下代码: 1. public class Tester 2. { 3. public static void main(String[] args) 4. { 5. A a = new B(); 6. System.out.println(a.getClass()); //Prints class B 7. System.out.println(a instanceof A); //Prints

我有以下代码:

1. public class Tester
2. {
3.      public static void main(String[] args)
4.      {
5.          A a = new B();
6.          System.out.println(a.getClass());    //Prints class B       
7.          System.out.println(a instanceof A);  //Prints true
8.          System.out.println(a instanceof B);  //Prints true
9.          System.out.println(a.valA);          //Prints 1
10.         System.out.println(a.valB);          //Compilation error
11.
12.     }
13. }
14.
15. class A
16. {
17.     int valA=1;
18. }
19.
20. class B extends A
21. {
22.     int valB=2;
23. }
第6行显示
a
类型为
B类
。但是,当它到达第10行时,编译器会产生一个错误
位置:a类型的变量a

所以我的问题是:现在
a
的类类型到底是什么?为什么
getClass()
显示它属于
class B
类型,而编译器在编译过程中将它抱怨为
A
类型

此外,既然
a instanceof B
是真的,为什么我不能访问
valB


为了让事情更清楚:


EDIT:我运行了以下语句:
System.out.println(a)输出为
B@36d98810
以某种方式证明执行了
类B
toString()
方法。既然变量
a
可以访问
类B
中的toString()方法,为什么它不能访问同样位于
类B
中的
valB

a
不是对象。这是一个变量

变量的类型为
A
。变量值在执行时引用的对象类型为
B


编译器根据所涉及表达式的编译时类型(本例中的变量)解析所有内容。当试图在编译时类型
A
内解析名称
valB
时,它找不到任何东西-因此出现错误。

这是类继承、接口等的基本属性。 类“A”没有变量“valB”。 如果要在类“B”中使用变量“valB”,则应首先将类“A”强制转换为“B”

尝试:


您应该知道对象类型和实例类型之间的区别。第一个是在编译类型时确定的,在运行时它会尽力保证该类型的安全。实例类型是实例化对象的类

A a; //this is an object type
new B(); //this is an instance type
A a = new B(); //all together, but a is of type A, having instance of type B.

您需要记住,编译执行是两个不同的过程,它们发生在不同的时间,并且具有不同的可用信息。编译器必须预测未来——它必须决定是否能够保证您的代码在未来的运行时有意义。它通过分析代码中对象的类型来实现这一点。另一方面,运行时只需检查事物的当前状态


当您阅读行
A=new B()
时,您推断出的有关
A
局部变量的信息比编译器的信息要多。编译器基本上只是将其视为
A=
。它不会记录用于生成
a
值的表达式的内容

事实上,你说的
A=…
是在告诉编译器:“嘿,这个
A
我将在我程序的其余部分处理的事情,它只是一个
A
,不要再假设了。”如果你说的是
B A=…
,那么你是在告诉编译器它是一个
B
(编译器还看到
B在代码的其他地方扩展了A
,因此它知道它也是
A

从编译器的角度来看,无论
a
a
a.getClass()
a.toString()
的类型是什么,后续表达式
a
都是合法的:
instanceof
操作符和
getClass()
toString()
方法是为所有
对象定义的(编译器不需要预测这些表达式在运行时将产生什么值,只需要它们分别产生
true
false
、一些
和一些
字符串

但是当你谈到
a.valA
a.valB
时,编译器实际上必须做一些实际的工作。它需要证明或保证
a
对象在运行时将有一个
valA
和一个
valB
字段。但是既然你之前明确告诉它,只需假设
a
是一个
A
,它无法证明在运行时它将有一个
valB
字段


现在,稍后,在执行时,JVM有了更多信息。当它计算
a.getClass()
时,它实际上会查找“隐藏”的具体类对于
a
并返回它。类似地,对于
实例B
,它查找具体的类,因此该表达式的结果是
true


a.toString()
的工作原理类似。在运行时,JVM知道
a
引用的东西实际上是a
B
,因此它执行
B
toString
方法。

加州大学伯克利分校的Jonathan Shewchuk教授解释了跟踪。18分钟开始。(如果链接改变,只需谷歌搜索CS61B第15讲:更多Java)

简而言之,变量有两种类型:静态类型和动态类型

Static type is its Type at compile time
Dynamic type is its Type at run time.
在你的例子中

A a = new B();
a的静态类型为a,动态类型为B

In Java a variable gets its non static methods from dynamic type
(if the method exists in both the parent and child class)
and 
its fields and static methods from the static type.
只有在子类中重写了该方法时,C#中才是如此

更新: 线路

告诉您a的动态类型是a的类型还是a的子类

更新2: 一个例子说明了这一点

public class PlayGround {

    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.print(a.name);// displays animal
        System.out.print("\r\n");
        a.MakeStaticSound();// displays static animal sound
        System.out.print("\r\n");
        a.MakeSound();// displays bow wow

    }

}

class Animal {
    public String name = "animal";

    public void MakeSound() {
        System.out.print("animal sound");
    }

    public static void MakeStaticSound() {
        System.out.print("static animal sound");
    }

}

class Dog extends Animal {
    public String name = "dog";

    public void MakeSound() {
        System.out.print("bow wow");
    }

    public static void MakeStaticSound() {
        System.out.print("static bow wow");
    }

}

请注意,调用a.MakeStaticSound()的可读性和首选方法是Animal.MakeStaticSound()

编译器只知道
a
类型为
a
,因此不能推断它将有一个名为
valB
的字段
a instanceof A
public class PlayGround {

    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.print(a.name);// displays animal
        System.out.print("\r\n");
        a.MakeStaticSound();// displays static animal sound
        System.out.print("\r\n");
        a.MakeSound();// displays bow wow

    }

}

class Animal {
    public String name = "animal";

    public void MakeSound() {
        System.out.print("animal sound");
    }

    public static void MakeStaticSound() {
        System.out.print("static animal sound");
    }

}

class Dog extends Animal {
    public String name = "dog";

    public void MakeSound() {
        System.out.print("bow wow");
    }

    public static void MakeStaticSound() {
        System.out.print("static bow wow");
    }

}