1、单例模式 定义:指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。属于创建型模式。 饿汉式单例被反射、序列化破坏 懒汉式单例被反射、序列化破坏 容器式单例被反射、序列化破坏 枚举式单例不会被反射、序列化破坏 在静态块中有map存放map。put(name,枚举值),反序列化也是通过这个拿到对象,所以不会被序列化破坏 枚举单例内部不让通过反射来创建对象Constructor。newInstance(Constructor。java:417)会直接抛出异常1。1饿汉式单例 优点:执行效率高,性能高,没有任何的锁 缺点:某些情况下回造成内存浪费。(类的数量没法控制的时候,类在加载的时候就初始化了,即使用不到也会被初始化,就会造成内存的浪费)。 代码实现:publicclassHungrySingleton{privatestaticfinalHungrySingletonsingletonnewHungrySingleton();privateHungrySingleton(){}publicstaticHungrySingletongetSingleton(){returnsingleton;}}1。2懒汉式单例 优点:节省内存 缺点:线程不安全,如果多个线程同时调用创建单例的方法,会创建多个对象 代码实现:以下代码已加上synchronized关键字,可以保证线程安全publicclassLazySimpleSingleton{privatestaticLazySimpleSingletonsingletonnull;privateLazySimpleSingleton(){}publicsynchronizedstaticLazySimpleSingletongetSingleton(){if(singletonnull){singletonnewLazySimpleSingleton();}returnsingleton;}}1。3双重检索单例 优点:性能高了,线程安全了 缺点:可读性难度大,代码不优雅 代码实现:publicclassLazyDoubleCheckSingleton{这里添加volatile关键字可以避免指令重排序造成的线程紊乱问题privatevolatilestaticLazyDoubleCheckSingletonsingletonnull;privateLazyDoubleCheckSingleton(){}publicLazyDoubleCheckSingletongetSingleton(){检查是否要阻塞if(singletonnull){synchronized(LazyDoubleCheckSingleton。class){检查是否为空if(singletonnull){singletonnewLazyDoubleCheckSingleton();这里会发生指令重排序}}}returnsingleton;}} 指令重排序:1。开辟堆空间2。对开辟的空间进行初始化3。将内存空间的地址赋值给变量(instance)这三步走完才是创建完一个对象。如果不加volatile关键字,可能这三步就会错乱,导致指令重排序。1。4静态内部类单例 优点:利用了JAVA本身的语法特点,性能高,避免了内存浪费 缺点:能够本反射破坏 代码实现:publicclassLazyStaticInnerClassSingleton{privateLazyStaticInnerClassSingleton(){避免反射破坏if(InnerClassSingleton。innerClassSingleton!null){thrownewRuntimeException(不能非法访问);}}publicstaticLazyStaticInnerClassSingletongetSingleton(){returnInnerClassSingleton。innerClassSingleton;}在这里使用的时候才会调用静态内部类InnerClassSingleton。innerClassSingleton;privatestaticclassInnerClassSingleton{privatestaticfinalLazyStaticInnerClassSingletoninnerClassSingletonnewLazyStaticInnerClassSingleton();}}1。5枚举单例 优点:最优雅的写法,提供性能,防止反射破坏单例,防止线程破坏单例,被官方推荐。 缺点:不能大面积创建对象,容器造成内存浪费,序列化会破坏单例。 代码实现:publicenumEnumSingleton{INSTANCE;privateObjectdata;publicstaticEnumSingletongetInstance(){returnINSTANCE;}publicObjectgetData(){returndata;}publicvoidsetData(Objectdata){this。datadata;}} 枚举单例内部不让通过反射来创建对象,Constructor。newInstance(Constructor。java:417)会直接抛出异常1。6容器式单例 优点:解决了大面积创建对象的问题,不会造成内存浪费,是对枚举单例的优化,是springioc使用的单例方式。 缺点:线程不安全 代码实现: 以下代码中已加入双重检索,从而可以避免线程安全的问题。publicclassContainerSingleton{privatestaticMapString,ObjectcontainerMapnewConcurrentHashMapString,Object();privateContainerSingleton(){}publicstaticObjectgetInstance(StringclassName){Objectinstancenull;if(!containerMap。containsKey(className)){synchronized(ContainerSingleton。class){if(!containerMap。containsKey(className)){try{Classlt;?aClassClass。forName(className);instanceaClass。newInstance();containerMap。put(className,instance);}catch(Exceptione){e。printStackTrace();}}}returninstance;}else{returncontainerMap。get(className);}}}1。7序列化单例 优点:避免序列化破坏单例 缺点: 代码实现:publicclassSerializableSingleton{privatestaticfinalSerializableSingletonsingletonnewSerializableSingleton();privateSerializableSingleton(){}publicstaticSerializableSingletongetSingleton(){returnsingleton;}加入此方法可以解决被序列化破坏的单例具体要看一下ObjectInputStream中的readObject()方法下的readObject0方法中的checkResolve方法中的readOrdinaryObject方法if(obj!nullhandles。lookupException(passHandle)nulldesc。hasReadResolveMethod()在这个方法处理之后就不会再重新创建了就直接返回singleton对象了privateObjectreadResolve(){returnsingleton;}} 反序列化是不走构造函数的,是直接使用字节码重组的1。8ThreadLocal单例 在同一个线程下单例,不同的线程下会创建不同的对象 代码实现:publicclassThreadLocalSingleton{privatestaticfinalThreadLocalThreadLocalSingletonthreadLocalSingletonnewThreadLocalThreadLocalSingleton(){OverrideprotectedThreadLocalSingletoninitialValue(){returnnewThreadLocalSingleton();}};privateThreadLocalSingleton(){}publicstaticThreadLocalSingletongetInstance(){returnthreadLocalSingleton。get();}}1。9单例模式学习重点私有构造器保存线程安全延迟加载防止序列化反序列化破坏单例防止反射攻击单例1。10单例模式在源码中的应用 Spring中AbstractFactoryBean类下的getObject方法 Mybatis中ErrorContext ServletContext、ServletConfig、ApplicationContext、DBPool、BeanFactory、Runtime、Rpc2、工厂模式2。1简单工厂模式 定义:指由一个工厂对象决定创建出哪一种产品类的实例,属于创建型模式,但是不属于23中设计模式。 使用场景:工厂类负责创建的对象较少应用层只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。 源码中实现场景:Calendar。getInstance();LoggerFactory。getLogger(Stringname);LoggerFactory。getLogger(Classlt;?clazz); 代码实现: publicinterfaceICourse{voidrecord();}publicclassCourseImplimplementsICourse{Overridepublicvoidrecord(){System。out。println(正在学习简单工厂模式);}}publicclassJavaCourseimplementsICourse{Overridepublicvoidrecord(){System。out。println(正在学习JAVA课程);}}publicclassSimpleFactory{publicICoursecreate(Classclazz){try{if(clazz!null){return(ICourse)clazz。newInstance();}}catch(Exceptione){e。printStackTrace();}returnnull;}}2。2工厂方法模式 定义:是指定义一个创建对象的接口,让实现这个接口类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类中进行,属于创建型模式。 使用场景:创建对象需要大量重复的代码时建议使用应用层不依赖于产品类实例如何被创建、实现等细节,一个类通过其子类来指定创建哪个对象。 源码中实现场景:LoggerFactory。getLogger(); 代码实现: publicinterfaceICourse{voidrecord();}publicclassJavaCourseimplementsICourse{Overridepublicvoidrecord(){System。out。println(正在学习JAVA工厂方法模式);}}publicclassPythonCourseimplementsICourse{Overridepublicvoidrecord(){System。out。println(正在学习Python工厂方法模式);}}publicinterfaceICourseFactory{ICoursecreate();}publicclassJavaCourseFactoryimplementsICourseFactory{OverridepublicICoursecreate(){returnnewJavaCourse();}}publicclassPythonCourseFactoryimplementsICourseFactory{OverridepublicICoursecreate(){returnnewPythonCourse();}}publicclassMethodFactoryMain{publicstaticvoidmain(String〔〕args){ICourseFactoryfactorynewJavaCourseFactory();ICourseiCoursefactory。create();iCourse。record();}}2。3抽象工厂模式 定义:指提供创建一系列相关或相互依赖对象的接口,无序指定它们具体的类,属于创建型模式。 使用场景:应用层不依赖于产品类实例如何被创建、实现等细节强调一系列相关产品对象一起使用创建对象需要大量重复代码提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体实现。 源码中实现场景:spring中DefaultListableBeanFactory类 代码实现: publicinterfaceICourse{voidrecord();}publicinterfaceINote{voidwrite();}publicinterfaceIVideo{voidvideo();}publicinterfaceICourseFactory{INotecreateNote();ICoursecreateCource();IVideocreateVideo();}publicclassJavaCourseimplementsICourse{Overridepublicvoidrecord(){System。out。println(course正在学习JAVA抽象工厂模式);}}publicclassJavaNoteimplementsINote{Overridepublicvoidwrite(){System。out。println(note正在学习抽象工厂方法模式);}}publicclassJavaVideoimplementsIVideo{Overridepublicvoidvideo(){System。out。println(video正在学习抽象工厂模式);}}publicclassJavaCourseFactoryimplementsICourseFactory{OverridepublicINotecreateNote(){returnnewJavaNote();}OverridepublicICoursecreateCource(){returnnewJavaCourse();}OverridepublicIVideocreateVideo(){returnnewJavaVideo();}}publicclassPythonCourseimplementsICourse{Overridepublicvoidrecord(){System。out。println(PythonCourse正在学习抽象工厂模式);}}publicclassPythonNoteimplementsINote{Overridepublicvoidwrite(){System。out。println(python正在学习抽象工厂方法);}}publicclassPythonVideoimplementsIVideo{Overridepublicvoidvideo(){System。out。println(PythonVideo正在学习抽象工厂模式);}}publicclassPythonFactoryimplementsICourseFactory{OverridepublicINotecreateNote(){returnnewPythonNote();}OverridepublicICoursecreateCource(){returnnewPythonCourse();}OverridepublicIVideocreateVideo(){returnnewPythonVideo();}}publicclassAbstractFactoryMain{publicstaticvoidmain(String〔〕args){ICourseFactoryfactorynewJavaCourseFactory();factory。createCource()。record();factory。createNote()。write();factory。createVideo()。video();ICourseFactorypythonFactorynewPythonFactory();pythonFactory。createCource()。record();pythonFactory。createNote()。write();pythonFactory。createVideo()。video();}}3、代理模式 指为其他对象提供一种代理,以控制对这个对象的访问,代理对象和目标对象之间起到中介作用,属于结构性模式。3。1静态代理模式 代码实现: publicinterfaceIPerson{voidfindFriend();}publicclassUncleimplementsIPerson{privateXiaoPengYouxiaoPengYou;publicUncle(XiaoPengYouxiaoPengYou){this。xiaoPengYouxiaoPengYou;}OverridepublicvoidfindFriend(){System。out。println(小朋友找到了叔叔);xiaoPengYou。findFriend();System。out。println(叔叔帮助小朋友找到了小伙伴);}}publicclassXiaoPengYouimplementsIPerson{OverridepublicvoidfindFriend(){System。out。println(小朋友想要找个小伙伴);}}publicclassStaticProxyMain{publicstaticvoidmain(String〔〕args){UncleunclenewUncle(newXiaoPengYou());uncle。findFriend();}}3。2动态代理模式 spring中的代理选择原则:当Bean有实现接口时,Spring就会用jdk动态代理当Bean没有实现接口时,Spring就会用cglib动态代理Spring可以通过配置强制使用cglib,只需要Spring配置文件中添加以下代码 Cglib动态代理代理和JDK动态代理区别Cglib是继承的方式,覆盖父类的方法,JDK采用的是实现的方式,必须要求代理的目标对象一定要实现一个接口,通过生成字节码重组成一个新的类。JDKProxy对于用户而言,依赖更强,调用也更复杂。Cglib对目标类没有任何要求Cglib效率更高,性能也更高,底层没有用到反射。JDKProxy生成逻辑较为简单,执行效率低,每次都要用到反射。Cglib目标代理类不能有final修饰的方法,忽略final修饰的方法。3。2。1jdk动态代理 实现原理:动态生成源码。java文件Java文件输出到磁盘,保存为文件Proxy0。java把。java文件编译成Proxy0。class文件把生成的。class文件加载到JVM中返回新的代理对象 代码实现: publicinterfaceIPerson{voidstudy();}publicclassStudentimplementsIPerson{Overridepublicvoidstudy(){System。out。println(学生想学习动态代理);}}publicclassTeacherimplementsIPerson{Overridepublicvoidstudy(){System。out。println(老师想学习动态代理);}}publicclassProxyClientimplementsInvocationHandler{privateObjecttarget;publicObjectgetTarget(Objecttarget){this。targettarget;Classlt;?clazztarget。getClass();类加载器Objectthis代表当前类(就是实现InvocationHandler这个接口的类)returnProxy。newProxyInstance(clazz。getClassLoader(),clazz。getInterfaces(),this);}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{Objectinvokemethod。invoke(this。target,args);returninvoke;}}publicclassDynamicProxyMain{publicstaticvoidmain(String〔〕args){ProxyClientproxyClientnewProxyClient();IPersontarget(IPerson)proxyClient。getTarget(newStudent());target。study();IPersontarget1(IPerson)proxyClient。getTarget(newTeacher());target1。study();jdk代理再被jdk代理start使用同一个InvocationHandlerIPersontarget2(IPerson)proxyClient。getTarget(newStudent());target2。study();IPersontarget3(IPerson)proxyClient。getTarget(target2);target3。study();jdk代理再被jdk代理endjdk代理再被jdk代理start使用不同的InvocationHandlerProxyClientproxyClient1newProxyClient();IPersontarget4(IPerson)proxyClient。getTarget(newStudent());target4。study();IPersontarget5(IPerson)proxyClient1。getTarget(target4);target5。study();jdk代理再被jdk代理end}}3。2。2cglib动态代理 实现原理:实现MethodInterceptor接口,重写intercept方法 代码实现: publicinterfaceIPerson{voidstudy();}publicclassStudent{publicvoidstudy(){System。out。println(学生想学习动态代理);}}publicclassTeacher{publicvoidstudy(){System。out。println(老师想学习动态代理);}}publicclassCgLibProxyimplementsMethodInterceptor{publicObjectgetInstance(Classclazz){cglib创建代理对象的类库EnhancerenhancernewEnhancer();enhancer。setSuperclass(clazz);enhancer。setCallback(this);returnenhancer。create();}OverridepublicObjectintercept(Objecto,Methodmethod,Object〔〕objects,MethodProxymethodProxy)throwsThrowable{ObjectresultmethodProxy。invokeSuper(o,objects);returnresult;}}publicclassCglibProxyMain{publicstaticvoidmain(String〔〕args){CgLibProxycgLibProxynewCgLibProxy();Studentinstance(Student)cgLibProxy。getInstance(Student。class);instance。study();Teacherinstance1(Teacher)cgLibProxy。getInstance(Teacher。class);instance1。study();}}3。3动态代理相关问题3。3。1我们的代理类还能再被代理吗? 1。jdk代理后的类再次被jdk代理? jdk不能再次被jdk代理,因为会造成handler调用死循环。如果使用不同的实现InvocationHandler的代理类是可以的,mybatis中的多重插件就是这么做的 2。jdk代理后的类再次被cglib代理? jdk生成的代理类是final,cglib生成的代理类是要去继承目标类的,final修饰的类不能被继承,所以不能被代理 3。cglib代理后的类再次被cglib代理? 会报方法名重复DuplicatemethodnamenewInstancewithsignature 4。cglib代理后的类再次被jdk代理? 类的签名已经改变,没有目标方法了 总结,只有jdk代理能再次被jdk代理,必须使用不同的InvocationHandler,其他都不可以3。3。2什么样类不能被代理? 1。对于jdk,必须有接口实现 2。final修飾的方法不能被cglib代理 3。方法不是public的3。3。3接口能够被代理吗? 接口能被代理,比如mybatis中的dao接口就是这样做4、模板方法模式 定义:又叫模板模式,是指定义一个算法的骨架,并允许子类为其中的一个或者多个步骤提供实现。模板方法使得子类不改变算法结构的情况下,重新定义算法的某些步骤。属于行为型设计模式 使用场景:一次性实现算法不变的部门,将可变的部分留给子类去实现。各子类中公共的行为被提取出来并集中到一个公共的父类中,避免代码重复。 源码中实现场景:jdbcTemplateAbstractList中的get方法HttpServlet中的doGet方法和doPost方法mybatis中的BaseExecutor下的doQuery方法 优点 1。利用模板模式将相同的处理逻辑代码放到抽象父类中,可以提高代码复用性 2。将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性 3。把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合代开闭原则。 缺点 1。类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类数量增加,间接的增加了系统实现的复杂度 2。继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要修改一遍 代码实现:publicclassJavaCourseextendsAbstractCoursr{OverridepublicbooleanneedCheckHomework(){returntrue;}OverrideprotectedvoidcheckHomework(){System。out。println(检查JAVA作业);}}publicabstractclassAbstractCoursr{publicvoidcreateCourse(){1。发布预习资料postPrepareData();2。制作PPTmakePPT();3。直播上课videoInClass();4。上传课堂笔记uploadNote();5。布置作业assignHomework();if(needCheckHomework()){6。检查作业checkHomework();}}publicabstractbooleanneedCheckHomework();protectedabstractvoidcheckHomework();protectedvoidassignHomework(){System。out。println(布置作业);}protectedvoiduploadNote(){System。out。println(上传课堂笔记);}protectedvoidvideoInClass(){System。out。println(直播上课);}protectedvoidmakePPT(){System。out。println(制作PPT);}protectedvoidpostPrepareData(){System。out。println(发布预习资料);}}publicclassTemplateMain{publicstaticvoidmain(String〔〕args){JavaCoursejavaCoursenewJavaCourse();javaCourse。createCourse();}}5、装饰器模式 定义:也叫包装模式,是指在不改变原对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案。属于结构型模式。 使用场景:用户扩展一个类的功能,或给一个类添加附加职责。动态的给一个对象添加功能,这些功能可以再动态的撤销。 源码中的实现:InputStreamBufferedInputStream就是实际使用到的装饰器模式BufferedReaderFileReadermybatis中的Cache的实现就是装饰器,同时也是委派模式 优点 1。装饰器是继承的有力补充,比继承灵活,不改变原有的对象的情况下,动态的给一个对象扩展功能 2。通过使用不同装饰器类以及这些装饰类的排列组合,可实现不同效果 3。装饰器完全遵守开闭原则 缺点 1。会出现更多的代码,更多的类,增加程序复杂性 2。动态装饰时,多层装饰会更复杂。 装饰器模式和代理模式对比 1。装饰器模式是一种特殊的代理模式 2。装饰器模式强调自身的功能扩展 3。代理模式强调的是代理过程的控制 代码实现: publicabstractclassBatterCake{publicabstractStringname();publicabstractDoublegetPrice();}publicclassBaseBatterCakeextendsBatterCake{OverridepublicStringname(){return煎饼;}OverridepublicDoublegetPrice(){return5D;}}publicclassDecoratorextendsBatterCake{privateBatterCakebatterCake;publicDecorator(BatterCakebatterCake){this。batterCakebatterCake;}OverridepublicStringname(){returnthis。batterCake。name();}OverridepublicDoublegetPrice(){returnthis。batterCake。getPrice();}}publicclassEggDecoratorextendsDecorator{publicEggDecorator(BatterCakebatterCake){super(batterCake);}OverridepublicDoublegetPrice(){returnsuper。getPrice()1;}OverridepublicStringname(){returnsuper。name()加1个鸡蛋;}}publicclassSuguaDecoratorextendsDecorator{publicSuguaDecorator(BatterCakebatterCake){super(batterCake);}OverridepublicStringname(){returnsuper。name()加1根肠;}OverridepublicDoublegetPrice(){returnsuper。getPrice()2;}}publicclassDecoratorMain{publicstaticvoidmain(String〔〕args){BatterCakebatterCake;batterCakenewBaseBatterCake();batterCakenewEggDecorator(batterCake);batterCakenewSuguaDecorator(batterCake);System。out。println(batterCake。name()batterCake。getPrice());}}6、策略模式 定义:又叫政策模式,将定义的算法家族分别封装起来,让它们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户。,可以避免多重分支的ifelse和switch语句。属于行为型模式。 使用场景:假如系统中有很多类,而他们的区别仅仅在于它们的行为不同。一个系统需要动态的在不同的算法中选择一种。需要屏蔽算法规则。 源码中的实现:Arrays中publicstaticvoidparallelSort(T〔〕a,intfromIndex,inttoIndex,Comparatorlt;?superTcmp)TreeMap中的compareSpring中的Resource 优点: 符合开闭原则 避免使用多重条件转移语句ifelseswitch 提供算法的安全性和保密性 缺点: 客户端必须知道所有的策略,并且自行决定使用哪一个策略类 代码中产生非常多的策略类,增加维护难度 代码实现: publicinterfaceIStradegy{voidalgorithm();}publicclassContext{privateIStradegyiStradegy;publicContext(IStradegyiStradegy){this。iStradegyiStradegy;}publicvoidalgorithm(){this。iStradegy。algorithm();}}publicclassConcreteStrategyAimplementsIStradegy{Overridepublicvoidalgorithm(){System。out。println(ConcreteStrategyA);}}publicclassConcreteStrategyBimplementsIStradegy{Overridepublicvoidalgorithm(){System。out。println(ConcreteStrategyB);}}publicclassStradegyMain{publicstaticvoidmain(String〔〕args){ContextcontextnewContext(newConcreteStrategyA());context。algorithm();}}7、委派模式 定义:又叫委托模式,基本的作用就是负责任务的调度和任务的分配,将任务的分配和执行分离开来,可以看做是一种特殊情况下的静态代理的全权代理。不属于GOF23种设计模式之一。属于行为型模式。 源码中实现:JVM中的双亲委派一层一层的往上去找类publicabstractclassClassLoader方法protectedClasslt;?loadClass(Stringname,booleanresolve)反射中的类publicfinalclassMethod方法publicObjectinvoke(Objectobj,Object。。。args)spring中BeanDefinitionBeanDefinitionParserDelegateDispatcherServlet中的doDispatch(); 委派模式和代理模式区别 委派模式是行为型模式,代理模式是结构型模式 委派模式注重的是任务派遣,注重结果,代理模式注重的是代码增强,注重过程。 委派模式是一种特殊的静态代理,相当于全权代理。 代码实现: publicinterfaceIEmployee{voiddoing(Stringtask);}publicclassBoss{publicvoidcommond(Stringtask,Leaderleader){leader。doing(task);}}publicclassEmployeeAimplementsIEmployee{Overridepublicvoiddoing(Stringtask){System。out。println(员工A开始做事);}}publicclassEmployeeBimplementsIEmployee{Overridepublicvoiddoing(Stringtask){System。out。println(员工B开始做事);}}publicclassLeaderimplementsIEmployee{privateMapString,IEmployeemapnewConcurrentHashMap();publicLeader(){map。put(爬虫,newEmployeeA());map。put(管理,newEmployeeB());}Overridepublicvoiddoing(Stringtask){if(!map。containsKey(task)){System。out。println(超出能力范围);}else{map。get(task)。doing(task);}if(爬虫。equals(task)){newEmployeeA()。doing(task);}elseif(管理。equals(task)){newEmployeeB()。doing(task);}else{System。out。println(不符合用人规范);}}}publicclassDelegateMain{publicstaticvoidmain(String〔〕args){BossbossnewBoss();boss。commond(爬虫,newLeader());}}