前言:
此刻兄弟们对“设计模式综合项目实战”都比较注意,我们都想要知道一些“设计模式综合项目实战”的相关资讯。那么小编在网上收集了一些有关“设计模式综合项目实战””的相关文章,希望咱们能喜欢,朋友们一起来学习一下吧!其实一开始工作的时候,作为一个菜鸡程序员,在日常的膜拜大佬的过程中,往往会听到大佬们说过什么设计模式啊,什么什么原则啊,然后在自己的工作中也想要学习学习,马克一下,但是在日常的curd中,基本上是没有多少能够用到设计模式的地步,所以一直对于这个概念比较抽象,但是随着时间的变化,总感觉自己在开发上面有很些不对劲了,感觉自己写的东西说不出来的烂,于是拿起了我曾经抛下的设计模式,准备和自己的日常代码结合下,于是写个日志,记录下自己学习的过程。
前言:因为自己真算不上什么大佬,而且我自己看的其实也不能算太多,所以以下内容可能会有些想当然的内容,如果有什么问题希望各位大佬能帮忙指出,谢谢。
单例模式
先贴上菜鸟教程的介绍:单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
说白了就是单例模式只允许一个类存在一个实例,当然了,这个概念比较简单,但是在实践中,其实我们往往会不知道具体的使用方式,所以再正式使用这个单例模式之前,我们先考虑下一个问题,再哪里需要使用单例模式呢?很显然,使用这个单例模式就是为了减少重复的创建和销毁一个类的对象,那么反向考虑下,什么类不需要重复的创建和销毁呢?结论只有一个,那就是:全局使用的类。当然了,这个具体什么是全局使用的类,就要看各自的业务逻辑,以及相关的需求了,比方说项目中可能存在某些全局配置的类啊,某些用于记录日志,有关于临界资源这些需要进行全局统一管理的类啊,往往在使用这些全局的类,我们可能就要考虑使用单例模式进行相应的配置了。这边介绍几个应用上的例子,如:在web中我们可能会存在多个地方处理同一文件,对于一些web项目中需要的计数功能,需要生成一些需要唯一编号,并且编号是按照一定规则生成的。这些地方都可以使用单例模式。
单例的实现方式
单例模式的实现算的上是多种多样了,当然各种方案也有着自己不同的优缺点,这里先简单介绍下。
饿汉式
非常简单的一个实现单例方式。主要借助了java中classloader机制实现了多线程中的单例,但是因为在类加载的时候就初始化了对象,所以对于内存有所浪费,并且如果长期不使用这个单例的情况下,这个方式也会产生垃圾对象。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }懒汉式
最基本的实现单例方式,简单,但是有很大的问题,不支持多线程,因此我个人感觉这个严格上来说不能算是单例了,毕竟满容易出现多个实例的。当然因为是最基本的方案,可以在之后的方案在懒汉式上做了很多的优化。
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { // 主要就是这里如果判断初始化的时候存在问题,容易出现多个实例 if (instance == null) { instance = new Singleton(); } return instance; } }懒汉式(线程安全模式)
懒汉式的升级版,说白了就是直接把懒汉式给加上了同步,用于解决线程不安全的情况。但是,加上了一个同步锁,基本上把获取单例对象的方法性能给延迟了,基本上等同把多线程改成了串行执行的方案。
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }双检锁
这个方案主要是针对于懒汉式(线程安全模式)的一个升级,对于整个方法加锁,很明显会很影响性能,于是我们在判断是否存在单例之后,在加锁,然后再在加锁的代码块内部加上一个判空,再来进行初始化的工作。这个方案明显性能上比懒汉式(线程安全模式)好的,但是因为jvm的指令重排机制,所以可能会出现空指针的异常。
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { // 将同步锁放在了这里 synchronized (Singleton.class){ if(null==instance) instance=new Singleton(); } } return instance; } }双检锁(volatile模式)
主要是针对于jdk1.5之后的volatile关键字的,这个关键字的具体内容和jvm内存模型有关,这里就不详细说了,单纯的说就是他能解决双检锁的指令重排问题,其他的和双检锁方案差不多。
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }静态内部类
这种方式主要是使用了静态内部类的延迟加载初始化方案。与饿汉式一样利用了 classloader 机制来保证初始化 instance 时只有一个线程这方式来控制同步,不过不一样的地方就是静态内部类不会和外部类一起被加载,只有当调用了外部类的getSingleton()方法时,才会装载内部类,从而延迟生成了单例。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }枚举类
个人觉得最简洁的实现单例的一个方案,并且这个方案解决了通过反射和反序列化生成多个实例的方式。主要用到的就是jdk1.5提供的枚举类来实现单例。
public enum Singleton { INSTANCE; public void whateverMethod() { } }
以上就是单例模式的7种实现方案了,具体的使用还是需要看各自的业务情况进行使用,毕竟我们才是决定代码的那个人啊。
附录反射和反序列化破解单例的方案(来源网上)
反射破解
public class BreakSingleton{ public static void main(String[] args) throw Exception{ Class clazz = Class.forName("Singleton"); Constructor c = clazz.getDeclaredConstructor(null); c.setAccessible(true); Singleton s1 = c.newInstance(); Singleton s2 = c.newInstance(); //通过反射,得到的两个不同对象 System.out.println(s1); System.out.println(s2); }}
如何避免以上的漏洞:
class Singleton{ private static final Singleton singleton = new Singleton(); private Singleton() { //在构造器中加个逻辑判断,多次调用抛出异常 if(instance!= null){ throw new RuntimeException() } } public static Singleton getInstance(){ return singleton; }}
反序列化
public class BreakSingleton{ public static void main(String[] args) throws Exception{ //先根据单例模式创建对象(单例模式所以s1,s2是一样的) Singleton s1=Singleton.getInstance(); Singleton s2=Singleton.getInstance();//将s1写入本地某个路径 FileOutputStream fos=new FileOutputStream("本地某个路径下文件"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(s1); oos.close(); fos.close();//从本地某个路径读取写入的对象 ObjectInputStream ois=new ObjectInputStream(new FileInputStream("和上面的本地参数路径相同")); Singleton s3=(Singleton) ois.readObject(); System.out.println(s1); System.out.println(s2); System.out.println(s3);//s3是一个新对象} }
如何避免实现序列化单例模式的漏洞:
class Singleton implements Serializable{ private static final Singleton singleton = new Singleton(); private Singleton() { } public static Singleton getInstance(){ return singleton; }//反序列化定义该方法,则不需要创建新对象 private Object readResolve() throws ObjectStreamException{ return singleton; }}
标签: #设计模式综合项目实战