Effective Java 读书笔记(一):创建和销毁对象

Effective Java 读书笔记(一):创建和销毁对象

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

静态工厂方法相比构造器,有何优势?

  1. 它们有名称:如果构造器参数本身不能描述正被返回的对象,那么具有适当名称的静态工厂方法会更容易使用,产生的客户端代码也更易于阅读。
  2. 不必在每次调用产生新的对象:起到缓存的作用,方便复用。比如 Bookean 的 valueOf 方法,类似于 Flyweight 模式。
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
  1. 可以返回原返回类型的任何子类型对象,提供更大的灵活性。这项技术适合基于接口的框架,用户知道,被返回的对象由相关接口精确指定,所以不需要阅读具体类的文档。

那么有什么缺点吗?

  1. 静态工厂方法代替构造器,构造器变为 private 的话,该类就不能子类化了。
  2. 静态工厂方法与其他静态方法没啥区别,如果文档没有明确标识,就不容易被发现。这一点可以在方法命名方面遵循一些惯例来解决:
    • valueOf / of:该方法返回的实例与参数有相同的值,实际上做的是类型转化的事儿。
    • getInstance:比较一般化的命名,如无特殊需要就用它了。
    • newInstance:跟 getInstance 类似,但是 newInstance 确保每次返回的都是新的实例。
    • getType / newType:静态工厂方法位于不同类的时候使用。

构造器参数较多时考虑用 Builder 构建

Builder 模式有何优点?

  1. 可以模拟具名的可选参数,代码更易读。
  2. 可以做更复杂的事情,比如参数校验。

Builder 模式有何缺点呢?需要写更多代码,在十分注重性能的场合,可能不是一个好的选择。

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

私有构造器实现单例代码如下:

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

这种方法有一个缺点:如果加上 implements Serializable 提供 Java 序列化功能,那么在反序列化会生成新的实例,要解决这个问题的话,可以在其中加一个 readResolve 方法:

public class Singleton implements Serializable{
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() { }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    private Object readResolve()
            throws java.io.ObjectStreamException {
        return INSTANCE;
    }
}

注:readResolve 是用于反序列化的,还有一个用于序列化的方法 writeReplace。序列化的时候,
首先 JVM 会先调用 writeReplace 方法,在这个阶段,我们可以进行张冠李戴,将需要进行序列化的对象换成我们指定的对象。

另一个更简单的提供单例模式的方法是使用枚举,枚举天然提供序列化机制,而且绝对能防止多次实例化。

public enum SingletonEnum {
    INSTANCE;

    public void justAFunction() {}
}

在私有构造器里抛异常来强化不可实例化的能力

这通常用于只想提供静态方法的工具类中:

public class Tool {
    private Tool() {
        throw new AssertionError();
    }
}

避免创建不必要的对象

  1. 要优先使用基本类型,而非装箱基本类型,要当心无意识的自动装箱。
  2. 如果对象是不可变的,它就可以始终被重用,比如 String:
// 错误做法:浪费性能
String a = new String("hello");
// 正确做法
String a = "hello";
  1. 优先使用静态工厂方法而非构造器,比如 Boolean.valueOf。

消除过期的引用

可以加速内存释放,减少内存泄漏,比如 Stack 对象中的 pop 方法,移除一个对象时,除了修改栈顶指针,还会把数组对应位置置 null。

public class Stack<E> extends Vector<E> {
    public synchronized E pop() {
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }
    public synchronized void removeElementAt(int index) {
        // 省略一些代码
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }       
}   

清空引用应该是一种例外,而非规范。消除过期引用的最好方法是让包含该引用的变量结束其生命周期。如果你在最紧凑的作用域范围内定义每一个变量,这种情形就会自然而然的发生。那么何时应该清空引用呢?一般而言,只要类是自己管理内存,程序员就应该警惕内存泄露问题。

避免使用终结方法 finalize

  1. Java 语言规范并不保证终结方法会被及时执行,甚至不保证他们会被执行。
  2. 性能很差。

参考文献

  1. Effective Java 第二版
  2. 关于 Java 对象序列化您不知道的 5 件事
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值