第5条:避免创建不必要的对象

一般来说,在能重用对象的时候就重用对象而不是创建一个相同功能的新对象。如果对象是不可变的,它始终能被重用。

考虑String s = new String("stringette");

每次被执行的时候都创建一个新的String实例,但这些创建对象的动作是不必要的,如果这个语句被频繁调用,那么多产生大量不必要的String实例。

改进String s = "stringette" 这样只需要一个String实例,即使多次执行,都返回在字符串常量池的同一个引用。

同样可以这样String s = new String("stringette").intern() ,intern方法会强迫在字符串常量池中寻找该字符串,如果有则返回该引用,如果没有,则创建一个。

对于同时提供了静态工厂和构造器的不可变类,通常用静态工厂方法而不是构造器,以避免创建不必要的对象。例如,静态工厂Boolean.valueOf(String),总是返回一个相同的实例,而构造器Boolean(String),每次调用都会创建一个新对象。

观察判断一个人是否出生在1946年到1964年之间:

public class Person {
    private final Date birthDate;

    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }

    public boolean isBabayBoomer() {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
    }
}

每次调用isBabayBoomer都会创建一个Calendar,一个TimeZone和两个Date实例,这是不必要的,因为这三个变量在该方法之中是不会被修改的(它们是可变对象,但我们不会修改)

改进:既然不会被修改那么把它们作为常量是比较合适的

public class Person {
    private final Date birthDate;

    private static final Date BOOM_START;
    private static final Date BOOM_END;

    static {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }

    public Person(Date birthDate) {
        this.birthDate = birthDate;
    }

    public boolean isBabayBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
    }
}

现在仅仅在类被加载的时候创建一个Calendar,一个TimeZone和两个Date实例一次,如果isBabayBoomer被频繁调用,能显著提高性能,因为创建一个Calendar的代价很大。

可以把常量的初始化延长至isBabayBoomer方法第一次被调用,不过这样的实现方法复杂,很可能无法将性能提高到超高现在已经达到的水平,比如是使用一个内部类,这时候必须先创建一个内部类的对象,再对域进行初始化,耗费的代价可能很大。

前面讨论的例子,对象显然是能够被重用的,因为它们初始化后不再改变,有些情况就不那么明显了,考虑适配器。

有一个Duck类和一个Goose类,因为接口不兼容,使用一个DuckAddapter去适配Goose对象以使接口一致,通常会这样适配Duck d = new DuckAdapter(new Goose());

适配器只是一个把功能委托给Goose,从而为Goose提供与Duck兼容的接口 ,没有其他状态信息,所以针对某个特定对象的适配器,不需要多个适配器实例。

观察下面代码:

Long sum = 0L;
for(long i=0; i < Integer.MAX_VALUE; i++) {
    sum += i;
}
System.out.println(sum);

sum是Long类型,由于再进行运算时,Long要自动拆箱成long才能运算,所以Long会先拆成long,由于sum是Long类型,又会自动装箱成Long,程序相当于构造了大约231个Long实例。将Long sum = 0L改成 long sum = 0L,在我的机器上运行时间从11454ms减少到1277ms。

认为创建对象的代价非常昂贵,我们应该要尽可能地避免创建对象。是错误的。

相反,由于小对象的构造器只做很少量的显式工作,所以,小对象的创建和回收代价很小。通过创建附加对象,提升程序清晰性是件好事。

通过维护自己的对象池来避免创建对象并不是好做法,除非池中对象是重量级的,例如数据库连接池,建立数据库的连接代价很大,因此重用变得非常有意义。

一般来说,对象池的对象一直占用内存空间,会损害性能。现代JVM有高度优化的垃圾回收器,性能会超过轻量级的对象池。

从程序可读性来说,尽量把对象的作用域限制在最小范围,其实是鼓励重复使用小对象的。

时间: 2024-08-27 10:26:16

第5条:避免创建不必要的对象的相关文章

图形对象的创建(常用图形对象的创建方法及特殊属性)

1.图形窗口对象 MATLAB的一切图形图像的输出都是在图形窗口中完成的. 一.创建 建立图形窗口对象使用figure函数,其调用格式为:句柄变量=figure(属性名1,属性值1,属性名2,属性值2,...) MATLAB通过对属性一的操作来改变图形窗口的形式.也可以使用figure函数按MATLAB默认的属性值建立图形窗口,格式为:figure  或  句柄变量 =figure MATLAB通过figure函数建立窗口之后,还可以调用figure函数来显示该窗口,并将其设定为当前窗口,调用格

String s=new String("xyz");创建几个String对象的问题

首先让我们了解几个概念: 栈 :由JVM分配区域,用于保存线程执行的动作和数据引用. 堆 :由JVM分配的,用于存储对象等数据的区域. 常量池constant pool :在堆中分配出来的一块存储区域,用于存储显式 的String,float或者integer.这是一个特殊的共享区域,可以在内存中共享的不经常改变的东西,都可以放在这里. 进入正题: String a = "abc";①String b = "abc";② 使用String a = "abc

Item 5 避免创建不必要的对象

场景一: 这个是经常出现的问题,因为我们经常误用String. public class Test { public static void main(String[] args) { //参数"terrible"会创建一个对象 //然后,new String(),这个语句会创建一个对象 //所以,这个语句执行过程中,会创建两个对象 String s = new String("terrible."); //只创建一个对象,该对象的引用被赋值到变量 ok Strin

Effective Java之避免创建不必要的对象和消除过期的引用

为什么要避免创建不必要的对象?创建对象的过程还是比较耗性能的,根据书上给出的性能测试,重复创建对象的时间消耗是重用对象时间消耗的250倍.下面列出一些可以重用对象的场景. 1.String类型要注意,String s = "as" 和 String s = new String("as")之间有很大的差别,第二种创建String实例的方法其实会产生两个"as"对象. 2.针对已知不会被修改的可变对象,可以只在第一次使用时初始化,而不是在每次使用时

String str=new String(&quot;abc&quot;)到底创建了几个对象

这句代码到底创建了几个对象?研究了好一阵,现在才能说清楚. package com.sun.test; public class Test<T> { T a; /** * @param args */ public static void main(String[] args) { String str=new String("abc"); } } 我们来看下这段简单代码的字节码: <pre name="code" class="java

在lua中创建字段安全的对象

lua萌新,刚刚学习和使用不到一个月.有不对的地方,还望各路大神不吝赐教. lua中可以用table来模拟对象,但table是可以任意增加键值的.在对象模拟中,暂且也叫它为字段(field)吧.如果在面向对象中,你定义了一个对象,可以在对象以外的地方随意改动这个对象的字段,访问不存在的字段,你想象一下这有多恐怖?比如你定义了一个Vector3{float x = 0; float y = 0; float z = 0;}  我在外面某处加一个float t = 1; 当你在创建并引用这对象的时候

避免创建不必要的对象

1,一般来说,做好能重用对象而不是在每次需要的时候就创建一个相同功能的对象,重用方式即快速,又流行.如果对象是不可变的,他就始终可以被重用. 如:String s=new String("stringtee");该语句在每次执行时都会创建一个新的String实例,如果这种用法是在一个循环中,或者是在一个频繁调用的方法中,就会创建出成千上万个不必要的String实例. 改进方法: String s="stringtee";这个版本只用了一个实例而不是每次都创建一个新的

【转载】如何在Android中避免创建不必要的对象

在编程开发中,内存的占用是我们经常要面对的现实,通常的内存调优的方向就是尽量减少内存的占用.这其中避免创建不必要的对象是一项重要的方面. Android设备不像PC那样有着足够大的内存,而且单个App占用的内存实际上是比较小的.所以避免创建不必要的对象对于Android开发尤为重要. 本文会介绍一些常见的避免创建对象的场景和方法,其中有些属于微优化,有的属于编码技巧,当然也有确实能够起到显著效果的方法. 使用单例 单例是我们常用的设计模式,使用这种模式,我们可以只提供一个对象供全局调用.因此单例

写一个类,在任何时候都可以向它查询“你已经创建了多少个对象?”

源代码: package jxl;class sum{ public static int a=0; int c1; public sum(int c2){ c1=c2; a++; } public int get(){ return a; }}public class Test4 { public static void main(String[] args) { // TODO 自动生成的方法存根 //调用 sum one=new sum(2); sum two=new sum(3); //