如何编写工具类的代码
在开发 过程中我们经常需要开发一些工具类,来简化我们的开发。如ID生成、日期处理等。工具类的编码需要遵循一定的规范,具体如下:
- 不可被实例化
- 所有方法均为静态方法
- 不可被继承
- 工具类的命名一般以
Utils
结尾 (建议)
以上 3 个原则,至少要遵循前两个,即 1 和 2。一些开源框架中的工具类都遵循 1 和 2,或者全部遵循。
不可被实例化
在Java中,工具类通常被设计为不能被实例化。原因是为了避免实例化工具类,因为工具类一般所有方法均为静态方法,是完全没有必要实例化之后再用的。即便有的工具类中有非静态方法,也应该不能被实例化,而是提供一个方法用户获取其实例,确保此工具类在系统中是单例的。
以下是几种实现:
1. 使用 abstract
关键字
使用 abstract
可以将工具类定义为一个抽象类,以此来阻止工具类的实例化。在 Spring 框架中常用这种方式。但是这种方式无法阻止类被继承。
package org.springframework.beans;
public abstract class BeanUtils {
}
2. 使用私有构造函数
建议使用私有构造函数来阻止类被实例化。如果类中全部是静态方法,使用私有函数可以阻止类被实例化。而如果类中是非静态方法,使用私有函数,再提供一个获取类的方法,可以确保工具类的在整个应用中只有一个实例存在。
以下是 org.apache.commons.collections4.CollectionUtils
中通过私有构造函数阻止类的实例化的代码:
package org.apache.commons.collections4;
public class CollectionUtils {
/**
* CollectionUtils should not normally be instantiated.
*/
private CollectionUtils() {}
}
再如:我们将系统中的配置放到了 sys.properties
文件中,然后提供了一个工具类 SysConfig
来获取系统的配置。这时候我们可以将此类设置为非静态方法的工具类。如下:
@Slf4j
public class SysConfig {
/**
* 单例
*/
private static final SysConfig INSTANCE = new SysConfig();
private final Properties properties;
/**
* 私有构造函数,阻止类的实例化,类的实例必须通过 getInstance 方法获取
*/
private SysConfig() {
this.properties = new Properties();
try {
ClassPathResource classPathResource = new ClassPathResource("sys.properties");
properties.load(classPathResource.getInputStream());
} catch (IOException e) {
log.error("配置文件加载失败", e);
}
}
public static SysConfig getInstance() {
return INSTANCE;
}
public String get(String key) {
return this.properties.getProperty(key);
}
// 测试工具类
public static void main(String[] args) {
System.out.println(SysConfig.getInstance().get("a"));
}
}
这样做的好处是,如果有多个配置文件,可以在此工具类中对配置文件的实例进行缓存。当只有一个配置文件时,通过静态代码块来实现初始化也是可行的,如下:
@Slf4j
public class SysConfig2 {
private static Properties properties = new Properties();
// 在静态代码块中初始化 Properties
static {
ClassPathResource classPathResource = new ClassPathResource("sys.properties");
try {
properties.load(classPathResource.getInputStream());
} catch (IOException e) {
log.error("配置文件加载失败", e);
}
}
private SysConfig2() {}
public static String get(String key) {
return properties.getProperty(key);
}
public static void main(String[] args) {
System.out.println(SysConfig2.get("a"));
}
}
所有方法均为静态方法
这里建议工具类内所有方法均为静态方法,如非必要,一般写非静态方法的工具类,但是切忌工具类中既有静态方法又有非静态方法。
不可被继承
不建议通过继承的方式来扩展工具类的功能。静态方法可以被继承,但是无法被重写。这一点是需要尤为注意的。
public class A {
protected A() {
}
public static void hello() {
System.out.println("A");
}
}
public class B extends A {
protected B() {
}
public static void hello() {
System.out.println("B");
}
public static void main(String[] args) {
B.hello();
}
}
// 输出:B
当工具类中只有一个私有的构造函数,这时候这个工具类是不能被继承的。如下:
public class A {
private A() {
}
}
public class B extends A {
}
这时候会报一个编译错误 There is no parameterless constructor available in 'config.A'
。
还有一个阻止类继承方法是使用 final
关键字,此关键字与 abstract
冲突,不可联用。使用 final
关键字可以显式的标明此类不可被继承。
如果我们的工具类内只有一个私有构造函数,那这个工具类就是无法继承的。这也是为什么建议使用 私有构造函数 来阻止类被实例化,因为使用 私有构造函数 即可阻止类被实例化,又可阻止类被继承,一箭双雕。
总结
建议的方式是
- 工具类内全部为静态方法
- 使用私有构造函数来阻止类被实例化和被继承。
以下是一个范例
public class LocaDateTimeUtils {
private LocaDateTimeUtils() {}
public static LocalDateTime of(String dateStr) {
// ...
}
public static String format(LocalDateTime dateTime) {
// ...
}
}