在学习Java的过程中,我遇到了一个奇怪的事情。请考虑以下程序:
public class GetClassNameInheritance {
public static void main(String[] args) {
Employee e = new Employee();
Person p = (Person) e;
System.out.println(p.getClass());
}
}
class Person {
}
class Employee extends Person {
}
我期待输出为Person
因为演员,但它是Employee
!老实说,我很难过,无法找到解释。官方Oracle tutorial没有提到这种行为,而docs似乎太简洁而无法提供帮助。我可以从其他StackOverflow示例中了解到这与“运行时类”有关,但我不了解其基本思想。有人能解释一下这里发生了什么吗?
答案 0 :(得分:2)
在Java中,类型转换不会更改对象的类型。在Employee
后,您的对象始终为Employee
。类型转换不像其他语言(如C / C ++)那样工作。
虽然p
引用的类型为Person
,但它实际上是指JVM堆中类型为Employee
的对象。当您调用p.getClass()
方法时,引用依次调用对象上的方法而不是引用。这就是你在输出中看到Employee
的原因。
可以使用的唯一类型的强制转换就是可以对基元执行的强制转换。
int x = (int)2.5f;
返回值将类型转换为int。
您可以在此处详细了解类型转换和转换:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
http://www.wideskills.com/java-tutorial/java-object-typecasting
答案 1 :(得分:1)
那么让我们来看看编译main方法时会发生什么:
0: new #2 // class Employee
3: dup
4: invokespecial #3 // Method Employee (Constructor)
7: astore_1 // Save local variable "e"
8: aload_1 // Load local variable "e"
9: astore_2 // Save to local variable "p"
10: getstatic #4 // Field System.out PrintStream;
13: aload_2 // Load local variable "p"
14: invokevirtual #5 // Method Object.getClass()
17: invokevirtual #6 // Method PrintStream.println(Object)
如果我们看一下反编译器对这个字节码的解释:
public static void main(String[] args) {
Employee e = new Employee();
System.out.println(e.getClass());
}
编译器“优化”代码导致您的演员表被删除,因为它被认为是不必要的。 Anacron的答案解释了为什么编译器认为没必要。
答案 2 :(得分:0)
如果您想了解当前班级的超级班级,可以使用
p.getClass().getSuperclass() //returns person.
来自文档
getClass()
始终返回此Object的运行时类。返回的Class对象是由所表示的类的静态同步方法锁定的对象。
运行时类是已经使用构造函数调用实例化的类。
实际上,如果您将看到,Person p = (Person) e;
不需要强制转换,您始终可以将派生类对象分配给基类。
例如
List<String> list = new ArrayList<>();
上面不会抛出错误。