“不让工具类能够实例化”的做法是适合的

工具类往往没有对象层面的域(非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来修饰它显然是不合适的。

posted @ 2017-07-10 15:40  Deolin  阅读(766)  评论(0编辑  收藏  举报