【问题标题】:How does Java Object casting work behind the scene? [duplicate]Java 对象转换如何在幕后工作? [复制]
【发布时间】:2012-11-15 20:11:28
【问题描述】:

可能重复:
How does the Java cast operator work?
Java casting implementation

我一直想知道对象转换在 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;

我知道你不需要强制转换它,但如果你这样做了怎么办?后端发生了什么重要的事情吗?

【问题讨论】:

  • 你的意思是class Child extends Parent 对吧?
  • 您不能在继承层次结构中将运行时类型的对象向下转换为高于您希望向下转换为的子类,这将引发 ClassCastException。我提出了修改建议,如果我错了,请纠正我。

标签: java casting polymorphism


【解决方案1】:

当您使用显式转换时,不会在系统中创建新对象(除了在最后一种情况下,您将原始类型转换为 object wrapper,因为 double 不是像 Double 这样的对象)。请注意,由于 Java 的 autoboxing feature,这个显式转换是不必要的。

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

第一个场景的答案是最复杂的。本质上,父类被视为接口。当您将Child 转换为Parent 时,只有Parent API 可用。但是,the overridden method will still be called。所以,如果你这样做:

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

...Child 的 public void a() 将被调用,即使它是通过 Parent 类的镜头看到的。但是,如果您要在 Child 中使用 Parent 所没有的第二种方法(例如 public void b()),您将无法在不强制转换的情况下调用它对象回Child。

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

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

这里有四个引用(p、p1、p2 和 p3),每个引用都指向您使用 new Parent() 声明创建的同一个对象。

不过,我可能会在哲学观点上争论,当你说Parent p = something 时,这种新引用的创建实际上是明确的,而不是在幕后。

链接:

【讨论】:

    【解决方案2】:

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

    强制转换会影响语法检查器查看对象的方式, 它不会影响对象本身,将 Child 强制转换为 父母,还是孩子。

    但是,演员表只在运行时检查。 这就是为什么它很危险,不应该使用 除非没有别的办法。

    【讨论】:

    • 我认为你的答案是最短的而且非常清楚,除了那个答案我想标记一些有用的东西:如果你想在投射对象时避免危险情况(CastClassException),你可以一直检查 if(object instanceof CastObjectClass)
    • 语法检查时间是什么意思,是编译时间吗??
    • @vinay "编译" 包括几个不总是组合在一起的步骤,parsing, syntax checking, compilation, linking。从技术上讲,编译语法错误的程序是可能的。在 java 中,它们通常一起发生,但这种类型的错误通常是由语法检查器发出的。
    【解决方案3】:

    根据这个: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        
    }
    

    【讨论】:

      【解决方案4】:

      首先,要非常小心不要将 conversion 与 casting 混淆。它们可能共享表面语法,但过程非常不同。

      在 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。该值是一个参考。

      【讨论】:

      • 我不相信你给出的双重例子会涉及浮点数。
      • 没错,必须有一个f。真的很遗憾,因为那样会更有教育意义。
      【解决方案5】:

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


      Child b = (Child) new Object();
      

      ClassCastException 失败。 JVM 将new Object() 中的getClass() 与Child.class 进行比较。由于Object.class 不是Child.class 的子类,所以会抛出异常。


      Double d = (Double) 3.3;
      

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

      Double d = Double.valueOf(3.3);
      

      这被称为Autoboxing。

      【讨论】:

      • @theon:感谢您的宝贵编辑。
      猜你喜欢
      • 1970-01-01
      • 2012-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 2010-11-07
      • 1970-01-01
      相关资源
      最近更新 更多