考虑静态工厂方法而不是构造函数!
考虑静态工厂方法而不是构造函数!
月伴飞鱼用静态工厂方法替代构造器
静态工厂方法具有名称,更具有可读性,尤其是构造器比较多的类。
通过静态工厂方法创建对象时,可以不必每次创建对象,使用预先构建好的实例,例如
Boolean
类。
- 工厂方法可以返回原类型的任何子类,这相比构造器更加灵活。
1 | public static Boolean valueOf(boolean b) { |
遇到多个构造器参数时考虑使用构建器
静态工厂方法和构造器都不能很好的扩展到大量参数。
对于大量参数的场景,一种方法是使用重叠构造器的方式:
第一个构造器只有少量必要参数,第二个构造器除了必要参数,还添加一些可选参数,以此类推。
- 重叠构造器在参数量可控的时候还好,随着参数增多,构造器方法也会爆炸式增多,变得难以维护。
另一种办法是
Java Beans
模式,通过无参构造器构造对象,调用setter
方法传递参数。
- 但是这种方法会导致对象在构建过程中处于不一致状态,而且把类做成不可变的可能不复存在。
构建器模式通过让调用方传递必要参数调用
Builder
类的构造器得到builder
对象。
- 然后调用可选参数的
setter
方法设置可选参数,最后调用build
方法生成不可变的最终对象。
1 | public final class NutritionFacts { |
所有的字段都是
final
的,并且没有提供任何setter
方法,对象一旦创建,其状态就不能再改变。
Builder
类用于构建NutritionFacts
对象,Builder
类的构造函数接受必填属性。
Builder
类提供了链式调用的方法来设置可选属性。最后,通过
build
方法创建NutritionFacts
对象。
用私有构造器或者枚举类型强化Singleton属性
Singleton
是指:仅仅需要被实例化一次的对象,通常用来代表没有状态的对象。通过定义私有构造器,提供公有静态域或者工厂方法来获取单例对象。
私有构造方法可以保证调用方无法通过构造器获取对象,只能获取创建好的单例对象。
- 为防止通过反射机制调用私有构造器,可以在第二次创建对象时抛出异常。
另一种实现单例的方法是:声明一个包含单个元素的枚举类型。
- 这种方法提供了序列化机制,不用考虑单例对象在序列化和反序列化时需要做的额外工作。
1 | public enum Singleton { |
通过私有构造器强化不可实例的能力
一些工具类只是用来提供一些静态变量或者静态的工具方法,不希望被实例化。
- 因为实例化没有意义。
但是在缺少显式声明的构造器时,编译器会自动提供一个无参的构造器,还是能被实例化和继承。
- 正确的做法是提供一个私有的构造器,让这种类不能被继承,也不能被实例化。
1 | public final class Arrays { |
1 | public class Collections { |
避免创建不必要的对象
下面这段代码,将变量
sum
声明为Long
类型以后,每次在做加法操作时:
- 会先将
int
类型的i转变为Long
类型的实例,这将导致构建大量的Long
实例。要优先使用基本类型而不是对应的装箱类,防止无意识的自动装箱。
1 | private static long sun() { |
try-with-resources优先于try-finally
使用
try-finally
来关闭资源存在一些问题。比如在
try
块和finally
块中都抛出异常,try
块中抛出的异常会被finally
块中抛出的异常完全抹除。
- 在异常堆栈轨迹中完全没有
try
块中的异常记录。使用
try-with-resources
可以解决这个问题。资源必须实现
java.lang.AutoCloseable
接口,这样才能在try-with-resources
语句中使用。
- 在
try
块结束时,所有声明的资源都会自动关闭。
1 | public class TryWithResourcesExample { |
在
try
语句的括号内声明资源,如果是多个资源,使用分号分割。在
try
块中使用声明的资源,如果在try
块中或资源关闭时发生IOException
,会捕获并处理异常。如果处理资源或者关闭资源都发生了异常,后一个异常会被禁止,保留第一个异常,禁止的异常会被打印到堆栈轨迹中。
- 也可以通过
java.lang.Throwable#getSuppressed
获取。