getClass()在向上转换后返回派生类名

时间:2016-12-01 04:10:27

标签: java

在学习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示例中了解到这与“运行时类”有关,但我不了解其基本思想。有人能解释一下这里发生了什么吗?

3 个答案:

答案 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<>();

上面不会抛出错误。

相关问题