以这个类为例:
public class Tmp {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
它编译成这样:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
16: pop
17: return
}
虽然这门课:
public class Tmp {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
编译成这样:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: return
}
您会看到唯一的区别是第一个(使用 an ArrayList
)调用invokevirtual
和另一个(使用List
uses)invokeinterface
。 invokeinterface
实际上比头发慢invokevirtual
(根据这个家伙的说法约为 38% )。这显然是由于 JVM 在搜索具体类的虚拟方法表而不是接口的方法表时可以进行的优化。所以你说的其实是真的。接口调用比具体类调用慢。
但是,您必须考虑我们正在谈论的速度。对于 100,000,000 次调用,实际差异为 0.03 秒。因此,您必须进行大量调用才能真正显着降低速度。
另一方面,正如@ChinBoon 指出的那样,对接口进行编码使使用您的代码的人变得更加容易,特别是如果您的代码返回某种 sort List
。因此,在绝大多数情况下,编码的便利性远远超过了相对的性能开销。
在阅读@MattQuigley 的评论并考虑开车回家后添加
这一切意味着这不是你应该过分担心的事情。任何性能增益或损失都可能非常小。
请记住,将接口用于返回类型和方法参数是一个非常好的主意。这使您和任何使用您的代码的人都可以使用List
最适合他们需要的任何实现。我的示例还表明,如果您碰巧使用了一个在List
99% 的情况下返回的方法,那么您最好将其强制转换为具体的类来提高性能。施放所需的时间可能会超过性能上的收益。
话虽如此,这个例子还表明,对于局部变量,使用具体类而不是接口确实更好。如果您使用的唯一方法是方法 on List
,那么您可以切换出没有副作用的实现类。另外,如果需要,您将可以访问特定于实现的方法。此外,还有轻微的性能提升。
tl;博士
始终使用接口作为方法的返回类型和参数。对局部变量使用具体类是个好主意。如果您使用的唯一方法是在接口上,它会带来较小的性能提升,并且切换实现不会产生任何成本。最后,你不应该太担心它。(除了返回类型和参数。这很重要。)