java的设计模式 - 单例模式
程序员文章站
2022-05-17 12:49:53
java 面试中单例模式基本都是必考的,都有最推荐的方式,也不知道问来干嘛。下面记录一下 ......
java 面试中单例模式基本都是必考的,都有最推荐的方式,也不知道问来干嘛。下面记录一下
饿汉式(也不知道为何叫这个名字)
public class singleton { private static final singleton instance = new singleton(); private singleton(){} public static singleton getinstance(){ return instance; } }
其实真心觉得没什么问题
-
private singleton
来修饰可以防止创建多个实例 - 没有延迟加载?这是需求不同好吗!有很多的需求是希望一开始就加载好的,不希望要用的时候再加载的,比如是
java.lang.runtime
但面试的时候,你不能这样回答,面试官不开心的,你要回答
- 没有延迟加载
- 譬如 singleton 实例的创建是依赖参数或者配置文件的,在 getinstance() 之前必须调用某个方法设置参数给它,是不能使用。(在构造函数上传参不行吗?,不考虑延迟加载)
懒汉模式
public class singleton { private volatile static singleton instance; //声明成 volatile private singleton (){} public static singleton getsingleton() { if (instance == null) { synchronized (singleton.class) { if (instance == null) { instance = new singleton(); } } } return instance; } }
你看加个延迟加载
- 要将 instance 声明个 volatile 好让在多线程的环境下可见
- 又要注意在多线程的情况下要主要加锁,因为会出现一个线程认为是空要构造对象,而另一个对象也认为是空要构造对象是情况
麻烦!!!
但也有应用场景的,在资源占用很多,又不常用的情况下,可以考虑用懒汉模式
懒汉式二式 —— 内部静态类
public class singleton { private final static class singleholder { private static final singleton instance = new singleton(); } private singleton() { } public static singleton getinstance() { return singleholder.instance; } }
因为这个是 jvm 保证其线性安全性的,而且会在加载的时候创建,又不依赖版本,所以以前会比较推荐。
致命的危险
其实上面的东东都有致命的危险!反射
上面按正常人类的做法是不会产生多个实例,如果会产生多个实例是说明上面的方式或多或少不够完美 ,比如反射
@test public void test() throws classnotfoundexception, illegalaccessexception, instantiationexception, nosuchmethodexception, invocationtargetexception { constructor<?> cls = singleton.class.getdeclaredconstructor(); cls.setaccessible(true); singleton singleton = (singleton) cls.newinstance(); assertnotequals(singleton,singleton.getinstance());//明显这两个对象是不一样的!!! }
也就是说上面的方式面对复杂的序列化或者反射攻击,可能会出现问题!(当然要先实现 serializable 接口)
比如:
@test public void test() throws classnotfoundexception, ioexception { //序列化对象到文件 objectoutputstream objectoutputstream = new objectoutputstream(new fileoutputstream("singleton")); objectoutputstream.writeobject(singleton.getinstance()); //从文件中读取对象 file file = new file("singleton"); objectinputstream objectinputstream = new objectinputstream(new fileinputstream(file)); singleton singleton = (singleton) objectinputstream.readobject(); //判断是否是同一个对象 assertnotequals(singleton,singleton.getinstance()); }
当然也有应对序列化的方式。
就是在类中添加readresolve
函数,因为反序列化中,如果存在这个函数,序列化的结果就是这函数的值。
所以你在要序列化中的类添加这句就可以了。
private object readresolve() { return getinstance(); }
但面对反射就确实无力了。
最好的方式 - 枚举类
public enum singletonenum { instance; }
- 面对反射:jvm 直接禁止了通过反射构造枚举实例的行为!
- 面对序列化:jvm 对 enum 类的序列化,不是调用 writeobject、readobject 这些方法的。结果是就算是序列化后再反序列化,结果都是一样。
以上
上一篇: 商标注册流程费用,商标注册需要哪些资料
下一篇: 《设计模式之禅》读书小结