Java:避免创建不必要的对象
为了保证内存的有效利用,应该尽量重用对象而不是在每次需要的时候就创建一个新的对象。
比如,构造器在每次被调用的时候就构建一个新的对象,而静态工厂方法则从来不要求这样做(顺便说一句,如果要求对象只有一个实例,可以使用静态工厂方法;如果对象有多个/不定个参数,可以使用静态Builder方法构建)。
举例来说,新建字符串有两种方法:
法一: String s = new String("This is a string."); 法二: String s = "This is a String";
在法一中,使用构造器强制构建了一个新的对象,不管相同的字符串是否已经存在;
而在法二中,则会先查看是否已经存在了该String对象,如果存在,则返回已存在String的引用,而不会再新建String对象。
因此,如果在String已经存在的情况下,法一多创建了一个不必要的String实例。如果这是在一个循环中,则频繁调用会产生成千上万个不必要的String。
再举一个关于Long和long的例子。对于Long而言,相当于上例的法一,每次需要创建新的实例,而如果对象被定义为long,则相当于法二,对象可以重新使用。代码如下:
public static void main(String[] argv){ long startTime = System.currentTimeMillis(); System.out.println("Integer.MAX_VALUE:"+Integer.MAX_VALUE); int result = 0; for(long i=0;i<Integer.MAX_VALUE;i++){ result += i; } long endTime = System.currentTimeMillis(); System.out.println("Int Result:"+result+". Total Cost Time:"+(endTime - startTime)); startTime = endTime; long sumlong2 = 0L; for(long i=0L;i<Integer.MAX_VALUE;i++){ sumlong2 += i; } endTime = System.currentTimeMillis(); System.out.println("long-long Sum:"+sumlong2+". Total Cost Time:"+(endTime - startTime)); startTime = endTime; long sumlong = 0L; for(Long i=0L;i<Integer.MAX_VALUE;i++){ sumlong += i; } endTime = System.currentTimeMillis(); System.out.println("long-Long Sum:"+sumlong+". Total Cost Time:"+(endTime - startTime)); startTime = endTime; Long sumLong1 = 0L; for(long i=0L;i<Integer.MAX_VALUE;i++){ sumLong1 += i; } endTime = System.currentTimeMillis(); System.out.println("Long-long Sum:"+sumLong1+". Total Cost Time:"+(endTime - startTime)); startTime = endTime; Long sumLong = 0L; for(Long i=0L;i<Integer.MAX_VALUE;i++){ sumLong += i; } endTime = System.currentTimeMillis(); System.out.println("Long-Long Sum:"+sumLong+". Total Cost Time:"+(endTime - startTime)); }
在代码中,分别对sum和i使用Long和long来实现,查看最后耗费时间的区别(单位:ms)。另外,由于最后结果超过int,我们把int结果作为参照。
代码结果如下:
Integer.MAX_VALUE:2147483647 Int Result:1073741825. Total Cost Time:8172 long-long Sum:2305843005992468481. Total Cost Time:8687 long-Long Sum:2305843005992468481. Total Cost Time:59188 Long-long Sum:2305843005992468481. Total Cost Time:49000 Long-Long Sum:2305843005992468481. Total Cost Time:96500
可以看到,在sum和i均为long的时候,耗费时间是最小的,8600ms左右。i单独为Long比sum单独为Long耗费时间要多一些(为什么?猜测是对i的操作比对sum的操作多一些。不过除去创建耗时之外,消耗时间和操作次数是成正比的吗?)。而毫无争议的,sum和i都为Long的情况最耗时,约为100s。因此可以看出,将变量声明为long而非Long,可以有效的节省时间。
因此,要优先使用基本类型而不是装箱类型,要当心无意识的自动装箱。
参考资料:
Effective Java 中文版