JAVA后端面试《设计模式》

设计模式

  • 一、代理模式
    • 1.静态代理
    • 2.动态代理
    • 3.Cglib代理
  • 二、单例模式
    • 1.非线程安全懒汉式
    • 2.线程安全懒汉式
    • 3.饿汉式(线程安全的):一般情况下建议使用
    • 4.双重校验锁(DCL,即 double-checked locking,线程安全的)
    • 5.静态内部类(线程安全)
    • 6.枚举(线程安全)
  • 三、简单工厂,工厂方法,抽象工厂
    • 1.简单工厂模式
    • 2.工厂方法模式
    • 3. 抽象工厂模式
    • 4.工厂方法模式和抽象工厂模式的区别?

一、代理模式

1.静态代理

  • 静态代理的特点:
    ①目标类和代理类都实现了同一接口
    ②比较容易实现
    ③在不修改目标类核心功能的前提下,很容易就可以对目标l类进行功能的增强
    ④缺点:因为目标类和代理类实现了同一接口,此时接口一旦发生改变,两个类都必须同时实现新增的功能(动态代理可以解决这一缺点)
/**
 *共同的接口
 */
public interface SingPerform {

    //举办演唱会
    void sing();

    //void play();

    //void xidu();
}
/**
 * 目标类:做主要工作的类.
 */
public class Singer implements SingPerform {

    @Override
    public void sing() {
        System.out.println("大歌星开始唱歌啦....");
    }
}
/**
 * 代理类:执行辅助性的一些功能
 * 利用有参构造函数引入目标类
 * 对目标类的核心功能进行增强
 */
public class Broker implements SingPerform {

    private Singer singer;

    public Broker(Singer singer) {
        this.singer = singer;
    }

    @Override
    public void sing() {
        System.out.println("经纪人开始准备演唱会啦...");

        singer.sing();

        System.out.println("经纪人为演唱会善后...");
    }
}
/**
 *静态代理测试
 */
public class StaticProxyTest {

    public static void main(String[] args){
        Singer singer=new Singer();
        Broker broker=new Broker(singer);
        broker.sing();
        /*  结果:"经纪人开始准备演唱会啦..."
                 "大歌星开始唱歌啦...."
                 "经纪人为演唱会善后..." */
    }
}

2.动态代理

  • 动态代理的特点
    ①目标类实现接口,代理类不实现接口
    ②以后目标类根据需要扩展接口的功能,而不会影响到代理类
    ③利用反射实现,性能不高
/**
*目标类的接口
*/
public interface SingPerform {

    //举办演唱会
    void sing();

    //void play();

    //void xidu();
}
/**
 * 目标类:做主要工作的类.
 */
public class Singer implements SingPerform {

    @Override
    public void sing() {
        System.out.println("大歌星开始唱歌啦....");
    }

}
/**
 * 代理类:用来执行一些辅助性工作的类.
 */
public class Broker {

    public void prepareSing() {
        System.out.println("经纪人开始准备演唱会...");
    }

    public void afterSing() {
        System.out.println("经纪人为演唱会善后...");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理工厂:在这里具体实现动态代理
 */
public class ProxyFactory {

    private Object target;//Singer
    private Broker broker;

    public ProxyFactory(Object target, Broker broker) {
        this.target = target;
        this.broker = broker;
    }

    //创建出以目标类为模板的代理对象的方法
    public Object newInstance() {
        Class<?> clazz = this.target.getClass();
        //得到目标类的类加载器
        //ClassLoader classLoader = clazz.getClassLoader();

        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {

            //创建出一个目标类的代理对象: 以目标类为模板,克隆出目标类的一个副本,然后以此为模板创建出一个对象,该对象就是目标类的代理对象
            //proxy:就是克隆出的代理对象(歌星的分身)
            //method:歌星的分身要执行的方法--->sing(),play(),xidu()
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                broker.prepareSing();

                //target.sing();
                //target.method(args)
                Object obj = method.invoke(target, args);

                broker.afterSing();

                return obj;
            }
        });
    }

}
/**
 *动态代理的测试
 */
public class DynamicProxyTest {

    public static void main(String[] args) {
        Singer target = new Singer();
        Broker broker = new Broker();
        ProxyFactory factory = new ProxyFactory(target, broker);
        //注意:此处必须转换为目标类的父类:接口
        SingPerform perform = (SingPerform) factory.newInstance();
        perform.sing();
    }

}

3.Cglib代理

  • Cglib代理的特点
    ①代理类和目标类都无需实现接口
    ②但是以目标类为父类,创建出一个功能增强的子类
    ③Spring中AOP的实现原理就是用的代理模式–>动态代理+Cglib代理
    (如果目标类实现了接口,Spring底层实现AOP就调用动态代理,否则就调用Cglib代理)
/**
 * 
 * 1.目标类UserDaoImpl:执行核心功能,CRUD.
 *
 * 在进行增删改的时候,自动为这3个方法添加事务功能,查询方法除外
 * 事务的具体实现逻辑:
 * ① 开启事务;
 * ② 执行核心功能代码;
 * ③ 提交事务.
 *
 * 
 */
public class UserDaoImpl {

    public void insert(){
        //System.out.println("开启事务...");
        System.out.println("添加用户");
        //System.out.println("提交事务...");
    }

    public void delete(){
        //System.out.println("开启事务...");
        System.out.println("删除用户");
        //System.out.println("提交事务...");
    }

    public void update(){
        System.out.println("修改用户");
    }

    public void find(){
        System.out.println("查询用户");
    }
}
/**
 * 2.代理类:执行辅助性的额外功能.
 */
public class Transaction {

    public void beginTransaction(){
        System.out.println("开启事务了...");
    }

    public void commit(){
        System.out.println("提交事务了...");
    }
}

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 *3.Cglib代理类
 *
 */
public class DaoInterceptor implements MethodInterceptor {

    private Object target;//目标类--->只能进行crud;
    private Transaction transaction;//代理类--->只有事务的代码

    public DaoInterceptor(Object target, Transaction transaction) {
        this.target = target;
        this.transaction = transaction;
    }

    //创建出一个Cglib代理对象:执行带有事务功能的crud任务.也就是该对象把事务与crud合二为一了!
    //proxy=target+transaction
    public Object createProxyInstance() {
        //Proxy.newProxyInstance(classloader,interfaces,invocationHandler);
      
        //Enhancer增强者:给目标类增强,添加事务功能
        Enhancer enhancer = new Enhancer();

        //为什么要获取目标类的字节码?
        Class<?> clazz = this.target.getClass();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setInterfaces(clazz.getInterfaces());
        //设置增强的回调函数
        enhancer.setCallback(this);
        //指定目标类,作为父类,以此为模板,创建出一个"儿子"出来
        enhancer.setSuperclass(clazz);

        //创建出一个增强目标类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        String methodName = method.getName();
        System.out.println("methodName=" + methodName);

        if ("find".equals(methodName)) {
            //直接执行find方法,不做额外操作
            method.invoke(target, args);
        } else {
            //对 增删改 进行了 增强!
            transaction.beginTransaction();

            method.invoke(target, args);

            transaction.commit();
        }

        return null;
    }

}


/**
 *Cglib代理测试类
 */
public class CglibTest {

    public static void main(String[] args) {
        UserDaoImpl target = new UserDaoImpl();
        Transaction transaction = new Transaction();
        DaoInterceptor interceptor = new DaoInterceptor(target, transaction);
        //一定要转换成父类类型!
        UserDaoImpl proxyInstance = (UserDaoImpl) interceptor.createProxyInstance();
        //proxyInstance.insert();
          proxyInstance.find();
    }
}

二、单例模式

  • 特点:
    • 1.单例类只能有一个实例。
    • 2.单例类必须自己创建自己的唯一实例。
    • 3.对外提供一种访问其唯一对象的方式。
  • 意图:保证一个类仅有一个实例,并提供一个全局访问点。
  • 主要解决:一个全局使用的类,频繁的创建和销毁。(例:一个班级只有一个班主任)

1.非线程安全懒汉式

  • 是否延迟初始化:是
public class Singleton{
    private static Singleton instance;
    private Singleton(){}//私有化的构造函数
    //对外提供方法获取唯一实例
    public static Singleton getInstance(){
     if(instance==null){
        instance = new Singleton();   
     }
     return instance;
    }    
}

2.线程安全懒汉式

  • 是否延迟初始化:是
  • 优点:第一次调用才初始化,避免内存浪费。
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

3.饿汉式(线程安全的):一般情况下建议使用

  • 是否延迟初始化:否
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){};
    public static Singleton getInstance(){
      return instance;
    }

}

4.双重校验锁(DCL,即 double-checked locking,线程安全的)

  • JDK 版本:JDK1.5 起

  • 是否 延迟初始化:是

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;  
    }  
}

5.静态内部类(线程安全)

  • 是否延迟初始化:是
public class Singleton{
     private static class SingletonHolder{
       private static final Singleton INSTANCE = new Singleton();     
     }
     private Singleton(){}
     public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
     }
}

6.枚举(线程安全)

  • JDK版本:JDK1.5起
  • 是否延迟初始化:否
*枚举单例演示*

class User{
    private Integer no ;
    private Integer age ;
    private String name ;
    //构造函数私有化
    private User(){}

    static enum Single{
        SINGLE;
        private User user;
        private Single(){
            this.user = new User();
        }
    }

    //对外提供获取唯一实例方法
    public static User instance(){
        return Single.SINGLE.user;
    }

    public Integer getNo() {
        return no;
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("User{");
        sb.append("no=").append(no);
        sb.append(", age=").append(age);
        sb.append(", name='").append(name).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

class XXX{
    public static void main(String[] args) {
        User instance = User.instance();
        System.out.println("instance = " + instance);
        User instance1 = User.instance();
        System.out.println("comparator = " + (instance1==instance));//true
    }
}

三、简单工厂,工厂方法,抽象工厂

部分引用地址: https://blog.csdn.net/TesuZer/article/details/89415055

1.简单工厂模式

角色组成

  • 工厂类:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。如例子中的Driver类。
  • 抽象产品:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。如例中的Car接口。
  • 具体产品: 工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。
    JAVA后端面试《设计模式》_第1张图片

简单工厂模式例1:

//简单工厂(Car为接口,Benz类和Bmw类均实现Car接口) 
class Driver{  
    public static Car createCar(String car){  
        Car c = null;  
        if("Benz".equalsIgnoreCase(car))  
            c = new Benz();  
        else if("Bmw".equalsIgnoreCase(car))  
            c = new Bmw();  
        return c;  
    }  
}  

简单工厂模式例2:
JAVA后端面试《设计模式》_第2张图片

2.工厂方法模式

角色组成

  • 抽象工厂:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
  • 具体工厂:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
  • 抽象产品:它是具体产品继承的父类或者是实现的接口。
  • 具体产品:具体工厂角色所创建的对象就是此角色的实例
    JAVA后端面试《设计模式》_第3张图片

工厂方法模式例1:

   //抽象工厂  
   abstract class Driver{  
       public abstract Car createCar(String car) throws Exception;  
   }  
   //具体工厂(每个具体工厂负责一个具体产品)  
   class BenzDriver extends Driver{  
       public Car createCar(String car) throws Exception {  
           return new Benz();  
       }  
   }  
   class BmwDriver extends Driver{  
       public Car createCar(String car) throws Exception {  
           return new Bmw();  
       }  
   } 

工厂方法模式例2:
JAVA后端面试《设计模式》_第4张图片

3. 抽象工厂模式

角色组成

  • 抽象工厂:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
  • 具体工厂:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
  • 抽象产品:它是具体产品继承的父类或者是实现的接口。
  • 具体产品:具体工厂角色所创建的对象就是此角色的实例。
    JAVA后端面试《设计模式》_第5张图片

抽象工厂模式(例子1)

  • BmwCar和BenzCar就是两个产品树(产品层次结构);BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。同理BmwBussinessCar和BenzBusinessCar也是一个产品族。
	 //抽象工厂
    abstract class Driver3{  
        public abstract BenzCar createBenzCar(String car) throws Exception;  
          
        public abstract BmwCar createBmwCar(String car) throws Exception;  
          
        public abstract AudiCar createAudiCar(String car) throws Exception;  
    }  
    //一个具体工厂生产跑车系汽车 
    class SportDriver extends Driver3{  
        public BenzCar createBenzCar(String car) throws Exception {  
            return new BenzSportCar();  
        }  
        public BmwCar createBmwCar(String car) throws Exception {  
            return new BmwSportCar();  
        }  
        public AudiCar createAudiCar(String car) throws Exception {  
            return new AudiSportCar();  
        }  
    }  
    //一个具体工厂生产商务系汽车
    class BusinessDriver extends Driver3{  
        public BenzCar createBenzCar(String car) throws Exception {  
            return new BenzBusinessCar();  
        }  
        public BmwCar createBmwCar(String car) throws Exception {  
            return new BmwBusinessCar();  
        }  
        public AudiCar createAudiCar(String car) throws Exception {  
            return new AudiBusinessCar();  
        }  
    }  


抽象工厂模式(例子2)

  • 抽象工厂模式也就是不仅生产鼠标,同时生产键盘。
  • 也就是 PC 厂商是个父类,有生产鼠标,生产键盘两个接口。
  • 戴尔工厂,惠普工厂继承它,可以分别生产戴尔鼠标+戴尔键盘,和惠普鼠标+惠普键盘。
  • 创建工厂时,由戴尔工厂创建。
  • 后续工厂.生产鼠标()则生产戴尔鼠标,工厂.生产键盘()则生产戴尔键盘。
    JAVA后端面试《设计模式》_第6张图片
    JAVA后端面试《设计模式》_第7张图片
    JAVA后端面试《设计模式》_第8张图片

4.工厂方法模式和抽象工厂模式的区别?

  • ①抽象产品:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
  • ②具体工厂类能创建的具体产品类型:工厂方法模式的具体工厂类只能创建一种具体产品类的实例,而抽象工厂模式可以创建多种具体产品类的实例。

你可能感兴趣的:(JAVA后端面试)