如果你没有一颗最求完善的心,得过且过,请远离设计模式。
如果你不知道设计原则,请远离设计模式。
如果你为了学习设计模式而学习设计模式,又不是太需要设计模式,请远离设计模式。
单例模式:
保证一个类仅有一个实例,并提供一个访问他的全局访问点
单例意味着什么?----崇高与孤独
?--什么样的对象适合用单例?
|--一个对象足以完成任务时,没有必要去创建多个对象
|--一个对象非常消耗资源,需要限制对象的创建
比如:世界,你不会让别人随便去new,如何不让外部无法创建本类对象,
如何给外部提供唯一的对象,是单例模式的核心,要达到这两点很简单。
一、单例的四种形式--形式上的一切都仅是开始而已
1.终极孤独--饿汉
饿汉式.png即便无人问津,我也永远存在
public class World {
private static World sWorld = new World();
//[1]私有化构造
private World() {
initWorld();//初始化世界
System.out.println("世界已创建");
}
private void initWorld() {
}
//[2]返回内部静态实例
public static World getInstance() {
return sWorld;
}
}
2.有你相伴--懒汉双检锁
懒汉双检锁.png虽然我们不是同类,但感谢有你相伴
public class World {
private static volatile World sWorld;
//[1]私有化构造
private World() {
initWorld();//初始化世界
System.out.println("世界已创建");
}
private void initWorld() {
}
//[2]返回内部静态实例
public static World getInstance() {
if (sWorld == null) {//判断非空后--执行
synchronized (World.class) {//加锁,保证多线程下的单例
if (sWorld == null) {//非空,创建实例
sWorld = new World();
}
}
}
return sWorld;
}
}
3.有你相伴--静态内部类
静态内部类.png和上面的功能基本一致,所以我喜欢这个
public class World {
//[1]私有化构造
private World() {
initWorld();//初始化世界
System.out.println("世界已创建");
}
private void initWorld() {
}
//[3]返回内部静态实例
public static World getInstance() {
return WorldHolder.sWorld;
}
//[2]创建内部类创建实例
private static class WorldHolder {
private static final World sWorld = new World();
}
}
4.至简--枚举
枚举.pngpublic enum World {
INSTANCE;
World() {
initWorld();//初始化世界
System.out.println("世界已创建");
}
private void initWorld() {
}
}
二、单例下的序列化与反射
单例的价值在于一个程序中只用一个该对象实例
如果有恶意份子通过反射创建了另一个世界会怎么样?
1.单例的测试(以静态内部类版为例)
单例测试.png通过debug看出两次获取的都是同一个世界,这就是单一实例
public class God {
public static void main(String[] args) {
World world1 = World.getInstance();
World world2 = World.getInstance();
}
}
2.通过反射创建实例
反射测试.png可见
world3
的内存地址已经不一样了,说明出现了第二个世界,也就是单例的失效
不过应该没有人吃饱了没事干用反射创建单例对象吧,天堂有路你不走...
public class God {
public static void main(String[] args) {
World world1 = World.getInstance();
World world2 = World.getInstance();
//通过反射创建
Class<World> worldClass = World.class;
try {
Constructor<World> constructor = worldClass.getDeclaredConstructor(null);
constructor.setAccessible(true);
World world3 = constructor.newInstance();
System.out.println(world3==world2);//false
System.out.println(world1==world2);//true
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.通过反序列化创建对象
反序列化.png如果你的单例类有序列化的需求(如,单例对象本地存储,单例对象网络传输)
反序列化形成的实例也并非原来的实例
---->[World]-------------
public class World implements Serializable {
---->[God]-------------
public class God {
public static void main(String[] args) {
World world1 = World.getInstance();
World world2 = World.getInstance();
//通过反射创建
Class<World> worldClass = World.class;
try {
Constructor<World> constructor = worldClass.getDeclaredConstructor(null);
constructor.setAccessible(true);
World world3 = constructor.newInstance();
System.out.println(world3 == world2);//false
System.out.println(world1 == world2);//true
} catch (Exception e) {
e.printStackTrace();
}
//通过反序列化创建对象
try {
//序列化输出
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("world.obj"));
oos.writeObject(world1);
//反序列化创建对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("world.obj"));
World world4 = (World) ois.readObject();
ois.close();
System.out.println(world1 == world4);//false
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.发序列化的解决方案
反序列化的防治.png通过反序列化时的钩子函数:
readResolve
来控制序列化对象实例
---->[World]-------------
//解决反序列化创建实例的问题,readResolve创建的对象会直接替换io流读取的对象
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
三、结尾小述
1.优缺点
优点:
设计者严格管控这个类,全局作用。简化使用者使用
只创建一个实例,避免频繁创建销毁对象,节省内存资源,减少系统开销
缺点:
没有抽象层,不利于扩展
职责过重,违背单一职责原则
2.常见的单例
java.util.Calendar 标准单例,通过Calendar.getInstance方法获取对象
java.lang.System 完全单例,不提供外部构造方法,全部以静态方法提供服务
android.view.LayoutInflater 标准单例 ,通过LayoutInflater.from(Context)方法获取对象
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 附录 |
---|---|---|
V0.1--github | 2018-2-10 | 无 |
发布名:
设计模式1:创建型-单例模式
捷文链接:https://www.jianshu.com/p/6c1f3e355ba9
2.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 |
我的github:https://github.com/toly1994328
我的简书:https://www.jianshu.com/u/e4e52c116681
我的掘金:https://juejin.im/user/5b42c0656fb9a04fe727eb37
个人网站:http://www.toly1994.com
3.声明
icon_wx_200.png1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持
网友评论