以这个类为例:
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
}
您会看到唯一的区别是第一个(使用ArrayList)调用invokevirtual,而另一个(使用List 使用)invokeinterface。 invokeinterface 实际上比invokevirtual 慢一点(根据this guy 大约38%)。这是apparently,因为 JVM 在搜索具体类的虚拟方法表与接口的方法表时可以进行优化。所以你说的其实是真的。接口调用比具体的类调用慢。
但是,您必须考虑我们所说的速度类型。对于 100,000,000 次调用,实际差异为 0.03 秒。因此,您必须进行 吨 次调用才能真正显着降低速度。
另一方面,正如@ChinBoon 指出的那样,对接口进行编码使那些使用您的代码的人变得更加容易,尤其是当您的代码返回某种List 时。所以在广大的大多数情况下,编码的容易程度远超过了相对的性能开销。
在阅读 @MattQuigley 的评论并考虑开车回家后添加
所有这一切的意思是,这不是您应该过分担心的事情。任何性能提升或损失都可能非常很小。
请记住,将接口用于返回类型和方法参数是一个非常好的主意。这使您和任何使用您的代码的人都可以使用最适合他们需要的List 的任何实现。我的示例还表明,如果您碰巧使用了一个在 99% 的情况下返回 List 的方法,那么不最好将其转换为具体的类来提高性能。施放所需的时间可能会超过性能上的收益。
话虽如此,这个例子还表明,对于 local 变量,使用具体类而不是接口确实更好。如果您使用的唯一方法是List 上的方法,那么您可以切换出没有副作用的实现类。另外,如果需要,您将可以访问特定于实现的方法。此外,性能略有提升。
tl;dr
始终使用接口作为方法的返回类型和参数。对局部变量使用具体类是个好主意。如果您使用的唯一方法是在接口上,它会带来较小的性能提升,并且切换实现不会产生任何成本。最后,你不应该太担心它。 (返回类型和参数除外。这很重要。)