zoukankan      html  css  js  c++  java
  • 第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有高度优化的垃圾回收器,性能会超过轻量级的对象池。

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

  • 相关阅读:
    (转)C#中的那些全局异常捕获
    mysql 5.7 MGR
    mysql 5.7 MGR
    perl 获取响应头
    python 获取响应头
    研究生开咖啡厅,年盈利15万,欲打造重庆咖啡文化
    女学生经营二手服装租赁,年营业额突破300万
    在校学生看中餐饮外送行业,企业估值500亿
    导演跨界跳入椰子水“新泳池”,一举占领椰子产品市场
    在校女学生,掌管27家卤味连锁店
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5610424.html
Copyright © 2011-2022 走看看