我们使用了静态代码块来创建conn实例,改进后只有在类加载初始化的时候创建了conn实例一次,而不是在每次调用getConnection方法的时候都去创建conn实例。如果getConnection方法被频繁的调用和使用,这种方式将会显著的提高我们程序的性能。除了提高性能之外,代码的含义也更加的清晰了,使得代码更易于理解。
第四部分:Map接口的keySet方法返回该Map对象的Set视图,其中包含该Map中所有的键(key)。粗看起来,好像每次调用keySet都应该创建一个新的Set实例,但是,对于一个给定的Map对象,实际上每次调用keySet都返回同样的Set实例。虽然被返回的Set实例一般是可改变的,但是所有返回的对象在功能上是等同的:当其中一个返回对象发生变化的时候,所有其他返回对象也要发生变化,因为它们是由同一个Map实例支撑的。虽然创建keySet视图对象的多个实例并无害处,却也是没有必要的。这一部分内容我不是特别的理解,贴一段代码给大家分析,如果有对这部分比较理解的朋友,请留下你宝贵的经验,万分感谢!
1 package com.czgo.effective; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.Map; 6 import java.util.Set; 7 8 public class TestKeySet { 9 10 public static void main(String[] args) { 11 12 Map<String,Object> map = new HashMap<String,Object>(); 13 map.put("A", "A"); 14 map.put("B", "B"); 15 map.put("C", "C"); 16 17 Set<String> set = map.keySet(); 18 Iterator<String> it = set.iterator(); 19 while(it.hasNext()){ 20 System.out.println(it.next()+"①"); 21 } 22 23 System.out.println("---------------"); 24 25 map.put("D", "D"); 26 set = map.keySet(); 27 it = set.iterator(); 28 while(it.hasNext()){ 29 System.out.println(it.next()+"②"); 30 } 31 32 } 33 34 }
第五部分:有一种创建多余对象的新方法,称作自动装箱(autoboxing),它允许程序员将基本类型和装箱基本类型(Boxed Primitive Type<引用类型>)混用,按需要自动装箱和拆箱。自动装箱使得基本类型和引用类型之间的差别变得模糊起来,但是并没有完全消除。它们在语义上还有着微妙的差别,在性能上也有着比较明显的差别。考虑下面的程序,它计算所有int正值的总和。为此,程序必须使用long变量,因为int不够大,无法容纳所有int正值的总和:
1 package com.czgo.effective; 2 3 public class TestLonglong { 4 5 public static void main(String[] args) { 6 Long sum = 0L; 7 for(long i = 0; i < Integer.MAX_VALUE; i++){ 8 sum += i; 9 } 10 System.out.println(sum); 11 } 12 13 }
这段程序算出的结果是正确的,但是比实际情况要慢的多,只因为打错了一个字符。变量sum被声明成Long而不是long,意味着程序构造了大约2的31次方个多余的Long实例(大约每次往Long sum中增加long时构造一个实例)。将sum的声明从Long改成long,速度快了不是一点半点。结论很明显:要优先使用基本类型而不是引用类型,要当心无意识的自动装箱。
最后,不要错误地认为"创建对象的代价非常昂贵,我们应该尽可能地避免创建对象"。相反,由于小对象的构造器只做很少量的显示工作,所以小对象的创建和回收动作是非常廉价的,特别是在现代的JVM实现上更是如此。通过创建附加的对象,提升程序的清晰性、简洁性和功能性,这通常是件好事。