单例模式很简单,基本有点开发经验或者学习经验的人都可以写出来,通过私有构造,返回INSTANCE,代码如下:
public class Singleton {
// 私有,静态的类自身实例
private static Singleton INSTANCE = new Singleton();
// 私有的构造子(构造器,构造函数,构造方法)
private Singleton() {}
// 公开,静态的工厂方法
public static Singleton getInstance() {
return INSTANCE;
}
public void sayHello(){
System.out.println("Hello,我是单例的方法哦!!");
}
}
没错,很简单,通过公开静态方法获取实例,构造方法定义为私有了,也无法实例化,代码如下
public static void main(String[] args) {
// 通过公开静态方法getInstance()获取
System.out.println(Singleton.getInstance());
Singleton.getInstance().sayHello();
}
运行结果:
com.mengli.bean.Singleton@5fb7a531
Hello,我是单例的方法哦!!
重点来了,Java反射是可以绕过Java机制调用私有构造的,所以我这样写:
public static void main(String[] args) {
// 通过公开静态方法getInstance()获取
System.out.println(Singleton.getInstance());
Singleton.getInstance().sayHello();
try {
// 通过反射操作
Class claxx = Class.forName("com.mengli.bean.Singleton");
Constructor<Singleton> constructor = claxx.getDeclaredConstructor();
constructor.setAccessible(true);
Object object = constructor.newInstance();
Singleton singleton = (Singleton)object;
System.out.println(singleton);
singleton.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
}
运行结果:
com.mengli.bean.Singleton@5fb7a531Hello,我是单例的方法哦!!
com.mengli.bean.Singleton@11be650f
Hello,我是单例的方法哦!!
很明显,这个时候内存中有了两个实例了,这就完全不符合单例模式了,如何修改呢?在此之前呢,先实现一个自定义异常:
public class StudyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public StudyException(String message){
super(message);
}
}
重点修改的地方来了:
public class Singleton {
// 定义全局静态变量count
private static int count = 0;
// 私有,静态的类自身实例
private static Singleton INSTANCE = new Singleton();
// 私有的构造子(构造器,构造函数,构造方法)
private Singleton() {
if(count > 0){
throw new StudyException("该类只能有一个实例对象!");
}
count ++ ;
}
// 公开,静态的工厂方法
public static Singleton getInstance() {
return INSTANCE;
}
public void sayHello(){
System.out.println("Hello,我是单例的方法哦!!");
}
}
如上代码所示,我加入了一个全局的count,当该对象实例化的时候,对count进行自增操作,也就是在count大于等于1的时候呢,说明有代码在视图创建第二个实例,这个时候就可以果断抛出异常,捕获处理...
我们再次执行程序:
com.mengli.bean.Singleton@7a4014a0
Hello,我是单例的方法哦!!
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mengli.main.StudyMain.main(StudyMain.java:20)
Caused by: com.mengli.exception.StudyException: 该类只能有一个实例对象!
at com.mengli.bean.Singleton.<init>(Singleton.java:14)
... 5 more
从结果开来,防御成功了是吧!