精华内容
下载资源
问答
  • 常见设计模式笔试面试题
    万次阅读 多人点赞
    2018-08-10 15:45:46

    设计模式一套被反复使用,多数人知晓的代码设计经验的总结,实现可重用代码,使代码更容易被理解,保证代码可靠性。

    总体来说,设计模式分为三大类:

    创建型模式(五种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

    结构型模式(七种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

    行为型模式(十一种):策策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    常见的设计模式介绍:

    1、单例模式

    意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    主要解决:一个全局使用的类频繁地创建与销毁。

    何时使用:当您想控制实例数目,节省系统资源的时候。

    如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

    关键代码:构造函数是私有的。

    应用实例: 1、一个党只能有一个书记。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

    优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。

    缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    使用场景: 1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

    注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

    2、工厂模式

    在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

    意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

    主要解决:主要解决接口选择的问题。

    何时使用:我们明确地计划不同条件下创建不同实例时。

    如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

    关键代码:创建过程在其子类执行。

    3、抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

    意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

    主要解决:主要解决接口选择的问题。

    何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

    如何解决:在一个产品族里面,定义多个产品。

    关键代码:在一个工厂里聚合多个同类产品。

    4、观察者模式

    当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

    意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

    何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

    如何解决:使用面向对象技术,可以将这种依赖关系弱化。

    关键代码:在抽象类里有一个 ArrayList 存放观察者们。

     

    更多相关内容
  • 设计模式面试题(总结最全面的面试题!!!)

    万次阅读 多人点赞 2020-04-11 23:26:21
    文章目录设计模式什么是设计模式为什么要学习设计模式设计模式分类设计模式的六大原则开放封闭原则(Open Close Principle)里氏代换原则(Liskov Substitution Principle)依赖倒转原则(Dependence Inversion ...

    文章目录

    设计模式

    什么是设计模式

    • 设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

    为什么要学习设计模式

    • 看懂源代码:如果你不懂设计模式去看Jdk、Spring、SpringMVC、IO等等等等的源码,你会很迷茫,你会寸步难行
    • 看看前辈的代码:你去个公司难道都是新项目让你接手?很有可能是接盘的,前辈的开发难道不用设计模式?
    • 编写自己的理想中的好代码:我个人反正是这样的,对于我自己开发的项目我会很认真,我对他比对我女朋友还好,把项目当成自己的儿子一样

    设计模式分类

    在这里插入图片描述

    • 创建型模式,共五种:工厂方法模式、抽象工厂模式单例模式、建造者模式、原型模式。

    • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    设计模式的六大原则

    在这里插入图片描述

    开放封闭原则(Open Close Principle)

    • 原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
    • 描述:一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
    • 优点:单一原则告诉我们,每个类都有自己负责的职责,里氏替换原则不能破坏继承关系的体系。

    里氏代换原则(Liskov Substitution Principle)

    • 原则思想:使用的基类可以在任何地方使用继承的子类,完美的替换基类。
    • 大概意思是:子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类中可以增加自己特有的方法。
    • 优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。

    依赖倒转原则(Dependence Inversion Principle)

    • 依赖倒置原则的核心思想是面向接口编程.

    • 依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,

    • 这个是开放封闭原则的基础,具体内容是:对接口编程,依赖于抽象而不依赖于具体。

    接口隔离原则(Interface Segregation Principle)

    • 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
    • 例如:支付类的接口和订单类的接口,需要把这俩个类别的接口变成俩个隔离的接口

    迪米特法则(最少知道原则)(Demeter Principle)

    • 原则思想:一个对象应当对其他对象有尽可能少地了解,简称类间解耦
    • 大概意思就是一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
    • 优点:低耦合,高内聚。

    单一职责原则(Principle of single responsibility)

    • 原则思想:一个方法只负责一件事情。
    • 描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
    • 优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。

    单例模式

    1.什么是单例

    • 保证一个类只有一个实例,并且提供一个访问该全局访问点

    2.那些地方用到了单例模式

    1. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
    2. 应用程序的日志应用,一般都是单例模式实现,只有一个实例去操作才好,否则内容不好追加显示。
    3. 多线程的线程池的设计一般也是采用单例模式,因为线程池要方便对池中的线程进行控制
    4. Windows的(任务管理器)就是很典型的单例模式,他不能打开俩个
    5. windows的(回收站)也是典型的单例应用。在整个系统运行过程中,回收站只维护一个实例。

    3.单例优缺点

    优点:

    1. 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就防止其它对象对自己的实例化,确保所有的对象都访问一个实例
    2. 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
    3. 提供了对唯一实例的受控访问。
    4. 由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
    5. 允许可变数目的实例。
    6. 避免对共享资源的多重占用。

    缺点:

    1. 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
    2. 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
    3. 单例类的职责过重,在一定程度上违背了“单一职责原则”。
    4. 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

    4.单例模式使用注意事项:

    1. 使用时不能用反射模式创建单例,否则会实例化一个新的对象
    2. 使用懒单例模式时注意线程安全问题
    3. 饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)

    5.单例防止反射漏洞攻击

    private static boolean flag = false;
    
    private Singleton() {
    
    	if (flag == false) {
    		flag = !flag;
    	} else {
    		throw new RuntimeException("单例模式被侵犯!");
    	}
    }
    
    public static void main(String[] args) {
    
    }
    

    6.如何选择单例创建方式

    • 如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
      如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒韩式。
      最好使用饿汉式

    7.单例创建方式

    (主要使用懒汉和懒汉式)

    1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
    2. 懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
    3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
    4. 枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
    5. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

    1.饿汉式

    1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
    package com.lijie;
    
    //饿汉式
    public class Demo1 {
    
        // 类初始化时,会立即加载该对象,线程安全,调用效率高
        private static Demo1 demo1 = new Demo1();
    
        private Demo1() {
            System.out.println("私有Demo1构造参数初始化");
        }
    
        public static Demo1 getInstance() {
            return demo1;
        }
    
        public static void main(String[] args) {
            Demo1 s1 = Demo1.getInstance();
            Demo1 s2 = Demo1.getInstance();
            System.out.println(s1 == s2);
        }
    }
    
    
    

    2.懒汉式

    1. 懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
    package com.lijie;
    
    //懒汉式
    public class Demo2 {
    
        //类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。
        private static Demo2 demo2;
    
        private Demo2() {
            System.out.println("私有Demo2构造参数初始化");
        }
    
        public synchronized static Demo2 getInstance() {
            if (demo2 == null) {
                demo2 = new Demo2();
            }
            return demo2;
        }
    
        public static void main(String[] args) {
            Demo2 s1 = Demo2.getInstance();
            Demo2 s2 = Demo2.getInstance();
            System.out.println(s1 == s2);
        }
    }
    
    
    

    3.静态内部类

    1. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
    package com.lijie;
    
    // 静态内部类方式
    public class Demo3 {
    
        private Demo3() {
            System.out.println("私有Demo3构造参数初始化");
        }
    
        public static class SingletonClassInstance {
            private static final Demo3 DEMO_3 = new Demo3();
        }
    
        // 方法没有同步
        public static Demo3 getInstance() {
            return SingletonClassInstance.DEMO_3;
        }
    
        public static void main(String[] args) {
            Demo3 s1 = Demo3.getInstance();
            Demo3 s2 = Demo3.getInstance();
            System.out.println(s1 == s2);
        }
    }
    
    

    4.枚举单例式

    1. 枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
    package com.lijie;
    
    //使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载
    public class Demo4 {
    
        public static Demo4 getInstance() {
            return Demo.INSTANCE.getInstance();
        }
    
        public static void main(String[] args) {
            Demo4 s1 = Demo4.getInstance();
            Demo4 s2 = Demo4.getInstance();
            System.out.println(s1 == s2);
        }
    
        //定义枚举
    	private static enum Demo {
    		INSTANCE;
    		// 枚举元素为单例
    		private Demo4 demo4;
    
    		private Demo() {
    			System.out.println("枚举Demo私有构造参数");
    			demo4 = new Demo4();
    		}
    
    		public Demo4 getInstance() {
    			return demo4;
    		}
    	}
    }
    
    
    

    5.双重检测锁方式

    1. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)
    package com.lijie;
    
    //双重检测锁方式
    public class Demo5 {
    
    	private static Demo5 demo5;
    
    	private Demo5() {
    		System.out.println("私有Demo4构造参数初始化");
    	}
    
    	public static Demo5 getInstance() {
    		if (demo5 == null) {
    			synchronized (Demo5.class) {
    				if (demo5 == null) {
    					demo5 = new Demo5();
    				}
    			}
    		}
    		return demo5;
    	}
    
    	public static void main(String[] args) {
    		Demo5 s1 = Demo5.getInstance();
    		Demo5 s2 = Demo5.getInstance();
    		System.out.println(s1 == s2);
    	}
    }
    

    工厂模式

    1.什么是工厂模式

    • 它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式

    2.工厂模式好处

    • 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
    • 利用工厂模式可以降低程序的耦合性,为后期的维护修改提供了很大的便利。
    • 将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

    3.为什么要学习工厂设计模式

    • 不知道你们面试题问到过源码没有,你知道Spring的源码吗,MyBatis的源码吗,等等等
      如果你想学习很多框架的源码,或者你想自己开发自己的框架,就必须先掌握设计模式(工厂设计模式用的是非常非常广泛的)

    4.Spring开发中的工厂设计模式

    1.Spring IOC

    • 看过Spring源码就知道,在Spring IOC容器创建bean的过程是使用了工厂设计模式

    • Spring中无论是通过xml配置还是通过配置类还是注解进行创建bean,大部分都是通过简单工厂来进行创建的。

    • 当容器拿到了beanName和class类型后,动态的通过反射创建具体的某个对象,最后将创建的对象放到Map中。

    2.为什么Spring IOC要使用工厂设计模式创建Bean呢

    • 在实际开发中,如果我们A对象调用B,B调用C,C调用D的话我们程序的耦合性就会变高。(耦合大致分为类与类之间的依赖,方法与方法之间的依赖。)

    • 在很久以前的三层架构编程时,都是控制层调用业务层,业务层调用数据访问层时,都是是直接new对象,耦合性大大提升,代码重复量很高,对象满天飞

    • 为了避免这种情况,Spring使用工厂模式编程,写一个工厂,由工厂创建Bean,以后我们如果要对象就直接管工厂要就可以,剩下的事情不归我们管了。Spring IOC容器的工厂中有个静态的Map集合,是为了让工厂符合单例设计模式,即每个对象只生产一次,生产出对象后就存入到Map集合中,保证了实例不会重复影响程序效率。

    5.工厂模式分类

    • 工厂模式分为简单工厂、工厂方法、抽象工厂模式
    简单工厂 :用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
    工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)   
    抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
    

    我下面来使用代码演示一下:

    5.1 简单工厂模式

    什么是简单工厂模式

    • 简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。

    代码演示:

    1. 创建工厂
    package com.lijie;
    
    public interface Car {
    	public void run();
    }
    
    1. 创建工厂的产品(宝马)
    package com.lijie;
    
    public class Bmw implements Car {
    	public void run() {
    		System.out.println("我是宝马汽车...");
    	}
    }
    
    1. 创建工另外一种产品(奥迪)
    package com.lijie;
    
    public class AoDi implements Car {
    	public void run() {
    		System.out.println("我是奥迪汽车..");
    	}
    }
    
    1. 创建核心工厂类,由他决定具体调用哪产品
    package com.lijie;
    
    public class CarFactory {
    
    	 public static Car createCar(String name) {
    		if ("".equals(name)) {
                 return null;
    		}
    		if(name.equals("奥迪")){
    			return new AoDi();
    		}
    		if(name.equals("宝马")){
    			return new Bmw();
    		}
    		return null;
    	}
    }
    
    1. 演示创建工厂的具体实例
    package com.lijie;
    
    public class Client01 {
    
    	public static void main(String[] args) {
    		Car aodi  =CarFactory.createCar("奥迪");
    		Car bmw  =CarFactory.createCar("宝马");
    		aodi.run();
    		bmw.run();
    	}
    }
    

    单工厂的优点/缺点

    • 优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
    • 缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则

    5.2 工厂方法模式

    什么是工厂方法模式

    • 工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节

    代码演示:

    1. 创建工厂
    package com.lijie;
    
    public interface Car {
    	public void run();
    }
    
    1. 创建工厂方法调用接口(所有的产品需要new出来必须继承他来实现方法)
    package com.lijie;
    
    public interface CarFactory {
    
    	Car createCar();
    
    }
    
    1. 创建工厂的产品(奥迪)
    package com.lijie;
    
    public class AoDi implements Car {
    	public void run() {
    		System.out.println("我是奥迪汽车..");
    	}
    }
    
    1. 创建工厂另外一种产品(宝马)
    package com.lijie;
    
    public class Bmw implements Car {
    	public void run() {
    		System.out.println("我是宝马汽车...");
    	}
    }
    
    1. 创建工厂方法调用接口的实例(奥迪)
    package com.lijie;
    
    public class AoDiFactory implements CarFactory {
    
    	public Car createCar() {
    	
    		return new AoDi();
    	}
    }
    
    1. 创建工厂方法调用接口的实例(宝马)
    package com.lijie;
    
    public class BmwFactory implements CarFactory {
    
    	public Car createCar() {
    
    		return new Bmw();
    	}
    
    }
    
    1. 演示创建工厂的具体实例
    package com.lijie;
    
    public class Client {
    
    	public static void main(String[] args) {
    		Car aodi = new AoDiFactory().createCar();
    		Car jili = new BmwFactory().createCar();
    		aodi.run();
    		jili.run();
    	}
    }
    

    5.3 抽象工厂模式

    什么是抽象工厂模式

    • 抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。
      在这里插入图片描述
      代码演示:
    1. 创建第一个子工厂,及实现类
    package com.lijie;
    
    //汽车
    public interface Car {
    	   void run();
    }
    
     class CarA implements Car{
    
    	public void run() {
    		System.out.println("宝马");
    	}
    	
    }
     class CarB implements Car{
    
    	public void run() {
    		System.out.println("摩拜");
    	}
    	
    }
    
    1. 创建第二个子工厂,及实现类
    package com.lijie;
    
    //发动机
    public interface Engine {
    
        void run();
    
    }
    
    class EngineA implements Engine {
    
        public void run() {
            System.out.println("转的快!");
        }
    
    }
    
    class EngineB implements Engine {
    
        public void run() {
            System.out.println("转的慢!");
        }
    
    }
    
    1. 创建一个总工厂,及实现类(由总工厂的实现类决定调用那个工厂的那个实例)
    package com.lijie;
    
    public interface TotalFactory {
    	// 创建汽车
    	Car createChair();
    	// 创建发动机
    	Engine createEngine();
    }
    
    //总工厂实现类,由他决定调用哪个工厂的那个实例
    class TotalFactoryReally implements TotalFactory {
    
    	public Engine createEngine() {
    
    		return new EngineA();
    	}
    
    	public Car createChair() {
    
    		return new CarA();
    	}
    }
    
    
    1. 运行测试
    package com.lijie;
    
    public class Test {
    
        public static void main(String[] args) {
            TotalFactory totalFactory2 = new TotalFactoryReally();
            Car car = totalFactory2.createChair();
            car.run();
    
            TotalFactory totalFactory = new TotalFactoryReally();
            Engine engine = totalFactory.createEngine();
            engine.run();
        }
    }
    

    代理模式

    1.什么是代理模式

    • 通过代理控制对象的访问,可以在这个对象调用方法之前、调用方法之后去处理/添加新的功能。(也就是AO的P微实现)

    • 代理在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增加新功能,这也和Spring的(面向切面编程)很相似

    2.代理模式应用场景

    • Spring AOP、日志打印、异常处理、事务控制、权限控制等

    3.代理的分类

    • 静态代理(静态定义代理类)
    • 动态代理(动态生成代理类,也称为Jdk自带动态代理)
    • Cglib 、javaassist(字节码操作库)

    4.三种代理的区别

    1. 静态代理:简单代理模式,是动态代理的理论基础。常见使用在代理模式
    2. jdk动态代理:使用反射完成代理。需要有顶层接口才能使用,常见是mybatis的mapper文件是代理。
    3. cglib动态代理:也是使用反射完成代理,可以直接代理类(jdk动态代理不行),使用字节码技术,不能对 final类进行继承。(需要导入jar包)

    5.用代码演示三种代理

    5.1.静态代理

    什么是静态代理

    • 由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

    代码演示:

    • 我有一段这样的代码:(如何能在不修改UserDao接口类的情况下开事务和关闭事务呢)
    package com.lijie;
    
    //接口类
    public class UserDao{
    	public void save() {
    		System.out.println("保存数据方法");
    	}
    }
    
    package com.lijie;
    
    //运行测试类
    public  class Test{
    	public static void main(String[] args) {
    		UserDao userDao = new UserDao();
    		userDao.save();
    	}
    }
    

    修改代码,添加代理类

    package com.lijie;
    
    //代理类
    public class UserDaoProxy extends UserDao {
    	private UserDao userDao;
    
    	public UserDaoProxy(UserDao userDao) {
    		this.userDao = userDao;
    	}
    
    	public void save() {
    		System.out.println("开启事物...");
    		userDao.save();
    		System.out.println("关闭事物...");
    	}
    
    }
    
    //添加完静态代理的测试类
    public class Test{
    	public static void main(String[] args) {
    		UserDao userDao = new UserDao();
    		UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
    		userDaoProxy.save();
    	}
    }
    
    • 缺点:每个需要代理的对象都需要自己重复编写代理,很不舒服,
    • 优点:但是可以面相实际对象或者是接口的方式实现代理

    2.2.动态代理

    什么是动态代理

    • 动态代理也叫做,JDK代理、接口代理。

    • 动态代理的对象,是利用JDK的API,动态的在内存中构建代理对象(是根据被代理的接口来动态生成代理类的class文件,并加载运行的过程),这就叫动态代理

    package com.lijie;
    
    //接口
    public interface UserDao {
        void save();
    }
    
    package com.lijie;
    
    //接口实现类
    public class UserDaoImpl implements UserDao {
    	public void save() {
    		System.out.println("保存数据方法");
    	}
    }
    
    • //下面是代理类,可重复使用,不像静态代理那样要自己重复编写代理
    package com.lijie;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    // 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
    public class InvocationHandlerImpl implements InvocationHandler {
    
    	// 这其实业务实现类对象,用来调用具体的业务方法
        private Object target;
    
        // 通过构造函数传入目标对象
        public InvocationHandlerImpl(Object target) {
            this.target = target;
        }
    
        //动态代理实际运行的代理方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("调用开始处理");
            //下面invoke()方法是以反射的方式来创建对象,第一个参数是要创建的对象,第二个是构成方法的参数,由第二个参数来决定创建对象使用哪个构造方法
    		Object result = method.invoke(target, args);
            System.out.println("调用结束处理");
            return result;
        }
    }
    
    • //利用动态代理使用代理方法
    package com.lijie;
    
    import java.lang.reflect.Proxy;
    
    public class Test {
        public static void main(String[] args) {
            // 被代理对象
            UserDao userDaoImpl = new UserDaoImpl();
            InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDaoImpl);
    
            //类加载器
            ClassLoader loader = userDaoImpl.getClass().getClassLoader();
            Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces();
    
            // 主要装载器、一组接口及调用处理动态代理实例
            UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
            newProxyInstance.save();
        }
    }
    
    • 缺点:必须是面向接口,目标业务类必须实现接口
    • 优点:不用关心代理类,只需要在运行阶段才指定代理哪一个对象

    5.3.CGLIB动态代理

    CGLIB动态代理原理:

    • 利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    什么是CGLIB动态代理

    • CGLIB动态代理和jdk代理一样,使用反射完成代理,不同的是他可以直接代理类(jdk动态代理不行,他必须目标业务类必须实现接口),CGLIB动态代理底层使用字节码技术,CGLIB动态代理不能对 final类进行继承。(CGLIB动态代理需要导入jar包)

    代码演示:

    package com.lijie;
    
    //接口
    public interface UserDao {
        void save();
    }
    
    package com.lijie;
    
    //接口实现类
    public class UserDaoImpl implements UserDao {
    	public void save() {
    		System.out.println("保存数据方法");
    	}
    }
    
    package com.lijie;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    //代理主要类
    public class CglibProxy implements MethodInterceptor {
    	private Object targetObject;
    	// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
    	public Object getInstance(Object target) {
    		// 设置需要创建子类的类
    		this.targetObject = target;
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(target.getClass());
    		enhancer.setCallback(this);
    		return enhancer.create();
    	}
    
    	//代理实际方法
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    		System.out.println("开启事物");
    		Object result = proxy.invoke(targetObject, args);
    		System.out.println("关闭事物");
    		// 返回代理对象
    		return result;
    	}
    }
    
    
    package com.lijie;
    
    //测试CGLIB动态代理
    public class Test {
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
            UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDaoImpl());
            userDao.save();
        }
    }
    

    建造者模式

    1.什么是建造者模式

    • 建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的方式进行创建。

    • 工厂类模式是提供的是创建单个类的产品

    • 而建造者模式则是将各种产品集中起来进行管理,用来具有不同的属性的产品

    建造者模式通常包括下面几个角色:

    1. uilder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
    2. ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
    3. Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
    4. Product:要创建的复杂对象。

    2.建造者模式的使用场景

    使用场景:

    1. 需要生成的对象具有复杂的内部结构。
    2. 需要生成的对象内部属性本身相互依赖。
    • 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

    • JAVA 中的 StringBuilder就是建造者模式创建的,他把一个单个字符的char数组组合起来

    • Spring不是建造者模式,它提供的操作应该是对于字符串本身的一些操作,而不是创建或改变一个字符串。

    3.代码案例

    1. 建立一个装备对象Arms
    package com.lijie;
    
    //装备类
    public class Arms {
    	//头盔
    	private String helmet;
    	//铠甲
    	private String armor;
    	//武器
    	private String weapon;
    	
    	//省略Git和Set方法...........
    }
    
    1. 创建Builder接口(给出一个抽象接口,以规范产品对象的各个组成成分的建造,这个接口只是规范)
    package com.lijie;
    
    public interface PersonBuilder {
    
    	void builderHelmetMurder();
    
    	void builderArmorMurder();
    
    	void builderWeaponMurder();
    
    	void builderHelmetYanLong();
    
    	void builderArmorYanLong();
    
    	void builderWeaponYanLong();
    
    	Arms BuilderArms(); //组装
    }
    
    
    1. 创建Builder实现类(这个类主要实现复杂对象创建的哪些部分需要什么属性)
    package com.lijie;
    
    public class ArmsBuilder implements PersonBuilder {
        private Arms arms;
    
        //创建一个Arms实例,用于调用set方法
        public ArmsBuilder() {
            arms = new Arms();
        }
    
        public void builderHelmetMurder() {
            arms.setHelmet("夺命头盔");
        }
    
        public void builderArmorMurder() {
            arms.setArmor("夺命铠甲");
        }
    
        public void builderWeaponMurder() {
            arms.setWeapon("夺命宝刀");
        }
    
        public void builderHelmetYanLong() {
            arms.setHelmet("炎龙头盔");
        }
    
        public void builderArmorYanLong() {
            arms.setArmor("炎龙铠甲");
        }
    
        public void builderWeaponYanLong() {
            arms.setWeapon("炎龙宝刀");
        }
    
        public Arms BuilderArms() {
            return arms;
        }
    }
    
    
    1. Director(调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建)
    package com.lijie;
    
    public class PersonDirector {
    	
    	//组装
    	public Arms constructPerson(PersonBuilder pb) {
    		pb.builderHelmetYanLong();
    		pb.builderArmorMurder();
    		pb.builderWeaponMurder();
    		return pb.BuilderArms();
    	}
    
    	//这里进行测试
    	public static void main(String[] args) {
    		PersonDirector pb = new PersonDirector();
    		Arms arms = pb.constructPerson(new ArmsBuilder());
    		System.out.println(arms.getHelmet());
    		System.out.println(arms.getArmor());
    		System.out.println(arms.getWeapon());
    	}
    }
    

    模板方法模式

    1.什么是模板方法

    • 模板方法模式:定义一个操作中的算法骨架(父类),而将一些步骤延迟到子类中。
      模板方法使得子类可以不改变一个算法的结构来重定义该算法的

    2.什么时候使用模板方法

    • 实现一些操作时,整体步骤很固定,但是呢。就是其中一小部分需要改变,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。

    3.实际开发中应用场景哪里用到了模板方法

    • 其实很多框架中都有用到了模板方法模式
    • 例如:数据库访问的封装、Junit单元测试、servlet中关于doGet/doPost方法的调用等等

    4.现实生活中的模板方法

    例如:

    1. 去餐厅吃饭,餐厅给我们提供了一个模板就是:看菜单,点菜,吃饭,付款,走人
      (这里 “点菜和付款” 是不确定的由子类来完成的,其他的则是一个模板。)

    5.代码实现模板方法模式

    1. 先定义一个模板。把模板中的点菜和付款,让子类来实现。
    package com.lijie;
    
    //模板方法
    public abstract class RestaurantTemplate {
    
    	// 1.看菜单
    	public void menu() {
    		System.out.println("看菜单");
    	}
    
    	// 2.点菜业务
    	abstract void spotMenu();
    
    	// 3.吃饭业务
    	public void havingDinner(){ System.out.println("吃饭"); }
    
    	// 3.付款业务
    	abstract void payment();
    
    	// 3.走人
    	public void GoR() { System.out.println("走人"); }
    
    	//模板通用结构
    	public void process(){
    		menu();
    		spotMenu();
    		havingDinner();
    		payment();
    		GoR();
    	}
    }
    
    
    1. 具体的模板方法子类 1
    package com.lijie;
    
    public class RestaurantGinsengImpl extends RestaurantTemplate {
    
        void spotMenu() {
            System.out.println("人参");
        }
    
        void payment() {
            System.out.println("5快");
        }
    }
    
    1. 具体的模板方法子类 2
    package com.lijie;
    
    public class RestaurantLobsterImpl  extends RestaurantTemplate  {
    
        void spotMenu() {
            System.out.println("龙虾");
        }
    
        void payment() {
            System.out.println("50块");
        }
    }
    
    1. 客户端测试
    package com.lijie;
    
    public class Client {
    
        public static void main(String[] args) {
            //调用第一个模板实例
            RestaurantTemplate restaurantTemplate = new RestaurantGinsengImpl();
            restaurantTemplate.process();
        }
    }
    

    外观模式

    1.什么是外观模式

    • 外观模式:也叫门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。

    • 它向现有的系统添加一个接口,用这一个接口来隐藏实际的系统的复杂性。

    • 使用外观模式,他外部看起来就是一个接口,其实他的内部有很多复杂的接口已经被实现

    2.外观模式例子

    • 用户注册完之后,需要调用阿里短信接口、邮件接口、微信推送接口。
    1. 创建阿里短信接口
    package com.lijie;
    
    //阿里短信消息
    public interface AliSmsService {
    	void sendSms();
    }
    
    package com.lijie;
    
    public class AliSmsServiceImpl implements AliSmsService {
    
        public void sendSms() {
            System.out.println("阿里短信消息");
        }
    
    }
    
    1. 创建邮件接口
    package com.lijie;
    
    //发送邮件消息
    public interface EamilSmsService {
    	void sendSms();
    }
    
    package com.lijie;
    
    public class EamilSmsServiceImpl implements   EamilSmsService{
    	public void sendSms() {
    		System.out.println("发送邮件消息");
    	}
    }
    
    1. 创建微信推送接口
    package com.lijie;
    
    //微信消息推送
    public interface WeiXinSmsService {
       void sendSms();
    }
    
    package com.lijie;
    
    public class WeiXinSmsServiceImpl implements  WeiXinSmsService {
        public void sendSms() {
            System.out.println("发送微信消息推送");
        }
    }
    
    1. 创建门面(门面看起来很简单使用,复杂的东西以及被门面给封装好了)
    package com.lijie;
    
    public class Computer {
    	AliSmsService aliSmsService;
    	EamilSmsService eamilSmsService;
    	WeiXinSmsService weiXinSmsService;
    
    	public Computer() {
    		aliSmsService = new AliSmsServiceImpl();
    		eamilSmsService = new EamilSmsServiceImpl();
    		weiXinSmsService = new WeiXinSmsServiceImpl();
    	}
    
    	//只需要调用它
    	public void sendMsg() {
    		aliSmsService.sendSms();
    		eamilSmsService.sendSms();
    		weiXinSmsService.sendSms();
    	}
    }
    
    1. 启动测试
    package com.lijie;
    
    public class Client {
    
        public static void main(String[] args) {
            //普通模式需要这样
            AliSmsService aliSmsService = new AliSmsServiceImpl();
            EamilSmsService eamilSmsService = new EamilSmsServiceImpl();
            WeiXinSmsService weiXinSmsService = new WeiXinSmsServiceImpl();
            aliSmsService.sendSms();
            eamilSmsService.sendSms();
            weiXinSmsService.sendSms();
    
            //利用外观模式简化方法
            new Computer().sendMsg();
        }
    }
    

    原型模式

    1.什么是原型模式

    • 原型设计模式简单来说就是克隆

    • 原型表明了有一个样板实例,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。

    2.原型模式的应用场景

    1. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。这时我们就可以通过原型拷贝避免这些消耗。
    2. 通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
    3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

    我们Spring框架中的多例就是使用原型。

    3.原型模式的使用方式

    1. 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。

    2. 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此Prototype类需要将clone方法的作用域修改为public类型。

    3.1原型模式分为浅复制和深复制

    1. (浅复制)只是拷贝了基本类型的数据,而引用类型数据,只是拷贝了一份引用地址。

    2. (深复制)在计算机中开辟了一块新的内存地址用于存放复制的对象。

    4.代码演示

    1. 创建User类
    package com.lijie;
    
    import java.util.ArrayList;
    
    public class User implements Cloneable {
        private String name;
        private String password;
        private ArrayList<String> phones;
    
        protected User clone() {
            try {
                User user = (User) super.clone();
                //重点,如果要连带引用类型一起复制,需要添加底下一条代码,如果不加就对于是复制了引用地址
                user.phones = (ArrayList<String>) this.phones.clone();//设置深复制
                return user;
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
    	
    	//省略所有属性Git Set方法......
    }
    
    1. 测试复制
    package com.lijie;
    
    import java.util.ArrayList;
    
    public class Client {
        public static void main(String[] args) {
            //创建User原型对象
            User user = new User();
            user.setName("李三");
            user.setPassword("123456");
            ArrayList<String> phones = new ArrayList<>();
            phones.add("17674553302");
            user.setPhones(phones);
    
            //copy一个user对象,并且对象的属性
            User user2 = user.clone();
            user2.setPassword("654321");
    
            //查看俩个对象是否是一个
            System.out.println(user == user2);
    
            //查看属性内容
            System.out.println(user.getName() + " | " + user2.getName());
            System.out.println(user.getPassword() + " | " + user2.getPassword());
            //查看对于引用类型拷贝
            System.out.println(user.getPhones() == user2.getPhones());
        }
    }
    
    1. 如果不需要深复制,需要删除User 中的
    //默认引用类型为浅复制,这是设置了深复制
    user.phones = (ArrayList<String>) this.phones.clone();
    

    策略模式

    1.什么是策略模式

    • 定义了一系列的算法 或 逻辑 或 相同意义的操作,并将每一个算法、逻辑、操作封装起来,而且使它们还可以相互替换。(其实策略模式Java中用的非常非常广泛)

    • 我觉得主要是为了 简化 if…else 所带来的复杂和难以维护。

    2.策略模式应用场景

    • 策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中,从而使得它们之间可以相互替换。
    1. 例如:我要做一个不同会员打折力度不同的三种策略,初级会员,中级会员,高级会员(三种不同的计算)。

    2. 例如:我要一个支付模块,我要有微信支付、支付宝支付、银联支付等

    3.策略模式的优点和缺点

    • 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性非常良好。

    • 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

    4.代码演示

    • 模拟支付模块有微信支付、支付宝支付、银联支付
    1. 定义抽象的公共方法
    package com.lijie;
    
    //策略模式 定义抽象方法 所有支持公共接口
    abstract class PayStrategy {
    
    	// 支付逻辑方法
    	abstract void algorithmInterface();
    
    }
    
    1. 定义实现微信支付
    package com.lijie;
    
    class PayStrategyA extends PayStrategy {
    
    	void algorithmInterface() {
    		System.out.println("微信支付");
    	}
    }
    
    1. 定义实现支付宝支付
    package com.lijie;
    
    class PayStrategyB extends PayStrategy {
    
    	void algorithmInterface() {
    		System.out.println("支付宝支付");
    	}
    }
    
    1. 定义实现银联支付
    package com.lijie;
    
    class PayStrategyC extends PayStrategy {
    
    	void algorithmInterface() {
    		System.out.println("银联支付");
    	}
    }
    
    1. 定义下文维护算法策略
    package com.lijie;// 使用上下文维护算法策略
    
    class Context {
    
    	PayStrategy strategy;
    
    	public Context(PayStrategy strategy) {
    		this.strategy = strategy;
    	}
    
    	public void algorithmInterface() {
    		strategy.algorithmInterface();
    	}
    
    }
    
    1. 运行测试
    package com.lijie;
    
    class ClientTestStrategy {
    	public static void main(String[] args) {
    		Context context;
    		//使用支付逻辑A
    		context = new Context(new PayStrategyA());
    		context.algorithmInterface();
    		//使用支付逻辑B
    		context = new Context(new PayStrategyB());
    		context.algorithmInterface();
    		//使用支付逻辑C
    		context = new Context(new PayStrategyC());
    		context.algorithmInterface();
    	}
    }
    

    观察者模式

    1.什么是观察者模式

    • 先讲什么是行为性模型,行为型模式关注的是系统中对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。

    • 观察者模式,是一种行为性模型,又叫发布-订阅模式,他定义对象之间一种一对多的依赖关系,使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

    2.模式的职责

    • 观察者模式主要用于1对N的通知。当一个对象的状态变化时,他需要及时告知一系列对象,令他们做出相应。

    实现有两种方式:

    1. 推:每次都会把通知以广播的方式发送给所有观察者,所有的观察者只能被动接收。
    2. 拉:观察者只要知道有情况即可,至于什么时候获取内容,获取什么内容,都可以自主决定。

    3.观察者模式应用场景

    1. 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。事件多级触发场景。
    2. 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

    4.代码实现观察者模式

    1. 定义抽象观察者,每一个实现该接口的实现类都是具体观察者。
    package com.lijie;
    
    //观察者的接口,用来存放观察者共有方法
    public interface Observer {
        // 观察者方法
        void update(int state);
    }
    
    1. 定义具体观察者
    package com.lijie;
    
    // 具体观察者
    public class ObserverImpl implements Observer {
    
        // 具体观察者的属性
        private int myState;
    
        public void update(int state) {
            myState=state;
            System.out.println("收到消息,myState值改为:"+state);
        }
    
        public int getMyState() {
            return myState;
        }
    }
    
    1. 定义主题。主题定义观察者数组,并实现增、删及通知操作。
    package com.lijie;
    
    import java.util.Vector;
    
    //定义主题,以及定义观察者数组,并实现增、删及通知操作。
    public class Subjecct {
    	//观察者的存储集合,不推荐ArrayList,线程不安全,
    	private Vector<Observer> list = new Vector<>();
    
    	// 注册观察者方法
    	public void registerObserver(Observer obs) {
    		list.add(obs);
    	}
        // 删除观察者方法
    	public void removeObserver(Observer obs) {
    		list.remove(obs);
    	}
    
    	// 通知所有的观察者更新
    	public void notifyAllObserver(int state) {
    		for (Observer observer : list) {
    			observer.update(state);
    		}
    	}
    }
    
    1. 定义具体的,他继承继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多。
    package com.lijie;
    
    //具体主题
    public class RealObserver extends Subjecct {
        //被观察对象的属性
    	 private int state;
    	 public int getState(){
    		 return state;
    	 }
    	 public void  setState(int state){
    		 this.state=state;
    		 //主题对象(目标对象)值发生改变
    		 this.notifyAllObserver(state);
    	 }
    }
    
    1. 运行测试
    package com.lijie;
    
    public class Client {
    
    	public static void main(String[] args) {
    		// 目标对象
    		RealObserver subject = new RealObserver();
    		// 创建多个观察者
    		ObserverImpl obs1 = new ObserverImpl();
    		ObserverImpl obs2 = new ObserverImpl();
    		ObserverImpl obs3 = new ObserverImpl();
    		// 注册到观察队列中
    		subject.registerObserver(obs1);
    		subject.registerObserver(obs2);
    		subject.registerObserver(obs3);
    		// 改变State状态
    		subject.setState(300);
    		System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
    		System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
    		System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
    		// 改变State状态
    		subject.setState(400);
    		System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
    		System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
    		System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
    	}
    }
    

    文章就到这了,没错,没了

    察者方法
    public void removeObserver(Observer obs) {
    list.remove(obs);
    }

    // 通知所有的观察者更新
    public void notifyAllObserver(int state) {
    	for (Observer observer : list) {
    		observer.update(state);
    	}
    }
    

    }

    4. 定义具体的,他继承继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多。
    ```java
    package com.lijie;
    
    //具体主题
    public class RealObserver extends Subjecct {
        //被观察对象的属性
    	 private int state;
    	 public int getState(){
    		 return state;
    	 }
    	 public void  setState(int state){
    		 this.state=state;
    		 //主题对象(目标对象)值发生改变
    		 this.notifyAllObserver(state);
    	 }
    }
    
    1. 运行测试
    package com.lijie;
    
    public class Client {
    
    	public static void main(String[] args) {
    		// 目标对象
    		RealObserver subject = new RealObserver();
    		// 创建多个观察者
    		ObserverImpl obs1 = new ObserverImpl();
    		ObserverImpl obs2 = new ObserverImpl();
    		ObserverImpl obs3 = new ObserverImpl();
    		// 注册到观察队列中
    		subject.registerObserver(obs1);
    		subject.registerObserver(obs2);
    		subject.registerObserver(obs3);
    		// 改变State状态
    		subject.setState(300);
    		System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
    		System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
    		System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
    		// 改变State状态
    		subject.setState(400);
    		System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
    		System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
    		System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
    	}
    }
    

    文章就到这了,没错,没了

    如果不是必要,准备上面那九个设计模式就好了,全部记住有点难

    展开全文
  • Java设计模式面试题及答案 下载链接:全部面试题及答案PDF 1.请列举出在 JDK 中几个常用的设计模式? 单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式 (Factory pattern)被用于...

    Java面试题及答案(2022版),每道都是认真筛选出的高频面试题,助力大家能找到满意的工作!

    Java设计模式面试题及答案

    下载链接全部面试题及答案PDF

    1.请列举出在 JDK 中几个常用的设计模式?

    单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式
    (Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.valueOf,观察者模式
    (Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator
    design pattern)被用于多个 Java IO 类中。

    2.什么是设计模式?你是否在你的代码里面使用过任何设计模式?

    设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式是代码可用性的延伸

    3.Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

    单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime 是单例模式的经典例子。从 Java5 开始你可以使用枚举(enum)来实现线程安全的单例。

    4.在 Java 中,什么叫观察者设计模式(observer design pattern)?

    观察者模式是基于对象的状态变化和观察者的通讯,以便他们作出相应的操作。简单的例子就是一个天气系统,当天气变化时必须在展示给公众的视图中进行反映。这个视图对象是一个主体,而不同的视图是观察者。

    5.使用工厂模式最主要的好处是什么?在哪里使用?

    工厂模式的最大好处是增加了创建对象时的封装层次。如果你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改。

    6.举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类层次?

    装饰模式增加强了单个对象的能力。Java IO 到处都使用了装饰模式,典型例子就是Buffered 系列类如 BufferedReader 和 BufferedWriter,它们增强了 Reader 和 Writer 对象,以实现提升性能的 Buffer 层次的读取和写入。

    7.在 Java 中,为什么不允许从静态方法中访问非静态变量?

    Java 中不能从静态上下文访问非静态数据只是因为非静态变量是跟具体的对象实例关联的,而静态的却没有和任何实例关联。

    8.设计一个 ATM 机,请说出你的设计思路?

    比如设计金融系统来说,必须知道它们应该在任何情况下都能够正常工作。不管是断电还是其他情况,ATM 应该保持正确的状态(事务) , 想想 加锁(locking)、事务(transaction)、错误条件(error condition)、边界条件(boundary condition) 等等。尽管你不能想到具体的设计,但如果你可以指出非功能性需求,提出一些问题,想到关于边界条件,这些都会是很好的。

    9.在 Java 中,什么时候用重载,什么时候用重写?

    如果你看到一个类的不同实现有着不同的方式来做同一件事,那么就应该用重写(overriding),而重载(overloading)是用不同的输入做同一件事。在 Java 中,重载的方法签名不同,而重写并不是。

    10.举例说明什么情况下会更倾向于使用抽象类而不是接口?

    接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题:在 Java 中,你只能继承一个类,但可以实现多个接口。所以一旦你继承了一个类,你就失去了继承其他类的机会了。

    接口通常被用来表示附属描述或行为如:Runnable、Clonable、Serializable 等等,因此当你
    使用抽象类来表示行为时,你的类就不能同时是 Runnable 和 Clonable(注:这里的意思是指如果把 Runnable 等实现为抽象类的情况),因为在 Java 中你不能继承两个类,但当你使用接口时,你的类就可以同时拥有多个不同的行为。

    在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接

    口稍快一点。如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现。

    11. ⼯⼚⽅法模式(利⽤创建同⼀接⼝的不同实例)

    1、普通⼯⼚模式:建⽴⼀个⼯⼚类,对实现了同⼀接⼝的⼀些类进⾏实例的创建;

    12.接口是什么?为什么要使用接口而不是直接使用具体类?

    接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使用可随机访问的 ArrayList,也可以使用方便插入和删除的 LinkedList。接口中不允许写代码,以此来保证抽象,但是 Java 8 中你可以在接口声明静态的默认方法,这种方法是具体的。

    13.java中,抽象类与接口之间有什么区别?

    1.一个类可以实现多个接口 ,但却只能继承最多一个抽象类。

    2.抽象类可以包含具体的方法 , 接口的所有方法都是抽象的。

    3.抽象类可以声明和使用字段 ,接口则不能,但接口可以创建静态的final常量。

    4.接口的方法都是public的,抽象类的方法可以是public,protected,private或者默认的package;

    5.抽象类可以定义构造函数,接口却不能。

    14.除了单例模式,你在生产环境中还用过什么设计模式?

    这需要根据你的经验来回答。一般情况下,你可以说依赖注入,工厂模式,装饰模式或者观察者模式,随意选择你使用过的一种即可。不过你要准备回答接下的基于你选择的模式的问题。

    15.什么是里氏替换原则?

    1、开闭原则(Open Close Principle)

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    2、里氏代换原则(Liskov Substitution Principle)

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

    3、依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

    4、接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    5、迪米特法则(最少知道原则)(Demeter Principle)

    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

    6、合成复用原则(Composite Reuse Principle)

    原则是尽量使用合成/聚合的方式,而不是使用继承

    16.什么情况下会违反迪米特法则?为什么会有这个问题?

    迪米特法则建议“只和朋友说话,不要陌生人说话”,以此来减少类之间的耦合。

    17.适配器模式是什么?什么时候使用?

    18.适配器模式与装饰器模式有什么区别?

    19.适配器模式和代理模式之间有什么不同?

    20.什么是模板方法模式?

    21.什么时候使用访问者模式?

    22.什么时候使用组合模式?

    23.继承和组合之间有什么不同?

    24.描述Java中的重载与重写?什么时候用重载,什么时候用重写?

    25.Java中,嵌套公共静态类与顶级类有什么不同?

    26.OOP中的组合、聚合和关联有什么区别?

    27.给我一个符合开闭原则的设计模式的例子?

    28.使用工厂模式最主要的好处是什么?你在哪里使用?

    29.工厂模式与抽象工厂模式的区别?

    30.什么是设计模式?你是否在你的代码里面使用过任何设计模式?

    31.你可以说出几个在JDK库中使用的设计模式吗?

    32.Java中什么是单例设计模式?用Java写出线程安全的单例

    33.什么是责任链设计模式?

    下载链接博主已将以上这些面试题整理成了一个面试手册,是PDF版的

    展开全文
  • 设计模式面试题设计模式速成版)

    千次阅读 多人点赞 2021-03-22 16:57:23
    若之前没有学习过设计模式,可以将该文章死记硬背,然后应付面试设计模式详细学习,可以参见 http://c.biancheng.net/view/1317.html 名词解释 设计模式:软件设计模式是对各种面向对象方法的一种总结。前辈们...

    说明

    该文章适用于之前学习过设计模式,但是基本忘了。使用该文章进行快速回忆。因为是应付面试(不是笔试),所以该文结合了自己的理解,使用大白话来解释各个模型,如有错误或不严谨的地方,欢迎在评论区指正。

    若之前没有学习过设计模式,可以将该文章死记硬背,然后应付面试。

    设计模式详细学习,可以参见 http://c.biancheng.net/view/1317.html

    名词解释

    • 设计模式:软件设计模式是对各种面向对象方法的一种总结。前辈们遇到了好多设计问题,然后利用面向对象解决了。然后他们把解决方案汇总起来,形成了20多种设计模式。它可以有效的帮助我们利用面向对象,来提高代码的复用性、扩展性等。 设计模式包含4个关键元素:

      • 模式名称:就是设计模式的名字。
      • 问题:就是当面对什么样的设计问题时,应该采用该设计模式。即该设计模式的应用场景
      • 解决方案:该设计模式是使用什么样的方案解决的。
      • 效果:最后解决后,效果怎么样。比如扩展性怎么样,复用性怎么样。优缺点等等
    • 组合:将多个种类的对象合成一个新的对象。如 显示器 + 键盘 + 鼠标 +主机 = 电脑。

    • 聚合:将多个同种对象起来,形成一个新的对象,如 50个学生 = 一个班级

    • 用户:在设计模式中,用户并不是指具体使用系统的用户,而是使用你代码,或者是扩展你代码的其他程序员。

    UML基础

    UML主要用于表示类与类之间的关系。例如:

    在这里插入图片描述
    如图,在该图中
    在这里插入图片描述
    该图表示类,其中 Rectangle为类名。 length:double属性:类型getArea():double方法:返回值

    在这里插入图片描述
    圆圈代表接口,getArea():double为该接口的方法

    在这里插入图片描述
    虚线箭头表示实现,用于表示类实现了这个接口。
    在这里插入图片描述
    如果为实线,则为继承

    在这里插入图片描述
    这个表示依赖。可以简单的理解为左边的类用到了右边的类。
    在这里插入图片描述
    这个是聚合,如果这个菱形为实心的,则为组合

    面向对象编程中,都有哪些设计原则

    开闭原则

    对扩展开放,对修改关闭。就是如果要修改原有的功能或者是扩展功能,尽量去扩展原有的代码,而不是修改原来已有的代码。

    里氏替换原则(Liskov Substitution Principle)

    任何子类对象都应该可以替换其派生的超类对象 。即,子类可以扩展父类的功能,但不要修改父类原有的功能。 也就是说,当一个子类继承父类后,尽量不要去重写它原有的方法。

    依赖转置(依赖倒置)原则

    要面向接口编程,不要面向实现编程。两个模块交互时,都访问各自接口,而不是具体的实现类。

    单一职责原则

    一个对象要专注于一种事情,不要让它担任太多责任。

    接口隔离原则

    一个接口尽量只包含用户关心的内容。就是一个接口不要太庞大。

    迪米特法则

    如果两个软件实体之间不是特别必要,尽量不要让他们直接通信。而是找个第三方进行转发,比如使用MQ(消息队列)。

    合成复用原则

    如果在“组合/聚合”和“继承”之间做抉择时,优先选择“组合/聚合”。

    设计模式的分类

    设计模式分为:

    • 创建型模式:用于创建对象的设计模式。一般可以简化用户创建对象的过程。其次可以降低耦合度,用户不需要关心对象具体的创建过程。
      • 包含:单例模式、原型模型、工厂模式、建造者模式
    • 结构型模型:组织对象之间的结构。使其易于扩展等。
      • 包括:代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式
    • 行为模型:主要用于决定对象如何做出行为
      • 包括:模板方法模式、策略模式、命令模式、责任链、状态模式、观察者模式、中介者模式、迭代器模式、访问者模式、备忘录模式、解释器模式

    创建型模式

    单例模式

    • 模式名称:单例,顾名思义,就是单个实例。整个应用程序只需要一个实例即可。
    • 问题:有很多情况下,只需要一个实例即可。现实生活中,地球、中国等,如果创建这些实例,那么只需要一个即可。 在代码世界中,全局上下文、线程池、连接池这些对象,在整个程序中也是只需要一个实例即可。
    • 解决方案:禁止用户new该对象。只能通过你提供的静态方法来获取该对象。而你的静态方法返回的都是同一实例。
    • 效果:避免用户new多个没用的对象。提高了系统性能

    在这里插入图片描述

    单例模式提供了两种方式:

    • 懒汉模式:重点是。系统运行初期并不把该单例对象实例化,而是等第一次使用的时候再进行实例化。就像你平时懒得学习,非要等到考前才学一样。
    public static synchronized LazySingleton getInstance() {
        //getInstance 方法前加同步
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    
    • 饿汉模式:重点是饿,也就是饥渴。还没到用的时候,就先实例化了,等到用的时候直接就可以用了。就像平时对知识很饥渴,学好了,然后直接面试就行了。
    private static final HungrySingleton instance = new HungrySingleton();
    public static HungrySingleton getInstance() {
        return instance;
    }
    

    原型模型

    • 模式名称:原型(prototype)就是人们常说的,把XXX作为原型,然后弄一个跟它差不多的。
    • 问题:经常会遇到需要克隆一个对象,然后对其稍作修改的业务场景。此时就可以使用原型模型,将对象A作为原型,然后克隆出一个对象B。
    • 解决方案:重写对象的clone方法。也可以根据需求自己写。根据实际需要决定要深拷贝还是浅拷贝。
    • 效果:简化了创建对象的过程

    在这里插入图片描述

    补充,深拷贝的实现方式:

    • 方法1:可以将其转成json,然后再转换回来
    • 方法2:若不方便转为json,可以将其序列化,然后再反序列化。

    工厂模式

    简单工厂模式

    • 模式名称:和生活中的工厂一样,通过工厂来生产产品(对象),而不是自己手动new。
    • 问题:生活中,你不可能所有产品都自己生产,所以你会把需求给工厂说,然后工厂给你生产,比如我想要黄色的衣服,工厂就给我生产一个黄色的衣服。 在程序中也一样,我通过工厂类,提出我的需求(传递参数),然后工厂类帮我new相应的对象,返回给我。
    • 解决方案:定义一个抽象对象(如动物),然后定义一个工厂类,工厂类封装了生产具体动物(当然这些具体动物要继承动物类)的方法。然后你通过参数传递给工厂类,比如要一只汪汪叫的动物,然后工厂方法就会return new Dog()
    • 效果:用户不必关心对象怎么生产的,甚至不用关心这个具体对象是什么。工厂的生产规则可以写入配置文件,这样修改时就不需要动代码了。

    在这里插入图片描述

    工厂方法模式

    在简单工厂中,有一个缺点:如果我们想让工厂再能多生产一种东西,那就要修改这个工厂类的方法。这显然不符合开闭原则。

    针对这个问题,所以引出了工厂方法模式。

    在简单工厂模式中,不管生产什么品牌的鞋子,都用同一工厂。

    而在工厂方法模式中,想要生产某一个品牌的鞋子,就使用其对应的工厂。也就是说一个工厂只生产一种产品,如果你想要的三种不同的产品,你就要有三个工厂。比如,耐克工厂专门生产耐克鞋,阿迪工厂专门生产阿迪鞋。当你后期想要扩展的时候,你就可以再来一个安踏工厂专门生产安踏鞋。

    在这里插入图片描述

    抽象工厂模式

    在工厂方法模式中,存在一个缺点,一个工厂只能生产一种产品。如耐克工厂只能生产耐克鞋。但是如果我想让耐克工厂即能生产耐克鞋也可以生产耐克衣服就没办法了。

    所以为了解决这个问题,就引出了抽象工厂。在抽象工厂类中定义多个产品,即定义它的子类应该生产哪些产品。 这样就可以做到,耐克厂可以生产耐克鞋和耐克衣服,阿迪工厂可以生产阿迪鞋和阿迪衣服。

    但是很明显有一个缺点:如果抽象工厂再加一种产品,比如帽子,那么所有的具体工厂类都要改造。
    在这里插入图片描述

    简单工厂、工厂方法和抽象工厂三者有什么区别

    • 简单工厂:所有的产品都由一个工厂生产。如果你想加产品,就改源代码。
    • 工厂方法:有多个工厂,但一个工厂只生产一种产品。如耐克厂只生产耐克鞋,阿迪厂只生产阿迪鞋。如果你想加产品,那就加工厂类。
    • 抽象工厂:有多个工厂,每个工厂可以生产多类产品。如耐克厂可以生产耐克鞋和耐克衣服,阿迪厂可以生产阿迪鞋和阿迪衣服。但如果你想生产帽子,那么就需要在抽象工厂里加抽象方法,然后修改所有的子类。

    建造者模式(Builder)

    • 模式名称:用户就是建造者,自己动手造自己想要的东西。
    • 问题:有些产品需要由多个部件组成,比如电脑需要键盘、鼠标、显示器、音响、主机等等。这些东西有些需要,有些也可以不需要,有些可以随便用(取默认值)。将其抽象到代码世界,若这些配置的组合方式全部弄成构造函数,那需要好多构造函数。用户光看这些构造函数就看晕了。
    • 解决方案:可以将每个部件的构造过程都封装成方法,提供给用户,用户选完自己的部件后,调用build()方法构建出他想要的对象。
    Computer computer=new Computer.Builder("因特尔","三星")
                    .setDisplay("三星24寸")
                    .setKeyboard("罗技")
                    .setUsbCount(2)
                    .build();
    
    • 效果:用户可以定制化自己的产品。避免臃肿的构造方法。

    结构型模式

    代理模式

    • 模型名称:和生活中的代理一个意思。本来该我做的事情,我找个代理人替我做。
    • 问题:我想把房子租出去,但是又不想直接带人看房,原因有很多:找客户、风险等。所以我找个中介(代理),让他替我把房子租出去。在代码世界中,前端调用一个后台方法,因为风险(要验证其是否登录),我不想让它直接调用核心方法,而是让它调用代理对象,如果没有风险,再由这个代理对象替它调用核心方法。
    • 解决方案:新建代理类,在代理类的方法中调用实际方法,可以在实际方法前后增加逻辑。当访问该方法时,不new该方法对应的对象,而是new其代理对象。通常有动态代理和静态代理
    • 效果:可以在方法前后增加一些业务逻辑,如日志、安全校验等。

    在这里插入图片描述
    代理模式的实现方式:

    • 静态代理:程序员自己编写代理类。或者是通过特定工具生成代理类(如AspectJ)。总之,在上JVM运行前的class文件中,代码用的就已经是代理类了。
    • 动态代理:在程序运行时,通过反射机制动态创建。如Spring AOP就是用的动态代理类。

    Spring AOP如何进行动态代理

    @Autowired
    private IService service;
    

    你在写代码时,你并没有实例化这个service。而是将该实例通过spring进行注入,这样spring在就可以在运行时,根据你配置的规则,来决定这个service应该使用正常的bean还是使用其代理类。

    装饰器模式

    • 模式名称:和现实装饰一样,在外层再套一层皮。
    • 问题:若我现在全裸,我想给自己装饰一下,那么我就可以在外层套一个外套。要是想再装饰,可以在外面再套一个貂皮大衣。在代码世界中,我可以在一个核心方法外再套一层计时功能,然后可以再套一层日志功能。 总之就是在方法的前后增加一些逻辑。
    • 解决方案:最简单的方式是继承该类,然后重写其方法,在前后增加逻辑,但是这样不符合里氏替换原则。可以通过定义装饰器类,该装饰器类和具体要装饰的类都来自同一父类,由该装饰器类负责装饰,用户调用时调用该装饰器类。
    • 效果:可以装饰原有类,可以提供更强劲的功能。

    在这里插入图片描述
    装饰器的典型应用:

    BufferedReader in = new BufferedReader(new FileReader("filename.txt"));
    String s = in.readLine();
    

    装饰器模式为FileReader增加了Buffer功能。

    装饰器模式和代理模式的区别

    共同点:

    • 效果类似:装饰器模式和代理模式都可以在方法的前后增加逻辑
    • 可以嵌套:代理可以层层代理,装饰也可以层层装饰

    区别:

    • 实现方式不同:代理类和原有类之间并没有什么直接关联。就像我找中介帮我卖房子,我和中介之间并没有什么直接关系,我出钱他办事,我是我,他是他。但是装饰类和被装饰类之间就有关系,他们有共同的父类。比如BufferReader装饰了FileReader,他们有共同的父类Reader。
    • 应用场景不同:代理模式主要用来做AOP,适用于同时对好多方法做切面。就像中介也不可能只帮我一个人卖房子。 而装饰器只专注于装饰一个类,BufferReader只专注于装饰Reader。

    适配器模式

    • 模式名称:在现实世界中,充电器全名为电源适配器。它的作用是将220V的交流电转化为5V的直流电。适配器就是一个转换器,将两种不兼容的东西,通过转换器使其互相兼容。
    • 问题:在现实世界有很多适配器。在代码世界中,同样有很多相互之间不兼容的接口或类,它们之间无法直接相互调用,所以就需要适配器来进行转换。如"手机"调用"电源适配器"的充电接口,然后由电源适配器调用电源来获取电。
    • 解决方案:适配器(充电器)通过组合(也可以使用继承,但不推荐)的形式保存适配者(手机)的引用。同时适配器实现目标对象(电源)的方法(充电)。当用户要给访问目标方法时,就new一个适配器,然后将适配者传给适配器,通过适配器访问目标方法。 比如,用户要给手机充电时,先new一个充电器,然后把手机连上充电器(把手机对象传给充电器),然后充电器访问电源的充电方法。
    • 效果:解决了两个对象之间不兼容导致无法调用的问题。

    在这里插入图片描述
    主要角色解释:

    • 适配器:就是充电器。负责数据转换
    • 适配者:就是手机。
    • 目标对象:就是电源。

    桥接模式

    • 模式名称:桥接个人理解是一个桥将两端接起来。我也不知道这么模式为什么这么起名,可能是因为该模式的英文名是Bridge吧。
    • 问题:通常一个角色可能不止有一种属性。比如皮包,可以有颜色、品牌、类型等不同维度的属性。如果使用子类的形式来构建不同的对象,如黄色LV钱包,这样的话子类的数量就会非常庞大。子类总数 = 颜色 × 品牌 × 类型。
    • 解决方案:选定一个属性作为主属性,使用继承的方式实现主属性。然后剩下的属性通过组合的方式实现。
    • 效果:优化了因属性太多导致有过多子类的问题。

    在这里插入图片描述
    在该图中,选定Bag的类型作为主属性,通过继承的方式定义不同的Bag。而包的颜色作为次要属性,通过组合的方式进行实现。

    外观模式(Facade)

    • 模式名称: 外观模式也叫门面模式。门面模式可能更形象一点。将所有细节都封装起来,只给你提供一个门面,你通过这个门面来做自己要做的事。
    • 问题:当你去银行办理业务时,流程可能很复杂,比如要先到窗口A登记信息,到窗口B验证信息,再到窗口C办理业务,最后到窗口D拿结果。
    • 解决方案:新建一个门面类(Facade),使用组合的方式组合窗口ABCD,然后只为客户提供一个窗口,客户在这个窗口就可以办理完毕。
    • 效果:降低了子系统和客户之间的耦合度。

    在这里插入图片描述
    扩展:
    这个模式有一个缺点,如果要增加子系统,或者变更外观角色,那么就要修改源代码,不符合开闭原则。 解决方案也很简单,只需要对外观角色提供抽象即可。

    在这里插入图片描述
    将外观对象提供一个抽象类。客户在使用时,使用其抽象类,这样在变更子系统后,可以重新创建一个具体外观类。

    享元模式

    • 模式名称:享元就是分享元数据。其中元为本原,也可以理解为本质。比如围棋子对象,可以有坐标、颜色、大小等。但围棋子的本质是它的颜色和大小,坐标是它的次要属性(每个棋子的坐标基本都不一样)
    • 问题:在程序中,需要创建大量相似的对象。比如围棋,需要很多围棋对象,它们除了坐标不一样,其他属性基本一致。LOL中的小兵,除了位置血量等不一样,其他属性也基本一样。假如每生成一个小兵,都使用深拷贝,那么内存中就会存在大量重复的数据。这样我的小破电脑肯定吃不消。
    • 基本概念: 这里插一条概念解释。
      • 外部状态:会改变的属性称为外部属性,如五子棋的坐标。它对应的对象被称为非享元对象
      • 内部状态:不会改变的属性。如五子棋的大小等。它对应的对象被称为享元对象
    • 解决方案:将原本的整个对象抽象出享元对象和非享元对象,然后通过工厂进行管理。用户通过工厂获取对象,工厂组合享元对象和非享元对象返回给用户。
    • 效果:优化了内存占用

    在这里插入图片描述

    扩展:

    • 单纯享元对象: 如果没有非享元对象,那么就称为单纯享元对象。此时就像是一个工厂管理着许多单例对象。
    • 复合享元对象:有非享元对象,就是上面那张图的情况。

    享元模式和原型模式比较

    共同点:它们都适用于,如果两个元素改动比较小的话,可以使用该模式。

    区别:

    • 享元模式:适用于存在大量相似元素,且相似元素可以抽象出共同点(内部状态)。代码相对复杂。
    • 原型模式:如果一共就不几个相似元素,直接用原型模式即可,没必要写享元模式。

    组合模式

    • 模式名称:把一堆对象组合起来,组成一个大的新的对象。
    • 问题:如果你想让一个人拥有游泳、绘画、编程三个技能,你可以实现这三个接口,如果他们是类,你可以不断继承 人->编程->绘画->游泳,但是这样显然不合适。
    • 解决方案:使用“组合/聚合”的形式,让“人”对象组合这三个技能即可。可以在人对象中定义一个 List<Ability>。
    • 效果:后期对组合对象加元素时比较方便,易扩展。

    在这里插入图片描述
    其实这种模式大家应该基本天天在用,就是对象里面套对象。只不过可能不知道这也是一种设计模式而已。

    行为型模型

    观察者模式

    • 模式名称:作为偷窥者,看到脱衣服,要流鼻血,看到自己被发现了,要跑。作为观察者同样,当被观察的事物状态发生变化时,观察者要做出相应的反映。
    • 问题:在代码世界,用户也想让自己的对象监听(观察)另一个对象,当另一个对象发生变化后,能够回调自己的对象的方法,但是他们不知道怎么做。
    • 解决方案:目标(被观察者)对象要包含观察者列表,当目标对象状态发生变化时,目标对象要依次调用观察者的通知方法。
    • 效果:目标对象发生变化时,会通知观察者。

    在这里插入图片描述
    用通俗的语言解释一下观察者模式:

    1. 小明、小王、小李三人想在考研政策变化的时候立刻做出反应。所以他们想作为观察者来观察考研政策的变化。因此,他们要继承观察者(Observer)接口,并实现反应(response)方法。
    2. 小明等三人只实现了接口,还没有在研招网上进行注册,那研招网肯定没办法通知他们。但同样此时,研招网也没有开通注册通道,他们也没法注册。
    3. 所以,研招网继承了抽象目标类,该类包含了注册、注销、通知用户等抽象方法。研招网继承该类并实现其抽象方法。
    4. 研招网开通了注册通道后,小明三人就在研招网上进行注册。此时研招网就已经知道要通知哪些人了。
    5. 之后,研招网有了新动态(目标对象状态发生了变化),变化的同时,研招网也会调用自己的notifyObserver()方法,该方法就是依次通知所有的观察者,相当于回到了小明三人的response方法。
    6. 小明三人收到了通知,得知了最新的考研政策。小明、小红开始努力学习,小李选择放弃考研。

    中介者模式(mediator)

    • 模式名称:mediator为“调停者;传递者;中介物”,意思为“充当各方之间联系纽带的谈判者”。
    • 问题:现实中充满了各种网状结构。如朋友圈是一个网,每个人是这张网上的一个节点。所有的租客和房主也能形成一张网等等。如果是网状结构,会存在一个问题,一个人的电话号码修改了,他需要提前通知所有的朋友。房主的房租降价了,也需要通知所有找房的租客。但如果把网状结构改为星状结构,有一个星星作为所有人中间的桥梁,那么问题就影刃而解了。于是QQ出现了 、房产中介出现了。 在代码世界中,也存在,当一方状态改变,他需要发送消息通知其他人(也可以是单纯的发送消息),那么应该怎么做呢?
    • 解决方案:定义一个中介类,提供注册和转发的功能。“其他同事”可以把自己注册到中介系统中。并同时拿着中介的联系方式(引用)。其他同事需要提供接收和发送的接口。这样,用户就可以调用其中一个同事的发送接口,然后发送接口调用中介的转发接口,让其通知其他同事。
      • 同事:中介服务的对象称为同事。
    • 效果:将网状结构转为星状结构,降低了各个同事之间的耦合性。

    在这里插入图片描述

    • register:负责注册同事。如果你不来婚姻介绍所注册登记,人家怎么给你介绍对象
    • relay:转发消息。将消息转发给其他同事
        public void relay(Colleague cl) {
            for (Colleague ob : colleagues) {
                if (!ob.equals(cl)) {
                    ((Colleague) ob).receive();
                }
            }
        }
    
    • receive:接收消息。中介的转发消息会调用每个同事的该方法。
    • send:发送消息。用户可以调用同事的该方法来发送消息,该方法会调用中介的relay方法来让中介去通知其他同事。这也就是为什么同事类要包含中介的引用。
        public void send() {
            System.out.println("具体同事类1发出请求。");
            mediator.relay(this); //请中介者转发
        }
    

    中介者模式和观察者模式对比

    相同点:中介者模式和观察者模式都可以用于当一方状态发生变化时通知其他人。

    区别:

    • 观察者模式:主要用于一对多。多个对象观察一个对象。观察者和目标对象通常不一致。比如,多个人观察天气变化。
    • 中介者模式:主要用于多对多,大家都是观察者,同时也都是目标对象。每个对象也都一样,至少是同一种抽象。比如群聊,每个人都可以发消息,然后通过中介转发给其他所有人。

    模板方法模式

    • 模式名称:与现实中的模板一致。拿一个简历模板,把里面内容改改。拷贝别人的代码模板,把里面内容改改。
    • 问题:在代码世界,经常会遇到很多逻辑相似的地方,但还不太一样。比如用纯jdbc操作数据库时,要连接数据库、构建Statement、写sql、获取结果、把结果转为对象。虽然都是这个流程,但是每次还都不太一样。如果每次都是复制之前的代码,然后改改,那会出现大量重复的代码。如果后期出现需求变换,比如要改下Statement的实现类,那所有涉及到的地方都要改。
    • 解决方案:将相同的过程抽象成一个抽象类,一模一样的地方用具体方法,不一样的地方使用抽象方法。下次再使用时,只需要继承该抽象类,然后实现其抽象方法即可。
    • 效果:减少了大量的重复逻辑。

    在这里插入图片描述

    策略模式

    • 模式名称:一个人想干一件事,有很多策略,然后他选其中一种策略来做。
    • 问题:用户想对一组数据进行排序,有很多策略可以选,如冒泡排序、快速排序、堆排序等。他不太确定后期会不会变,要是为每种排序都写一个类,而后期想切换排序就会很麻烦。
    • 解决方案:定义一个接口,让其他排序类继承这个接口。用户在使用时不使用具体的类,而是使用接口。这样后期变更策略时,不会影响用户的代码。
    • 效果:将行为和具体实现分离开,便于后期扩展和维护。

    策略模式其实到处都是,一个接口有好几个实现类,你选择其中一个进行使用。

    但有一点不同的是,策略模式需要有策略的选择角色,比如环境类。通过设置环境对象,让环境对象帮助用户选择使用哪一种策略,而不是由用户自己选择。

    在这里插入图片描述
    策略模式也可以和工厂模式结合使用,使用工厂来决定应该生产出哪一种策略来让用户使用。

    在这里插入图片描述

    命令模式

    • 模式名称:在Linux中,用户使用工具基本都靠命令。一般都是封装一个cli,如redis-cli,用户通过客户端发送命令给服务端,然后服务端做响应的处理。同样这个思想可以放到面向对象编程中。
    • 问题:如果两个模块相互之间调用时,使用直接调用的方法,这样耦合性太高了。这样的话,其中一方升级,增加了命令,另一方想要使用,就必须跟着升级。
    • 解决方案:在请求者和接受者之间加一层命令类,通过命令对象来决定请求者发出的这个请求,应该由哪个接受者进行处理。
    • 效果:对请求者和接受者进行了解构。一方的改动不会影响到另一方。

    在这里插入图片描述

    责任链模式

    • 模式名称:一个人想办理一个业务,这个时候需要找有关部门,因为有关部门有责任帮用户办理该业务。 那为什么称为责任链呢?因为这个业务比较复杂,可能会分为以下两种情况:
      • 纯的责任链(推卸责任):A部门说,这个不归我管,你去找B部门问问。结果B部门又让用户找C部门,C让用户找D,最终形成一条责任链,A->B->C->D。但是这条链上一定会有一个部门可以处理。
      • 不纯的责任链(都有责任):A部门,我签好字了,你去找B部门继续签字,B部门签完后,继续找C部门签字。最后形成一条责任链A->B->C。这条责任链中,每一个对象都担负了一定责任。
    • 问题:不管是现实世界,还是代码世界,都会存在这种的责任链现象。一个对象的处理需要不断的向后传递。
    • 解决方案:定义一个抽象处理者,其中包含了下一环节的处理者,和一些抽象方法(如设置下一环节处理者,处理对象等)。根据需求定义具体处理者(继承抽象继承者)。用户在使用时,声明各个具体处理者的实例,然后将其穿成一个链,然后将对象交给链中的第一个处理者进行处理。
    • 效果:
      • 增加了系统的可扩展性(如果对象增加处理,只需要增加处理对象并将其加入责任链中)。
      • 在纯的责任链中,避免了很多if-else语句。如果不用责任链的话,那就是使用if-else或switch语句来判断应该选用处理类进行处理。

    在这里插入图片描述
    在这里插入图片描述

    状态模式

    • 模式名称:人有不同的状态,人在不同的状态下,同样的事情会有不同的反映。对于对象来说,也会有不同的状态,也应该具备对不同状态做出不同反映的能力。
    • 问题:如果我有一个电梯对象,它具有停止、正在上升、正在下降等状态。此时如果用户按下了某个楼层,电梯应该如何根据自己当前状态做出反应呢?
    • 解决方案:定义状态抽象类,其中包括抽象方法Handle。让具体状态类实现抽象状态类,实现其Handle方法。有状态对象使用组合的方式集成状态对象,并提供设置状态接口。 比如:①定义电梯状态抽象类。②定义“上升状态”、“下降状态”和“停止状态”三个类,并实现抽象类中的“用户按下按键”方法。③电梯对象中使用组合的方式集成“状态”对象。
    • 效果:符合单一职责原则,一个类只干一件事。将状态与实际对象分离,实现解耦。

    在这里插入图片描述

    迭代器模式

    • 模式名称:迭代就是遍历一个集合
    • 问题:如果我想遍历一个集合,可以使用for循环。但是有一个缺点,我需要知道集合的大小,并且该集合还要能支持随机访问 ,比如list[3]这样。但是Set就不支持这种随机访问(随机访问可以理解为通过下标进行访问)。那能不能提供一种统一的遍历方式,且不需要关心它是否支持随机访问呢?
    • 解决方案:定义一个Iterator接口,包含next(),hasNext()方法。让你需要遍历的对象实现该接口,通过下面这种方式访问
    while(obj.hasNext()) {
    	obj.next();
    }
    
    • 效果:遍历对象无需知道它的内部结构,比如如何获取它的长度等。

    在这里插入图片描述

    扩展:
    平时用的 for-each 的本质就是迭代器模式。例如下列代码:

    public class Test implements Iterable {
    
        public Iterator iterator() {
            return null;
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            for (Object o : test) {
                System.out.println(o);
            }
        }
    }
    

    该代码是可以编译通过的,因为Test类实现了Iterable方法,所以可以使用for-each遍历它。否则,则编译会报错。

    访问者模式

    访问者模式 (偷个懒)

    备忘录模式(快照模式)

    • 模式名称:个人感觉快照模式这个名字更合适。为对象打一个快照,当发现对象出问题时,可以恢复到之前的快照版本。
    • 问题:在使用虚拟机时,我们经常给虚拟机打快照,如果后面出问题了,可以恢复。在面向对象编程中,也会出现类似的需求。比如你要对一个对象进行一系列的操作,如果失败的话,就会滚到之前的状态,所以你需要在开始之前为对象打一个快照。
    • 解决方案:新建一个管理者,负责管理快照。当你要备份快照前,你可以使用原型模式来生成一份一样的对象,交给管理者来管理。当需要恢复时,通过管理者来恢复到之前的快照。
    • 效果:可以方便的管理备份和恢复对象

    在这里插入图片描述

    解释器模式

    • 模式名称:Javascript、Python这些都是解释性语言。而将字符翻译成机器语言的东西就称为“解释器”
    • 问题:若你也想定义一种解释性语言怎么办?
    • 解决方案:定义解释器。这个就不深入讲了(其实我不会,而且面试官基本也都不会)
    • 效果:比如Hibernate定义有HQL,还有SpringEL。这些都算是利用解释器模式来方便的让用户更灵活的达到自己想要的效果。
    展开全文
  • 本压缩包主要内容为c#教程(书籍)及一些经典资料,主要包括有: ...23种设计模式.pdf c++笔试面试宝典2009版.doc 程序员面试宝典.pdf 高质量C++-C编程指南.mht 注意:共有两个分卷,这是分卷2。
  • 面试题 ( v4.4 ) 如果您喜欢该项目,请单击。 拉取请求受到高度赞赏。 目录 问:什么是 NoSQL 数据库? NoSQL 数据库有哪些不同类型? NoSQL 是一种非关系型 DBMS,不需要固定模式,避免连接,并且易于扩展。 使用 ...
  • 本压缩包主要内容为c#教程(书籍)及一些经典资料,主要包括有: ...23种设计模式.pdf c++笔试面试宝典2009版.doc 程序员面试宝典.pdf 高质量C++-C编程指南.mht 注意:共有两个分卷,这是分卷1。
  • 1、单例模式是一种常用的设计模式,单例模式比较简单但包含了关于线程安全、内存模型、类加载机制等一些比较核心的知识点。 2、单例模式就是在整个运行时域,一个类只有一个实例对象。 3、为什么需要单例模式呢?...
  • 最新Java面试题常见面试题及答案汇总

    万次阅读 多人点赞 2019-07-12 08:56:55
    Java最新面试题面试题答案汇总
  • 设计模式面试专题及答案
  • 2022前端笔试面试题

    千次阅读 2022-04-06 14:43:23
    讲讲你知道的设计模式 (吐槽:后来学了Java才知道面试官问的是啥) 知识点: 1. 什么是设计模式? Design pattern,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 2.有哪些设计模式? 共...
  • 数据库常见笔试面试题

    千次阅读 2019-04-13 09:28:39
    数据库基础(常见面试题) 一、数据库基础 数据抽象:物理抽象、概念抽象、视图级抽象,内模式模式、外模式 SQL语言包括数据定义、数据操纵(Data Manipulation),数据控制(Data Control) 数据定义:Create Table,...
  • 京东Java面试题笔试题(含答案)

    千次阅读 2022-04-14 18:58:01
    其他互联网大厂面试题 1:阿里巴巴Java面试题 2:阿里云Java面试题-实习生岗 3:腾讯Java面试题-高级 4:字节跳动Java面试题 5:字节跳动Java面试题-大数据方向 6:百度Java面试题 7:蚂蚁金服Java面试题-中级 8:...
  • 本文分为十九个模块,分别是:Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、 Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、...
  • 高级java工程师笔试题目录 编程语言/框架/平台 安卓 AngularJS 角 主干JS C++ C C# 。网 Clojure CSS Cucumber 姜戈 码头工人 EmberJS 二郎 高朗 图Ql HTML 离子 IOS Java JavaScript jQuery 前端构建工具 ...
  • java 面试宝典 Java中的23种设计模式 Java面试141 Java入门需掌握的30个基本概念
  • 关于设计模式和框架的笔试面试题,找了好久才找到 的,应届生绝对用的着
  • 其他互联网大厂面试题 1:阿里巴巴Java面试题 2:阿里云Java面试题-实习生岗 3:腾讯Java面试题-高级 4:字节跳动Java面试题 5:字节跳动Java面试题-大数据方向 6:百度Java面试题 7:蚂蚁金服Java面试题-中级 8:...
  • 前端面试题汇总 笔试

    万次阅读 多人点赞 2018-02-11 16:08:47
    前端面试题目1. div和span的区别?div是块级标签,span是行级标签 2. 在html中,position取值有哪几种,默认值是什么?取值:static、relative、fixed、absolute默认值:static 3. 前端页面由哪三层构成,分别...
  • 高级java工程师笔试题精彩访谈 技术面试问题列表的精选列表。 如果你想贡献,请阅读 或 。 查看我的或 . 目录 编程语言/框架/平台 安卓 . AngularJS 角 主干JS C++ C C# 。网 Clojure CSS Cucumber 姜戈 码头工人 ...
  • 面试总结:Golang常见面试题汇总

    千次阅读 2021-06-28 18:37:57
    Go 的设计者倾向于高性能的并发表现,为了避免过多浪费 CPU 资源,自旋的线程数不会超过 GOMAXPROCS。 本地队列(local queue): 本地是相对 P 而言的本地,每个 P 维护一个本地队列;与 P 绑定的 M 中如若生成新的...
  • 操作系统常见笔试面试题(一)

    万次阅读 多人点赞 2018-08-07 11:23:51
    进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,...
  • 请通过编码实现得到第N个的值 通过列举使用过的设计模式分别描述这些设计模式的核心思想优点缺点并写出至少2种设计模式的样例代码 给出以下3张表 ORDER ORDER ID MUMBER(38) (PK) ORDER_DESC VARCHAR(200) ORDER_DAT
  • 文章目录单例模式适配器模式代理模式桥接模式建造者模式外观模式 单例模式 适配器模式 代理模式 桥接模式 建造者模式 外观模式
  • JS笔试面试常见编程

    千次阅读 多人点赞 2021-03-11 13:16:03
    JS笔试面试编程一、排序1、快速排序2、冒泡排序3、插入排序4、选择排序5、希尔排序6、归并排序7、二分查找(折半查找)二、数(一)自然数中出现过多少次数字几问题(二)回文数判断(三)判断一组数字是否连续(四...
  • python自动化笔试面试题(附带答案)

    万次阅读 多人点赞 2019-05-23 14:19:09
    1、自动化代码中,用到了哪些设计模式? 答:自动化代码用到过的设计模式: ①单例设计模式 ②工厂模式 ③PO设计模式 ④数据驱动模式 ⑤面向接口编程设计模式 2、什么是Selenium 答:Selenium是一个开源的web自动化...
  • 100 道 Linux 常见面试题,建议收藏!

    千次阅读 2021-08-23 00:43:35
    本文共 2W+字,分别从 Linux 概述、磁盘、目录、文件、安全、语法级、实战、文件管理命令、文档编辑命令、磁盘管理命令、网络通讯命令、系统管理命令、备份压缩命令等方面拆解 Linux 常见面试问题。可以先收藏,...
  • 前端小程序笔试面试题

    千次阅读 2021-03-30 11:10:21
    前端HTML+CSS笔试面试题前端JS笔试面试题前端Vue笔试面试题前端小程序笔试面试题前端面试必备宝典--知识点深入整合篇 1、微信小程序有几个文件 WXML(WeiXin Markup Language)是框架设计的一套标签语言,...
  • 史上最全Java初中级面试题,发现网上很多Java初级面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 本人发现网上虽然有不少Java相关的面试题,但第一未必全,第二未必有...
  • 100道最新Java面试题常见面试题及答案汇总

    万次阅读 多人点赞 2021-01-28 15:32:15
    小编特意整理了100道Java面试题,送给大家,希望大家都能顺利通过面试,拿下高薪。赶紧码住吧~~ Q1:Java内部类和子类之间有什么区别? 答案:内部类是指在一个外部类的内部再定义一个类,内部类对外部类有访问权限...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,715
精华内容 8,686
热门标签
关键字:

常见设计模式笔试面试题

友情链接: snake.zip