工具类往往没有对象层面的域(非static域),
那么每个工具类的对象除了内存地址不同以外,是完全一样的
并且,工具类里定义的全是static方法,
那么为了调用这些方法,仅仅用类名调用就可以了
结合以上两点,工具类没有必要实例化,也就没有必要向客户端提供实例化的解决方案。
最先想到的应该是将显式的将构造方法私有化
class OneUtil { private OneUtil() {} public static void method() { // Do something. } }
这样一来,客户端的“new OneUtil();”被阻止了,客户端的开发者可能会去寻找valueOf()、getInstance()之类的方法。当他们找不到后,他们才会觉得OneUtil是个工具类。
但是,仅仅如此OneUtil还是能够通过反射来产生实例。
public class OneUtilTest { public static void main(String[] args) { OneUtil oneUtil = null; // oneUtil = new OneUtil(); try { oneUtil = (OneUtil) Class.forName("OneUtil").newInstance(); } catch (InstantiationException e) { } catch (IllegalAccessException e) { } catch (ClassNotFoundException e) { } oneUtil.method(); } }
更进一步的方法是在构造方法里追加异常,保证异常能在运行期尽可能早的发生,
RuntimeException的派生异常类,可以满足这个需求。
class OneUtil { private OneUtil() { throw new UnsupportedOperationException("This class should not be instantiated."); } public static void method() { System.out.println("Do something."); } }
用恶意的方法测试一下
public class OneUtilTest { public static void main(String[] args) throws Exception { OneUtil oneUtil = null; // oneUtil = new OneUtil(); Constructor<OneUtil> constructor=OneUtil.class.getDeclaredConstructor(); constructor.setAccessible(true); oneUtil = constructor.newInstance(); oneUtil.method(); } }
Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at io.deolin.OneUtilTest.main(OneUtilTest.java:22) Caused by: java.lang.UnsupportedOperationException: This class should not be instantiated. at io.deolin.OneUtil.<init>(OneUtilTest.java:7) ... 5 more
其实用abstract来修饰工具类也能达到类似的效果,
但是,abstract的作用更像是对外声明“这个类无法实例化,是用来被继承的”
工具类作为一个过程化的反oop的东西,用abstract来修饰它显然是不合适的。