Java中的铸造成本是多少?避免它是一个好主意吗?

时间:2014-10-13 08:43:01

标签: java casting

我知道有两种类型的强制转换是隐式显式强制转换。我在StackOverflow上阅读了不同的问题,例如thisthisthis,但我仍然想知道在Java中投射的成本是多少,避免它是个好主意?它的最佳实践是什么?

有两种类型的铸造:

String s = "Cast";
Object o = s; // implicit casting

Object o = someObject;
String s = (String) o; // explicit casting

在第二种情况下,运行时会产生开销,因为必须检查两种类型,如果不能进行转换,JVM必须抛出ClassCastException

有人说最好对应用程序进行概要分析,以确保转换是否有用。

5 个答案:

答案 0 :(得分:18)

类型转换通常是一个指标,您应该考虑针对您的问题的替代解决方案。毕竟,在编译时可以检查的所有内容都将有助于程序员。

然而,有时类型转换是不可避免的,并且在通用代码中它们经常发生而程序员没有注意到。因此,我们做出了很大的努力来使类型演员变得非常快。

过去,运行时类型转换包括必须遍历超类型层次结构才能找到匹配项的可能性。今天,类型转换只不过是一个数字比较加上一个简单的指针比较,如果没有优化,当分析器可以证明转换将永远成功。

为了快速进行类型转换,每个类都知道它在类层次结构中的深度,并且有一个包含所有超类型的表。要测试一个类,比较它的深度,如果深度较低,则它不能是兼容的类,并且类型转换失败。否则,在等于检查类深度的位置处的表条目必须完全匹配,以便全部进行测试。

例如:

Object o=new JButton();
Container c=(Container)o;

Container的深度为3,下面是超类表:

Object
Component
Container

JButton的深度为5,以及下表的超类:

Object
Component
Container
JComponent
JButton

现在进行类型转换检查:

  • JButton的深度为5 ≥ 3,深度为Container,因此测试可能会成功
  • 检查表格中的第三个条目,它是完全匹配的:

    Object          Object
    Component       Component
    Container   <=> Container
    JComponent
    JButton
    

因此不再遍历层次结构,并且类型转换操作相当便宜。

答案 1 :(得分:6)

  

在第二种情况下,运行时会产生开销,因为必须检查这两种类型,如果不能进行转换,JVM必须抛出ClassCastException。

不一定,很可能对于像这样的简单情况,JIT将能够确定演员将始终有效并将优化支票。运行a microbenchmark确认了这一假设:

Benchmark                  Mode  Samples  Score   Error  Units
c.a.p.SO26335959.cast      avgt        5  3.177 ± 0.060  ns/op
c.a.p.SO26335959.noCast    avgt        5  3.174 ± 0.108  ns/op

在更复杂的情况下,分支预测也会对您有利。

底线,像往常一样:测量,不要猜测!

答案 2 :(得分:1)

除非您正在为服务器编写性能关键循环,否则您不应该考虑哪些更快。思考而不是干净,可维护的代码。并且铸造几乎总是与这些目标紧张,并且通常可以通过更好的代码结构来消除。

回答你的问题。向上投射通常几乎不需要任何费用(当您将引用类型更改为对象的父类时)。了解引用类型足以判断uptyping是否有效,它只是让类加载器查找继承映射。它也是一个可以在编译时完成的检查。 (除非参考类型已经下调)。

向下转换速度较慢,因为JVM必须接受引用,查找实际的对象类,它可能有许多类型(实现许多接口),然后为每个这样的接口检查是否要转换为每个(可能很多)引用类型的有效类型(或多或少)uptyping。由于向下转换引用实际对象,因此只能在运行时检查它。

无论如何,向下转换通常不是性能问题(并且可能依赖于JVM),但我相信通过继承结构的病态选择可能会这样做。特别是在允许多重继承的语言中。

您可能会发现this link有趣

答案 3 :(得分:0)

尽可能避免施放。铸造的东西更难维护,因此成本更高 - 如果你需要检查一个物体,请使用&#39; instanceof&#39;(例如:

if (o instanceof String) {
   // do something
}

JVM使用它来检查你的对象是否是一个String并且因为你设置o = s而返回true。

如果您稍后在代码中更改了类的超类,则强制转换可能不再有效,您需要重新编写代码,而在instanceof后面,您只需更改应检查的类。

//如果我错了,请纠正我。

关于时间投射的成本:正如我之前提到的,如果您稍后更改代码或更改超类,将花费大量时间。运行时,由于没有类型安全性而完成转换,因此检查需要较少的运行时间,然后由于未经许可的转换尝试而导致异常成本。

答案 4 :(得分:0)

生成的字节代码包含指令checkcast。所以,是的,铸造成本。虽然它并不昂贵,但很可能它会在运行时由JIT优化。