美文网首页
Effective Java 读书笔记(1)

Effective Java 读书笔记(1)

作者: Lin_Shao | 来源:发表于2017-08-28 14:33 被阅读22次

    创建和销毁对象


    第一条:考虑用静态工厂方法代替构造器

    当该类需要返回不同的实例(子类实例),或者唯一一个实例(单例)时,应考虑用静态工厂方法代替构造器。
    静态工厂方法的一些惯用名称:

    • valueOf 返回的实例与参数有相同的值,相当于类型转换
    • of valueOf的简化
    • getInstance 返回的实例是通过参数类描述的,但一般不具有与参数同样的值。对于Singleton来说,无参数,返回唯一一个实例。
    • newInstance 类似于getInstance,但每次返回新的实例
    • getType 在工厂方法处于不同的类时使用,Type指返回类型的类型
    • newType 同上

    第二条:遇到多个构造器参数时要考虑用构建器

    当创建对象面临多个可选参数时(即,构造器有多个可选的参数),有以下三种方案:

    • 重叠构造器模式
      根据参数的不同组合,编写多个签名不同的构造器或静态工厂方法。优点:线程安全,参数组合少时简单;缺点:当参数量很大,且其组合很多时,代码编写麻烦且可读性差
    • JavaBean模式
      调用一个构造器类创建对象并设置必要的参数,再通过setter方法类设置每个可选参数。优点:实例简单,代码简洁,可扩展性强。缺点:构造过程被分为多个调用方法,难以保证一致性,(多线程)易出错难调试;无法实例化一个不可变对象(致命)
    • Builder模式
      不直接生成想要的对象,而是先用必要的参数实例化一个builder,再用setter方法设置每个可选参数,最后调用无参的build方法来生成不可变的对象。既能保证重叠构造器模式的安全性,也能保证像JavaBean一样简单可读

    第三条:用私有构造器或者枚举类型强化Singleton属性

    实现Singleton有以下三种方法:

    • 将构造器私有化,将实例设为公有的静态final成员。私有构造器只被调用一次,用来实例化单例:
    public class FirstSingleton {
        public String message;
        public static final FirstSingleton INSTANCE = new FirstSingleton();
    
        private FirstSingleton(){
            message = "Hello World! First Singleton!";
        }
    }
    

    由于缺少公有的或者受保护的构造器,所以保证了单例的全局唯一性。

    注意:享有特权的客户端可以借助AccessibleObject.setAccessibe方法,通过反射机制调用私有构造器。如果需要抵御这种攻击,可以修改私有构造器,使其在构造第二个实例时抛出异常。

    • 公有的成员是个静态工厂方法:
    public class SecondSingleton {
        public String message;
        private static final SecondSingleton INSTANCE  = new SecondSingleton();
        private SecondSingleton(){
            message = "Hello World! Second Singleton!";
        }
        public static SecondSingleton getInstance(){
            return INSTANCE;
        }
    }
    

    对于静态方法getInstance的所有调用,都会返回同一个对象引用(上述注意依然使用)。
    公有域方法的主要好处在于,组成类的成员的声明很清楚第表明了这个类是一个Singleton:公有的静态域是final的,所以该域将总是包含相同的对象引用。

    对于以上两种方法,在序列化时,仅仅在声明中加上implements Serializable是不够的。为了维护并保证Singleton,必须声明所有的实例域都是瞬时的(transient)的,并提供一个readResove实例。否则,每次反序列化一个实例时,都会创建一个新的实例:

        private Object readResolve(){
            return INSTANCE;
        }
    
    • 编写一个包含单个元素的枚举类型:
        public enum ThirdSingleton {
        INSTANCE;
        public String message = "Hello World! Third Singleton!";
    }
    

    这种方法在功能上与公有域方法相近,但是其更加简洁,无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击时。单元素的枚举类型已经成为实现Singleton的最佳方法。


    第四条:通过私有化构造器强化不可实例的能力

    有时候,我们需要编写只包含静态方法和静态域的类。这些工具类不希望被实例化,实例对它没有任何意义。这个时候,我们需要私有化构造器,防止其在无意间被实例化。


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

    一般来说,最好能重用对象而不是每次需要 的时候就创建一个相同功能的新对象。

    • 重用不可变对象:如果对象是不可变的,它就始终可以被重用(单例模式、String对象池)
    • 重用已知不会被修改的可变对象。创建某些对象的成本是十分昂贵的,且其在创建之后,其值一般不会改变,此时我们可以考虑重用这个对象已减少创建成本。
    • 使用适配器(适配器是指这样一个对象:它把功能委托给一个后备对象,从而为后备对象提供一个可以替代的接口)。如Map类的KeySet方法,它返回一个Set对象。对于一个Map而言,它的key是可变的,但是keySet对象是唯一的,只是其内容会发生变化。
    • 优先使用基本类型而不是自动装箱基本类型,当心无意识的自动装箱。

    第六条:消除过期的对象引用

    在支持垃圾回收的语言中,内存泄漏是很隐蔽的,称之为“无意识的对象保持(unintentional object retention)“更为恰当。如果一个对象被无意识保留起来,那么垃圾回收机制不仅不会去处理这个对象,而且不会处理这个对象所引用的所有其他对象,久而久之会对性能造成潜在的重大影响。

    这种问题一般出现在堆、栈、数组、链表等数据结果在pop对象时没有消除引用,如size--;
    这种问题的解决方法很简单,一旦对象过期,清空这些引用即可stack[size--] = null;

    内存泄漏的常见来源:

    • 类自己管理内存。当我们需要写自己管理内存 的类时,如手动实现一个stack,应谨记一旦元素不需要用到,要立即释放(消除引用),以便垃圾回收机制能及时回收。
    • 缓存。缓存中的对象极易被遗忘。-> 使用WeakHashMap代表缓存
    • 监听器及其他回调。如果你实现了一个API,客户端在这个API中注册回调,却没有显式取消注册,那么除非你采取某些动作,否则它们就会被积聚。确保回调立即被当做立即回收的最佳方法是只保存它们的弱引用。

    第七条:避免使用终结方法

    终结方法(finalizer)通常是不可预测的,也是很危险的。
    终结方法通常在垃圾回收机制回收对象时负责执行,其执行时间取决于jvm设计、配置以及程序执行过程。而且,java规范并不保证终结方法一定会被执行。还有一点,使用终结方法会带来严重的性能损耗。
    若一个对象需要在其终结时进行某些动作,可以考虑使用显式终结动作,并要求所有客户端在结束该实例时调用该方法,例如InputStream接口的close方法
    终结方法的合法用途:

    • 充当显式终结方法的“安全网(safety net)。
    • 终结非关键的本地资源(一般指本地对等体native peer),即本地对象。

    相关文章

      网友评论

          本文标题:Effective Java 读书笔记(1)

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