美文网首页读书笔记
《Effective Java》第二章:创建和销毁对象

《Effective Java》第二章:创建和销毁对象

作者: ldlywt | 来源:发表于2018-03-08 15:17 被阅读24次

    创建和销毁对象

    1.考虑用静态工厂方法代替构造器

    • 静态工厂方法有名称
    String.valueOf("1")
    
    • 静态工厂方法不必在每次调用它们的时候都创建一个新的对象
      创建对象不适用 new 关键字

    • 静态工厂方法可以返回原返回类型的任何子类型
      其实就是静态工厂方法返回值可以是多态,new 出来的必须返回原对象

    • 在创建参数化类型实例的时候,代码变的简单
      原来是:

    Map<String, List<String>> m = new HashMap<String,List<String>>();
    

    如果使用静态工厂方法:

    Public static <K,V> HashMap<K,V> newInstance(){
        return new HashMap<K,V>();
    }
    

    创建对象的时候可以这样:

    Map<String, List<String>> m = HashMap.newInstance();
    

    2.遇到多个构造器参数时要考虑用构造器

    如果需要很多的构造器,那样代码会变得非常繁琐并且不实用,每次 new 一个对象时还必须查看构造器的参数。

    • 使用 JavaBean 的setter 和 getter 方法解决
      缺点一:在构造过程中JavaBean可能处于不一致的状态
      缺点二:JavaBean模式阻止了把类做成不可变的可能,多线程的情况下会出现问题
    • Builder模式
      Builder模式我们很常用,推举使用

    3.用私有构造器或者枚举类型强化Singleton属性

    其实就是单例模式

    4.通过私有构造器强化不可实例化的能力

    就是工具类加一个私有的构造,再抛出一个异常

    public class Util{
        private Util(){
            throw new Error();
        }
    }
    

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

    5.1 反例一

    String s = new String("hello");
    

    代替

    String s = "hello";
    

    虽然看起来没太多区别,但是如果在for循环里面或者方法的多次调用里面,每次new一个对象会被性能造成很大的影响。

    5.2反例二

    有如下代码:

    public class Person{
        private final Date birthDate;
        //省略一些初始化
        //这是错误的写法
        public boolean isBabyBoomer(){
            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.gettTime();
            return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
        }
    }
    

    其实这段代码看着没什么问题,并且我们日常中也是这么写的,但是它在性能上是有很大的缺陷的。
    Date对象一旦计算出来之后就不会再变化了。
    isBabyBoomer()方法每次调用时,会新建一个Calender、一个TimeZone和两个Date实例,这是不必要也是浪费性能。
    推举使用下面的静态static代码块初始化:

    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.gettTime();
        }
    
        public boolean isBabyBoomer(){
            return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
        }
    }
    

    static修饰的代码块会在类初始化进行加载,并且只会加载一次,所以Calendar、TimeZone和Date也只会实例化一次,而不是每次调用方法的时候都进行开辟内存进行实例化。
    如果isBabyBoomer()没用到,其实没必要加载static代码块,使用延迟初始化。虽然性能会好很多,但是导致太复杂了,反而有点过于为了性能而优化了。

    5.3不要莫名的自动拆装箱

    6.消除过期的对象引用

    • 只要类是自己管理内存,程序员就应该警惕内存泄漏问题
    • 内存泄漏的另一个常见来源是缓存
    • 内存泄漏的第三个常见来源是监听器和其他回调,确保回调立即当作垃圾回收的最佳方法是使用弱引用

    7.不要使用终结方法

    尽量不要使用 System.gc 和 System.runFinalization 这种终结方法,他们确实增加了终结方法被执行的机会,但是他们并不保证终极方法一定会被执行。
    终结方法有一个很严重的性能损失。
    但是显示的终结方法除外,try...finally...

    相关文章

      网友评论

        本文标题:《Effective Java》第二章:创建和销毁对象

        本文链接:https://www.haomeiwen.com/subject/trjyfftx.html