日期:2014-05-20  浏览次数:20763 次

刨根问底:接口和类的效率问题
在看Java性能优化的一些文章中,不少都提到说new一个接口的性能一般要低于new一个类,
举例:
ArrayList<String> list = new ArrayList<String>();
        
        List<String> list2 = new ArrayList<String>();

一般都推荐用第一种方式,有点明白有似乎不明白,大拿能指点一二吗?

------解决方案--------------------
喜欢第二种方式 UP
------解决方案--------------------
就性能上来说,用接口差一些。
主要是面向对象的虚函数(动态束定)概念会引起性能下降。
------解决方案--------------------
ArrayList<String> list = new ArrayList<String>();//这就是普通的定义对象的方式
      
List<String> list2 = new ArrayList<String>();//体现面向接口编程的思想
------解决方案--------------------
在JVM中对于一个具体类,查找方法表很容易确定方法的位置

而接口是不好实现方法表的,所以要遍历以找到具体方法

可参考《深入java虚拟机》第二版 P194~P199
------解决方案--------------------
用接口来定义体现的是可扩展性强,
当然在效率上肯定是有所牺牲。
不可能什么优点都占,不过有时候这点牺牲是值得的。
------解决方案--------------------
理论上
Java的public member函数,都是virtual,共识吧?
我们调用list的某个方法,比如size()还是length()什么的,我不知道。

JVM都会调用这个指令 invokevirtual,而不是invokespecial。
那么就都是涉及到查找虚表的问题。

你怎么就知道list 是 ArrayList<String>类型。而不是派生自它的list = new MyArrayList<String>()呢?

声明成ArrayList<String>,恰巧也确实是这个类型的对象,可不表示这个类型没有派生类啊:)!



实际上

ArrayList<String> list = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
如果你写到一个函数里面了。
又分别调用了
list.length();
list2.length();

实际上编译器可能根据上下文,知道list2的实际对象,把invoke编译成非virtual的调用。都是有可能的。
为了提速嘛。


所以,看什么书,多少页都是扯淡的。
------解决方案--------------------
向楼上高手致敬。
------解决方案--------------------
虚方法的动态分派问题可以参考《深入理解Java虚拟机 JVM高级特性与最佳实践》P214~P217  P220~P221

如果说到JIT优化,不得不说到方法内联,对于虚方法该如何内联,参考《深入理解Java虚拟机 JVM高级特性与最佳实践》P307~P309
------解决方案--------------------
都是牛人,连多少页都帮找到
------解决方案--------------------
塞~这点效率 在当今这种硬件配置下 可以忽略了吧。
------解决方案--------------------
这点“损失”可以忽略不计...