试比较以下两行代码在被多次反复执行时的效率差异:由于String被实现为不可变对象,JVM底层将其实现为常量池,既所有值等于"stringette" 的String对象实例共享同一对象地址,而且还可以保证,对于所有在同一JVM中运行的代码,只要他们包含相同的字符串字面常量,该对象就会被重用。
我们继续比较下面的例子,并测试他们在运行时的效率差异:
- Boolean b = Boolean.valueOf("true");
- Boolean b = new Boolean("true");
前者通过静态工厂方法保证了每次返回的对象,如果他们都是true或false,那么他们将返回相同的对象。换句话说,valueOf将只会返回Boolean.TRUE或Boolean.FALSE两个静态域字段之一。而后面的Boolean构造方式,每次都会构造出一个新的Boolean实例对象。这样在多次调用后,第一种静态工厂方法将会避免大量不必要的Boolean对象被创建,从而提高了程序的运行效率,也降低了垃圾回收的负担。
继续比较下面的代码:
- public class Person {
- private final Date birthDate;
- public boolean isBabyBoomer {
- Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- c.set(1946,Calendar.JANUARY,1,0,0,0);
- Date dstart = c.getTime();
- c.set(1965,Calendar.JANUARY,1,0,0,0);
- Date dend = c.getTime();
- return birthDate.compareTo(dstart) >= 0 && birthDate.compareTo(dend) < 0;
- }
- }
- public class Person {
- private static final Date BOOM_START;
- private static final Date BOOM_END;
- static {
- Calender c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- c.set(1946,Calendar.JANUARY,1,0,0,0);
- BOOM_START = c.getTime();
- c.set(1965,Calendar.JANUARY,1,0,0,0);
- BOOM_END = c.getTime();
- }
- public boolean isBabyBoomer() {
- return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
- }
- }
改进后的Person类只是在初始化的时候创建Calender、TimeZone和Date实例一次,而不是在每次调用isBabyBoomer方法时都创建一次他们。如果该方法会被频繁调用,效率的提升将会极为显著。
集合框架中的Map接口提供keySet方法,该方法每次都将返回底层原始Map对象键数据的视图,而并不会为该操作创建一个Set对象并填充底层Map所有键的对象拷贝。因此当多次调用该方法并返回不同的Set对象实例时,事实上他们底层指向的将是同一段数据的引用。
在该条目中还提到了自动装箱行为给程序运行带来的性能冲击,如果可以通过原始类型完成的操作应该尽量避免使用装箱类型以及他们之间的交互使用。见下例:
- public static void main(String[] args) {
- Long sum = 0L;
- for (long i = 0; i < Integer.MAX_VALUE; ++i) {
- sum += i;
- }
- System.out.println(sum);
- }
本例中由于错把long sum定义成Long sum,其效率降低了近10倍,这其中的主要原因便是该错误导致了2的31次方个临时Long对象被创建了。