1 你所知道的设计模式有哪些?Java 中一般认为有 23 种设计模式,我们不需要所有的都市,可是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。
需要掌握的设计模式我单独列出来了,固然能掌握的越多越好。总体来说设计模式分为三大类:建立型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、制作者模式、原型模式。结构型模式,共7种:适配器模式、装饰器模式、署理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共11种:计谋模式、模板方法模式、视察者模式、迭代子模式、责任链模式、下令模式、备忘录模式、状态模式、会见者模式、中介者模式、解释器模式。2 单例设计模式?2.1单例模式界说单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在盘算机系统中,线程池、缓存、日志工具、对话框、打印机、显卡的驱动法式工具常被设计成单例。这些应用都或多或少具有资源治理器的功效。
每台盘算机可以有若干个打印机,但只能有一个Printer Spooler,以制止两个打印作业同时输出到打印机中。每台盘算机可以有若干通信端口,系统应当集中治理这些通信端口,以制止一个通信端口同时被两个请求同时挪用。总之,选择单例模式就是为了制止纷歧致状态。2.2 单例模式的特点● 单例类只能有一个实例。
● 单例类必须自己建立自己的唯一实例。● 单例类必须给所有其他工具提供这一实例。单例模式保证了全局工具的唯一性,好比系统启动读取设置文件就需要单例保证设置的一致性。
2.3 单例的四大原则● 结构器私有化● 以静态方法或者枚举返回实例● 确保实例只有一个,尤其是多线程情况● 确保反序列化时不会重新构建工具2.4 实现单例模式的方式(1)饿汉式(立刻加载):饿汉式单例在类加载初始化时就建立好一个静态的工具供外部使用,除非系统重启,这个工具不会改变,所以自己就是线程宁静的。Singleton通过将结构方法限定为private制止了类在外部被实例化,在同一个虚拟机规模内,Singleton的唯一实例只能通过getInstance()方法会见。(事实上,通过Java反射机制是能够实例化结构方法为private的类的,会使Java单例实现失效) package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 * * 饿汉式(立刻加载) */ public class Singleton1 {/** * 私有结构 */ private Singleton1() { System.out.println("结构函数Singleton1");}/** * 初始值为实例工具 */ private static Singleton1 single = new Singleton1();/** * 静态工厂方法 * @return 单例工具 */ public static Singleton1 getInstance() { System.out.println("getInstance");return single;} public static void main(String[] args){ System.out.println("初始化"); Singleton1 instance = Singleton1.getInstance();}}(2)懒汉式(延迟加载):该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程情况下会发生多个Singleton工具 package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 * * 懒汉式(延迟加载) */ public class Singleton2 {/** * 私有结构 */ private Singleton2() { System.out.println("结构函数Singleton2");}/** * 初始值为null */ private static Singleton2 single = null;/** * 静态工厂方法 * @return 单例工具 */ public static Singleton2 getInstance() {if(single == null){ System.out.println("getInstance"); single = new Singleton2();}return single;} public static void main(String[] args){ System.out.println("初始化"); Singleton2 instance = Singleton2.getInstance();}}(3)同步锁(解决线程宁静问题):在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例工具问题,可是该方式运行效率却很低下,下一个线程想要获取工具,就必须等候上一个线程释放锁之后,才可以继续运行。package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 * * 同步锁(解决线程宁静问题) */ public class Singleton3 {/** * 私有结构 */ private Singleton3() {}/** * 初始值为null */ private static Singleton3 single = null; public static Singleton3 getInstance() { // 等同于 synchronized public static Singleton3 getInstance()synchronized(Singleton3.class){ // 注意:内里的判断是一定要加的,否则泛起线程宁静问题if(single == null){ single = new Singleton3();}}return single;}}(4)双重检查锁(提高同步锁的效率):使用双重检查锁进一步做了优化,可以制止整个方法被锁,只对需要锁的代码部门加锁,可以提高执行效率。
package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 * 双重检查锁(提高同步锁的效率) */ public class Singleton4 {/** * 私有结构 */ private Singleton4() {}/** * 初始值为null */ private static Singleton4 single = null;/** * 双重检查锁 * @return 单例工具 */ public static Singleton4 getInstance() {if (single == null) {synchronized (Singleton4.class) {if (single == null) { single = new Singleton4();}}}return single;}}(5) 静态内部类:这种方式引入了一个内部静态类(static class),静态内部类只有在挪用时才会加载,它保证了Singleton 实例的延迟初始化,又保证了实例的唯一性。它把singleton 的实例化操作放到一个静态内部类中,在第一次挪用getInstance() 方法时,JVM才会去加载InnerObject类,同时初始化singleton 实例,所以能让getInstance() 方法线程宁静。
特点是:即能延迟加载,也能保证线程宁静。静态内部类虽然保证了单例在多线程并发下的线程宁静性,可是在遇到序列化工具时,默认的方式运行获得的效果就是多例的。package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 * * 静态内部类(延迟加载,线程宁静) */ public class Singleton5 {/** * 私有结构 */ private Singleton5() {}/** * 静态内部类 */ private static class InnerObject{ private static Singleton5 single = new Singleton5();} public static Singleton5 getInstance() {return InnerObject.single;}}(6)内部枚举类实现(防止反射攻击):事实上,通过Java反射机制是能够实例化结构方法为private的类的。
这也就是我们现在需要引入的枚举单例模式。package com.atguigu.interview.chapter02;/** * @author : atguigu.com * @since 2019/7/22 */ public class SingletonFactory {/** * 内部枚举类 */ private enum EnumSingleton{ Singleton; private Singleton6 singleton; //枚举类的结构方法在类加载是被实例化 private EnumSingleton(){ singleton = new Singleton6();} public Singleton6 getInstance(){return singleton;}} public static Singleton6 getInstance() {return EnumSingleton.Singleton.getInstance();}}class Singleton6 { public Singleton6(){}}3 工厂设计模式(Factory)3.1 什么是工厂设计模式?工厂设计模式,顾名思义,就是用来生产工具的,在java中,万物皆工具,这些工具都需要建立,如果建立的时候直接new该工具,就会对该工具耦合严重,如果我们要更换工具,所有new工具的地方都需要修改一遍,这显然违背了软件设计的开闭原则,如果我们使用工厂来生产工具,我们就只和工厂打交道就可以了,彻底和工具解耦,如果要更换工具,直接在工厂里更换该工具即可,到达了与工具解耦的目的;所以说,工厂模式最大的优点就是:解耦3.2 简朴工厂(Simple Factory)界说:一个工厂方法,依据传入的参数,生成对应的产物工具;角色:1、抽象产物2、详细产物3、详细工厂4、产物使用者使用说明:先将产物类抽象出来,好比,苹果和梨都属于水果,抽象出来一个水果类Fruit,苹果和梨就是详细的产物类,然后建立一个水果工厂,划分用来建立苹果和梨。
代码如下:水果接口:public interface Fruit {void whatIm();}苹果类:public class Apple implements Fruit { @Override public void whatIm() { System.out.println("苹果");}}梨类:public class Pear implements Fruit { @Override public void whatIm() { System.out.println("梨");}}水果工厂:public class FruitFactory { public Fruit createFruit(String type) {if (type.equals("apple")) {//生产苹果return new Apple();} else if (type.equals("pear")) {//生产梨return new Pear();}return null;}}使用工厂生产产物:public class FruitApp { public static void main(String[] args) { FruitFactory mFactory = new FruitFactory(); Apple apple = (Apple) mFactory.createFruit("apple");//获得苹果 Pear pear = (Pear) mFactory.createFruit("pear");//获得梨 apple.whatIm(); pear.whatIm();}}以上的这种方式,每当添加一种水果,就一定要修改工厂类,违反了开闭原则;所以简朴工厂只适合于产物工具较少,且产物牢固的需求,对于产物变化无常的需求来说显然不合适。3.3 工厂方法(Factory Method)界说:将工厂提取成一个接口或抽象类,详细生产什么产物由子类决议;角色:1、抽象产物2、详细产物3、抽象工厂4、详细工厂使用说明:和上例中一样,产物类抽象出来,这次我们把工厂类也抽象出来,生产什么样的产物由子类来决议。
代码如下:水果接口、苹果类和梨类:代码和上例一样抽象工厂接口:public interface FruitFactory { Fruit createFruit();//生产水果}苹果工厂:public class AppleFactory implements FruitFactory { @Override public Apple createFruit() {return new Apple();}}梨工厂:public class PearFactory implements FruitFactory { @Override public Pear createFruit() {return new Pear();}}使用工厂生产产物:public class FruitApp { public static void main(String[] args){ AppleFactory appleFactory = new AppleFactory(); PearFactory pearFactory = new PearFactory(); Apple apple = appleFactory.createFruit();//获得苹果 Pear pear = pearFactory.createFruit();//获得梨 apple.whatIm(); pear.whatIm();}}以上这种方式,虽然解耦了,也遵循了开闭原则,可是如果我需要的产物许多的话,需要建立很是多的工厂,所以这种方式的缺点也很显着。3.4 抽象工厂(Abstract Factory)界说:为建立一组相关或者是相互依赖的工具提供的一个接口,而不需要指定它们的详细类。角色:1、抽象产物2、详细产物3、抽象工厂4、详细工厂使用说明:抽象工厂和工厂方法的模式基本一样,区别在于,工厂方法是生产一个详细的产物,而抽象工厂可以用来生产一组相同,有相对关系的产物;重点在于一组,一批,一系列;举个例子,如果生产小米手机,小米手机有许多系列,小米note、红米note等;如果小米note生产需要的配件有825的处置惩罚器,6英寸屏幕,而红米只需要650的处置惩罚器和5寸的屏幕就可以了。
用抽象工厂来实现:cpu接口和实现类:public interface Cpu {void run();class Cpu650 implements Cpu { @Override public void run() { System.out.println("650 也厉害");}}class Cpu825 implements Cpu { @Override public void run() { System.out.println("825 更强劲");}}}屏幕接口和实现类:public interface Screen {void size();class Screen5 implements Screen { @Override public void size() { System.out.println("" +"5寸");}}class Screen6 implements Screen { @Override public void size() { System.out.println("6寸");}}}抽象工厂接口:public interface PhoneFactory { Cpu getCpu();//使用的cpu Screen getScreen();//使用的屏幕}小米手机工厂:public class XiaoMiFactory implements PhoneFactory { @Override public Cpu.Cpu825 getCpu() {return new Cpu.Cpu825();//高性能处置惩罚器} @Override public Screen.Screen6 getScreen() {return new Screen.Screen6();//6寸大屏}}红米手机工厂:public class HongMiFactory implements PhoneFactory { @Override public Cpu.Cpu650 getCpu() {return new Cpu.Cpu650();//高效处置惩罚器} @Override public Screen.Screen5 getScreen() {return new Screen.Screen5();//小屏手机}}使用工厂生产产物:public class PhoneApp { public static void main(String[] args){ HongMiFactory hongMiFactory = new HongMiFactory(); XiaoMiFactory xiaoMiFactory = new XiaoMiFactory(); Cpu.Cpu650 cpu650 = hongMiFactory.getCpu(); Cpu.Cpu825 cpu825 = xiaoMiFactory.getCpu(); cpu650.run(); cpu825.run(); Screen.Screen5 screen5 = hongMiFactory.getScreen(); Screen.Screen6 screen6 = xiaoMiFactory.getScreen(); screen5.size(); screen6.size();}}以上例子可以看出,抽象工厂可以解决一系列的产物生产的需求,对于大批量,多系列的产物,用抽象工厂可以更好地治理和扩展。3.5三种工厂方式总结1、对于简朴工厂和工厂方法来说,两者的使用方式实际上是一样的,如果对于产物的分类和名称是确定的,数量是相对牢固的,推荐使用简朴工厂模式;2、抽象工厂用来解决相对庞大的问题,适用于一系列、大批量的工具生产。4 署理模式(Proxy)4.1什么是署理模式?署理模式给某一个工具提供一个署理工具,并由署理工具控制对原工具的引用。通俗地来讲署理模式就是我们生活中常见的中介。
举个例子来说明:如果说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,可是这确实太浪费我得时间和精神了。我只是想买一辆车而已为什么我还要分外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我管理车辆过户流程,我只是卖力选择自己喜欢的车,然后付钱就可以了。
用图表现如下:4.2 为什么要用署理模式?中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托工具,而署理类工具可以在客户类和委托工具之间起到中介的作用,其特征是署理类和委托类实现相同的接口。开闭原则,增加功效:署理类除了是客户类和委托类的中介之外,我们还可以通过给署理类增加分外的功效来扩展委托类的功效,这样做我们只需要修改署理类而不需要再修改委托类,切合代码设计的开闭原则。署理类主要卖力为委托类预处置惩罚消息、过滤消息、把消息转发给委托类,以及事后对返回效果的处置惩罚等。署理类自己并不真正实现服务,而是通过挪用委托类的相关方法,来提供特定的服务。
真正的业务功效还是由委托类来实现,可是可以在业务功效执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功效,我们就可以使用署理类来完成,而没须要修改已经封装好的委托类。4.3 有哪几种署理模式?我们有多种差别的方式来实现署理。
如果根据署理建立的时期来举行分类的话,可以分为两种:静态署理、动态署理。● 静态署理是由法式员建立或特定工具自动生成源代码,再对其编译。
在法式员运行之前,署理类.class文件就已经被建立了。● 动态署理是在法式运行时通过反射机制动态建立的。4.4 静态署理(Static Proxy)第一步:建立服务类接口public interface BuyHouse {void buyHouse();}第二步:实现服务接口public class BuyHouseImpl implements BuyHouse { @Override public void buyHouse() { System.out.println("我要买房");}}第三步:建立署理类 public class BuyHouseProxy implements BuyHouse { private BuyHouse buyHouse; public BuyHouseProxy(final BuyHouse buyHouse) {this.buyHouse = buyHouse;} @Override public void buyHouse() { System.out.println("买房前准备"); buyHouse.buyHouse(); System.out.println("买房后装修");}}第四步:编写测试类public class HouseApp { public static void main(String[] args) { BuyHouse buyHouse = new BuyHouseImpl(); BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse); buyHouseProxy.buyHouse();}}静态署理总结:优点:可以做到在切合开闭原则的情况下对目的工具举行功效扩展。缺点:我们得为每一个服务建立署理类,事情量太大,不易治理。
同时接口一旦发生改变,署理类也得相应修改。4.5 JDK动态署理(Dynamic Proxy)在动态署理中我们不再需要再手动的建立署理类,我们只需要编写一个动态处置惩罚器就可以了。真正的署理工具由JDK在运行时为我们动态地来建立。
第一步:建立服务类接口代码和上例一样第二步:实现服务接口代码和上例一样第三步:编写动态处置惩罚器public class HouseApp { public static void main(String[] args) { BuyHouse buyHouse = new BuyHouseImpl(); BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse); buyHouseProxy.buyHouse();}}第四步:编写测试类public class HouseApp { public static void main(String[] args) { BuyHouse buyHouse = new BuyHouseImpl(); BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse); buyHouseProxy.buyHouse();}} Proxy是所有动态生成的署理的配合的父类,这个类有一个静态方法Proxy.newProxyInstance(),吸收三个参数:● ClassLoader loader:指定当前目的工具使用的类加载器,获取加载器的方法是牢固的● Class<?>[] interfaces:指定目的工具实现的接口的类型,使用泛型方式确认类型● InvocationHandler:指定动态处置惩罚器,执行目的工具的方法时,会触发事件处置惩罚器的方法JDK动态署理总结:优点:相对于静态署理,动态署理大大淘汰了开发任务,同时淘汰了对业务接口的依赖,降低了耦合度。缺点:Proxy是所有动态生成的署理的配合的父类,因此服务类必须是接口的形式,不能是普通类的形式,因为Java无法实现多继续。4.6 CGLib动态署理(CGLib Proxy)JDK实现动态署理需要实现类通过接口界说业务方法,对于没有接口的类,如何实现动态署理呢,这就需要CGLib了。CGLib接纳了底层的字节码技术,其原理是通过字节码技术为一个类建立子类,并在子类中接纳方法拦截的技术拦截所有父类方法的挪用,顺势织入横切逻辑。
但因为接纳的是继续,所以不能对final修饰的类举行署理。JDK动态署理与CGLib动态署理均是实现Spring AOP的基础。Cglib子类署理实现方法:(1)引入cglib的jar文件,asm的jar文件(2)署理的类不能为final(3)目的业务工具的方法如果为final/static,那么就不会被拦截,即不会执行目的工具分外的业务方法第一步:建立服务类public class BuyHouse2 { public void buyHouse() { System.out.println("我要买房");}}第二步:建立CGLIB署理类public class CglibProxy implements MethodInterceptor { private Object target; public CglibProxy(Object target) {this.target = target;}/** * 给目的工具建立一个署理工具 * @return 署理工具 */ public Object getProxyInstance() { //1.工具类 Enhancer enhancer = new Enhancer(); //2.设置父类 enhancer.setSuperclass(target.getClass()); //3.设置回调函数 enhancer.setCallback(this); //4.建立子类(署理工具)return enhancer.create();} public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); //执行目的工具的方法 Object result = method.invoke(target, args); System.out.println("买房后装修");return result;}}第三步:建立测试类public class HouseApp { public static void main(String[] args) { BuyHouse2 target = new BuyHouse2(); CglibProxy cglibProxy = new CglibProxy(target); BuyHouse2 buyHouseCglibProxy = (BuyHouse2) cglibProxy.getProxyInstance(); buyHouseCglibProxy.buyHouse();}}CGLib署理总结: CGLib建立的动态署理工具比JDK建立的动态署理工具的性能更高,可是CGLIB建立署理工具时所花费的时间却比JDK多得多。所以对于单例的工具,因为无需频繁建立工具,用CGLIB合适,反之使用JDK方式要更为合适一些。
同时由于CGLib由于是接纳动态建立子类的方法,对于final修饰的方法无法举行署理。4.7 简述动态署理的原理, 常用的动态署理的实现方式动态署理的原理: 使用一个署理将工具包装起来,然后用该署理工具取代原始工具。任何对原始工具的挪用都要通过署理。
署理工具决议是否以及何时将方法调 用转到原始工具上动态署理的方式基于接口实现动态署理: JDK动态署理基于继续实现动态署理: Cglib、Javassist动态署理。
本文来源:爱游戏app下载ios-www.huaxinfoods.com