pursue wind pursue wind
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
  • EffectiveJava

    • 考虑使用静态工厂方法替代构造方法
    • 当构造方法参数过多时使用builder模式
    • 使用私有构造方法或枚类实现Singleton属性
    • 使用私有构造方法执行非实例化
    • 依赖注入优于硬连接资源(hardwiring resources)
    • 避免创建不必要的对象
    • 消除过期的对象引用
    • 避免使用Finalizer和Cleaner机制
    • 使用try-with-resources语句替代try-finally语句
    • 重写equals方法时遵守通用约定
    • 重写equals方法时同时也要重写hashcode方法
    • 始终重写 toString 方法
    • 谨慎地重写 clone 方法
    • 考虑实现Comparable接口
    • 使类和成员的可访问性最小化
    • 在公共类中使用访问方法而不是公共属性
    • 最小化可变性
    • 组合优于继承
    • 要么设计继承并提供文档说明,要么禁用继承
    • 接口优于抽象类
    • 为后代设计接口
    • 接口仅用来定义类型
    • 类层次结构优于标签类
    • 支持使用静态成员类而不是非静态类
    • 将源文件限制为单个顶级类
    • 不要使用原始类型
    • 消除非检查警告
    • 列表优于数组
    • 优先考虑泛型
    • 优先使用泛型方法
    • 使用限定通配符来增加API的灵活性
    • 合理地结合泛型和可变参数
    • 优先考虑类型安全的异构容器
    • 使用枚举类型替代整型常量
    • 使用实例属性替代序数
    • 使用EnumSet替代位属性
    • 使用EnumMap替代序数索引
    • 使用接口模拟可扩展的枚举
    • 注解优于命名模式
    • 始终使用Override注解
    • 使用标记接口定义类型
    • lambda表达式优于匿名类
    • 方法引用优于lambda表达式
    • 优先使用标准的函数式接口
    • 明智审慎地使用Stream
    • 优先考虑流中无副作用的函数
    • 优先使用Collection而不是Stream来作为方法的返回类型
    • 谨慎使用流并行
    • 检查参数有效性
    • 必要时进行防御性拷贝
    • 仔细设计方法签名
    • 明智审慎地使用重载
    • 明智审慎地使用可变参数
    • 返回空的数组或集合,不要返回 null
    • 明智审慎地返回 Optional
    • 为所有已公开的 API 元素编写文档注释
    • 最小化局部变量的作用域
    • for-each 循环优于传统 for 循环
    • 了解并使用库
    • 若需要精确答案就应避免使用 float 和 double 类型
    • 基本数据类型优于包装类
    • 当使用其他类型更合适时应避免使用字符串
    • 当心字符串连接引起的性能问题
    • 通过接口引用对象
    • 接口优于反射
    • 明智审慎地本地方法
    • 明智审慎地进行优化
    • 遵守被广泛认可的命名约定
    • 只针对异常的情况下才使用异常
    • 对可恢复的情况使用受检异常,对编程错误使用运行时异常
    • 避免不必要的使用受检异常
    • 优先使用标准的异常
    • 抛出与抽象对应的异常
    • 每个方法抛出的异常都需要创建文档
    • 在细节消息中包含失败一捕获信息
    • 保持失败原子性
    • 不要忽略异常
    • 同步访问共享的可变数据
    • 避免过度同步
    • executor 、task 和 stream 优先于线程
    • 并发工具优于 wait 和 notify
    • 文档应包含线程安全属性
    • 明智审慎的使用延迟初始化
    • 不要依赖线程调度器
    • 优先选择 Java 序列化的替代方案
    • 非常谨慎地实现 Serializable
    • 考虑使用自定义的序列化形式
    • 保护性的编写 readObject 方法
    • 对于实例控制,枚举类型优于 readResolve
    • 考虑用序列化代理代替序列化实例
  • On Java 8

  • 书
  • EffectiveJava
pursuewind
2020-11-22

使用私有构造方法或枚类实现Singleton属性

# 3. 使用私有构造方法或枚类实现 Singleton 属性

单例是一个仅实例化一次的类[Gamma95]。单例对象通常表示无状态对象,如函数 (条目 24) 或一个本质上唯一的系统组件。让一个类成为单例会使测试它的客户变得困难,因为除非实现一个作为它类型的接口,否则不可能用一个模拟实现替代单例。

有两种常见的方法来实现单例。两者都基于保持构造方法私有和导出公共静态成员以提供对唯一实例的访问。在第一种方法中,成员是 final 修饰的属性:

// Singleton with public final field
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public void leaveTheBuilding() { ... }
}
1
2
3
4
5
6

私有构造方法只调用一次,来初始化公共静态 final Elvis.INSTANCE 属性。缺少一个公共的或受保护的构造方法,保证了全局的唯一性:一旦 Elvis 类被初始化,一个 Elvis 的实例就会存在——不多也不少。客户端所做的任何事情都不能改变这一点,但需要注意的是:特权客户端可以使用 AccessibleObject.setAccessible 方法,以反射方式调用私有构造方法(详见第 65 条)。如果需要防御此攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。

在第二个实现单例的方法中,公共成员是一个静态的工厂方法:

// Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { ... }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() { ... }
}
1
2
3
4
5
6
7
8

所有对 Elvis.getInstance 的调用都返回相同的对象引用,并且不会创建其他的 Elvis 实例(与前面提到的警告相同)。

公共属性方法的主要优点是 API 明确表示该类是一个单例:公共静态属性是 final 的,所以它总是包含相同的对象引用。 第二个好处是它更简单。

静态工厂方法的优势之一在于,它提供了灵活性:在不改变其 API 的前提下,我们可以改变该类是否应该为单例的想法。工厂方法返回该类的唯一实例,但是,它很容易被修改,比如,改为每个调用该方法的线程返回一个唯一的实例。 第二个好处是,如果你的应用程序需要它,可以编写一个泛型单例工厂(generic singleton factory )(详见第30 条)。 使用静态工厂的最后一个优点是,可以通过方法引用(method reference)作为提供者,例如 Elvis::instance 等同于 Supplier<Elvis>。 除非满足以上任意一种优势,否则还是优先考虑公有域(public-field)的方法。

为了将上述方法中实现的单例类变成是可序列化的 (第 12 章),仅仅将 implements Serializable 添加到声明中是不够的。为了保证单例模式不被破坏,必须声明所有的实例字段为 transient,并提供一个 readResolve 方法(详见第 89 条)。否则,每当序列化的实例被反序列化时,就会创建一个新的实例,在我们的例子中,导致出现新的 Elvis 实例。为了防止这种情况发生,将如下的 readResolve 方法添加到 Elvis 类:

// readResolve method to preserve singleton property
private Object readResolve() {
     // Return the one true Elvis and let the garbage collector
     // take care of the Elvis impersonator.
    return INSTANCE;
}
1
2
3
4
5
6

实现一个单例的第三种方法是声明单一元素的枚举类:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() { ... }
}
1
2
3
4
5
6

这种方式类似于公共属性方法,但更简洁,无偿地提供了序列化机制,并提供了防止多个实例化的坚固保证,即使是在复杂的序列化或反射攻击的情况下。这种方法可能感觉有点不自然,但是 单一元素枚举类通常是实现单例的最佳方式。注意,如果单例必须继承 Enum 以外的父类 (尽管可以声明一个 Enum 来实现接口),那么就不能使用这种方法。

Last Updated: 2023/01/30, 11:01:00
当构造方法参数过多时使用builder模式
使用私有构造方法执行非实例化

← 当构造方法参数过多时使用builder模式 使用私有构造方法执行非实例化→

Theme by Vdoing | Copyright © 2019-2023 pursue-wind | 粤ICP备2022093130号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
  • 飙升榜
  • 新歌榜
  • 云音乐民谣榜
  • 美国Billboard榜
  • UK排行榜周榜
  • 网络DJ