饿汉模式:
package instance;
public class SingleTon {
// 饿汉模式,构造方法必须私有,所有的单例构造方法必须私有,才能保证构造方法只有本类能够使用
private SingleTon(){
System.out.println("构造方法执行了,创建单例对象");
}
// 私有构造内部可以调用,创建实例
private static final SingleTon instance = new SingleTon();
// 获取实例方法
public static SingleTon getInstance() {
return instance;
}
}
测试类
package instance;
public class TestSingleTon {
public static void main(String[] args) {
System.out.println(SingleTon.getInstance());
System.out.println(SingleTon.getInstance());
}
}
输出结果:
类加载的时候初始化一次,初始化的时候通过new SingleTon()就生成了单例对象,多次创建单例地址一样,说明验证成功,多次创建单例对象并不是每次调用每次生成,而是直接用初始化好的单例对象
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
格外注意:单例是可以被破坏的,可以通过反射、反序列化破坏,下面演示代码
通过反射破坏单例:
public class TestSingleTon {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
System.out.println(SingleTon.getInstance());
System.out.println(SingleTon.getInstance());
fanshe(SingleTon.class);
}
// 通过反射破坏单例模式
public static void fanshe(Class<?> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
System.out.println("反射创建单例:" + constructor.newInstance());
}
}
输出结果可以看到,通过反射拿到构造方法创建出来的单例对象地址已经发生了变化,单例已经被破坏了
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
构造方法执行了,创建单例对象
反射创建单例:instance.SingleTon@42110406
那么如何避免单例模式被破坏?
只需要在构造方法加一个非null判断抛出异常即可
private SingleTon(){
if (instance!=null){
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("构造方法执行了,创建单例对象");
}
重新运行代码,打印堆栈信息如下,通过反射创建单例的时候,就抛出了异常
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
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 instance.TestSingleTon.fanshe(TestSingleTon.java:18)
at instance.TestSingleTon.main(TestSingleTon.java:12)
Caused by: java.lang.RuntimeException: 单例对象不能重复创建
at instance.SingleTon.<init>(SingleTon.java:7)
... 6 more
通过反序列化破坏单例
// 首先让类实现以下序列化接口
public class SingleTon implements Serializable{}
测试类:
// 调用反序列化方法
fanxuliehua(SingleTon.getInstance());
// 定义反序列化方法
private static void fanxuliehua(Object instance) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
System.out.println("反序列化创建对象:"+ois.readObject());
}
// 对象地址不一样,说明单例被破坏
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
反序列化创建对象:instance.SingleTon@ea30797
反序列化破坏单例解决办法,类中添加如下方法,
public Object readResolve(){
return instance;
}
输出结果:
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
反序列化创建对象:instance.SingleTon@67424e82
通过unsafe破坏单例:
// unsafe破坏单例,unsafe为jdk里面的类
private static void unsafe(Class<?> clazz) throws InstantiationException {
Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
System.out.println("unsafe创建实例:"+o);
}
构造方法执行了,创建单例对象
instance.SingleTon@67424e82
instance.SingleTon@67424e82
unsafe创建实例:instance.SingleTon@5387f9e0
unsafe破坏单例暂无解决方法
精品推荐
发布者:admin,如若转载,请注明出处:https://ai1024.vip/42878.html