46

可能的重复:
Java cast 运算符如何工作?
Java 强制转换实现

我一直想知道对象转换在 Java 中是如何工作的。我理解原始类型更像是二进制表示级别,但是对象呢?是不是有点像Polymorphism,还是dynamic binding因为一切都将在运行时确定?例如:

class Parent{
     void A(){}
}
class Child extends Parent{
     @Override
     void A(){}
}

Parent p = new Parent();
Child c = (Child) p;

这在幕后如何运作?它会创建一个新的实例Child吗?而且,如果你尝试投射会发生什么:

Child b = (Child) new Object();

最后一个,将原语转换为包装类时:

Double d = (Double) 3.3;

我知道你不需要投射它,但如果你这样做呢?后端有什么重要的事情发生吗?

4

5 回答 5

19

当您使用显式强制转换时,系统中不会创建新对象(除了在最后一种情况下,您将原始类型强制转换为对象包装器,因为double不是像Doubleis 那样的对象)。请注意,由于 Java 的自动装箱功能,这种显式转换不是必需的。

在您的(Child) new Object()场景中,您将收到ClassCastException,因为 an Objectis not a Child(尽管反之亦然)。

第一个场景的答案是最复杂的。本质上,父类被视为接口。当您将Child转换为 时Parent,只有ParentAPI 可用。然而,被覆盖的方法仍然会被调用。所以,如果你这样做:

Parent p = (Parent) new Child();
p.a();

... Child'spublic void a()将被调用,即使它是通过Parent班级的镜头看到的。Child但是,如果您在中有第二种方法Parent没有(public void b()例如),如果将对象转换回Child.

正如您所说,“幕后”,唯一创建的新事物是指向同一对象的另一个对象引用。您可以对同一个单一对象有任意多的引用。考虑这个例子:

Parent p = new Parent();
Parent p1 = p;
Parent p2 = p;
Parent p3 = p2;

在这里,有四个引用(pp1p2p3),每个引用都指向您使用new Parent()声明创建的同一个对象。

不过,我可能会在哲学观点上争论,当你说Parent p = something.

链接:

于 2012-11-15T20:16:23.000 回答
12

您的主要问题的简单答案是否定的。所有转换都发生在语法检查时。

强制转换会影响语法检查器查看对象的方式,它不会影响对象本身,将 Child 强制转换为 Parent,仍然是 Child。

然而,演员表只在运行时检查。这就是为什么它很危险,除非没有其他办法,否则不应使用。

于 2012-11-15T21:38:04.730 回答
5

根据这个:checkcast,它的作用是验证引用是否可分配。如果是,则不会更改堆栈并保留对该引用的操作。

因此,如果您有:

 Child c = ( Child )  anyObject; 
 c.sayHi();

如果转换成功,则sayHi 可以调用该方法:

如果 objectref 可以转换为已解析的类、数组或接口类型,则操作数堆栈不变;否则,checkcast 指令会抛出 ClassCastException。

这是“字节码”

$ cat CastDemo.java 
class Parent {}
class Child extends Parent {}
class Main {
    Child c = (Child) new Parent();
}
$ javap -c Main
Compiled from "CastDemo.java"
class Main {
  Child c;

  Main();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: new           #2                  // class Parent
       8: dup           
       9: invokespecial #3                  // Method Parent."<init>":()V
      12: checkcast     #4                  // class Child
      15: putfield      #5                  // Field c:LChild;
      18: return        
}
于 2012-11-15T20:22:46.273 回答
4

首先,要非常小心不要将转换强制转换混淆。它们可能共享表面语法,但是是非常不同的过程。

在 Java 中,您可以将 Object 向下转换为任何类型,但在运行时,ClassCastException如果该对象实际上与目标类型不兼容,您将得到一个。这发生在字节码级别:有一条专用于向下转换的字节码指令。

Child c = (Child) new Object();

将无条件地导致一个ClassCastException.

Double d = 3.3; // note: no explicit casting needed

自动装箱Double. 所以在这里,实际上创建了一个新实例。

一个正常的、成功的 dowcast 可能如下所示:

Object o = "a";
String s = (String)o;

在这里,没有创建对象:只有 的值o被复制到s. 该值是一个参考。

于 2012-11-15T20:15:30.037 回答
3

向下转换一个对象不会对该对象做任何事情。在幕后,编译器将注入checkcast字节码操作。如果p不是真正的实例Child,则会抛出异常。否则,您基本上对具有不同、更具体类型的同一对象具有(类型)安全引用。


Child b = (Child) new Object();

这失败了ClassCastException。JVM比较getClass()与. 由于不是 的子类,因此引发异常。new Object()Child.classObject.classChild.class


Double d = (Double) 3.3;

在这里,甚至不需要强制转换,这也可以Double d = 3.3:在幕后,这被翻译成:

Double d = Double.valueOf(3.3);

这称为自动装箱

于 2012-11-15T20:17:26.263 回答