Java23种设计模式

1、单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

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

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

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

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

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

优点:

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

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

  • 饿汉式
package com.liuxiang.Singleton;

//一上来就创建对象
public class TestSingleton {

    //饿汉式 构造器私有 避免别人创建对象
    private TestSingleton(){

    }
    // 饿汉式可能会造成浪费空间
    private final static TestSingleton singleton = new TestSingleton();

    public static TestSingleton getInstance(){
        return singleton;
    }
    
}
  • 懒汉式
package com.liuxiang.Singleton;

public class TestSingleton {

    //构造器私有 避免别人创建对象
    private TestSingleton(){

    }
    // 懒汉式可能会造成浪费空间 volatile确保不会被指令重排
    private volatile  static TestSingleton singleton;

    public static TestSingleton getInstance(){
        if (singleton == null){
            //多线程获取 需要使用synchronized 双重检测锁模式
            synchronized (TestSingleton.class){
                if (singleton == null){
                    singleton = new TestSingleton();
                    //DCL懒汉式双重检测机制,在极端情况下还是会出现问题
                    //new一个对象在正常情况下的执行流程应该是:
                    //1.分配内存空间
                    //2.执行构造方法,初始化对象
                    //3.把这个对象指向这个空间
                    //如果出现了指令重排123 变成了132 就会出现问题
                    //所以我们应该加上volatile确保不会发生指令重排
                }
            }
        }
        return singleton;
    }

}
  • 使用枚举确保单例模式不被反射破坏
package com.liuxiang.Singleton;

import java.lang.reflect.Constructor;

public class TestSingleton {

    //构造器私有 避免别人创建对象
    private TestSingleton(){

    }
    // 懒汉式可能会造成浪费空间 volatile确保不会被指令重排
    private volatile  static TestSingleton singleton;

    public static TestSingleton getInstance(){
        if (singleton == null){
            //多线程获取 需要使用synchronized 双重检测锁模式
            synchronized (TestSingleton.class){
                if (singleton == null){
                    singleton = new TestSingleton();
                }
            }
        }
        return singleton;
    }

    //使用反射创建对象 破坏单例模式
    public static void main(String[] args) throws Exception {
        Constructor<TestSingleton> declaredConstructor = TestSingleton.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//关闭安全检测
        TestSingleton singleton1 = declaredConstructor.newInstance();//通过反射创建对象
        TestSingleton singleton2 = declaredConstructor.newInstance();//通过反射创建对象
        System.out.println(singleton1);
        System.out.println(singleton2);
    }

}
com.liuxiang.Singleton.TestSingleton@1b6d3586
com.liuxiang.Singleton.TestSingleton@4554617c

使用反射就可以绕过私有变量创建新的对象,我们查看反射的源码可以发现:反射机制不能操作枚举类

2、工厂模式

  1. 简单工厂模式

public interface Car {
    String name();
}

public class WuLing implements Car{
    @Override
    public String name() {
        return "五菱宏光!";
    }
}

public class Tesla implements Car{
    @Override
    public String name() {
        return  "特斯拉!";
    }
}

public class CarFactory {
    //方法一
    public static Car getCar(String car){
        if (car.equals("五菱宏光")){
            return new WuLing();
        }else if (car.equals("特斯拉")){
            return new Tesla();
        }else {
            return null;
        }
    }

    //方法二
    public static Car getWuLing(){
        return new WuLing();
    }
    public static Car getTesla(){
        return new Tesla();
    }
}

public class Customer {
    public static void main(String[] args) {
        Car car1 = CarFactory.getWuLing();
        Car car2 = CarFactory.getTesla();

        System.out.println(car1.name());
        System.out.println(car2.name());
    }
}
  1. 工厂方法模式

public interface Car {
    String name();
}

public class Tesla implements Car {
    @Override
    public String name() {
        return  "特斯拉!";
    }
}

public class WuLing implements Car {
    @Override
    public String name() {
        return "五菱宏光!";
    }
}

public interface CarFactory {
    Car getCar();
}

public class TeslaFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Tesla();
    }
}

public class WuLingFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new WuLing();
    }
}

public class Customer {
    public static void main(String[] args) {
        Car car1 = new WuLingFactory().getCar();
        Car car2 = new TeslaFactory().getCar();

        System.out.println(car1.name());
        System.out.println(car2.name());
    }
}

3、抽象工厂模式

public interface PhoneProduct {
    void start();
    void shutdown();
    void call();
    void sms();
}

public class HuaweiPhone implements PhoneProduct {
    @Override
    public void start() {
        System.out.println("开启华为手机");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为手机");
    }

    @Override
    public void call() {
        System.out.println("华为打电话");
    }

    @Override
    public void sms() {
        System.out.println("华为发短信");
    }
}

public class XiaomiPhone implements PhoneProduct {
    @Override
    public void start() {
        System.out.println("开启小米手机");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭小米手机");
    }

    @Override
    public void call() {
        System.out.println("小米打电话");
    }

    @Override
    public void sms() {
        System.out.println("小米发短信");
    }
}

public interface RouteProduct {
    void start();
    void shutdown();
    void Wifi();
    void setting();
}

public class HuaweiRouter implements RouteProduct{
    @Override
    public void start() {
        System.out.println("启动华为路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为路由器");
    }

    @Override
    public void Wifi() {
        System.out.println("打开华为Wifi");
    }

    @Override
    public void setting() {
        System.out.println("华为设置");
    }
}

public class XiaomiRouter implements RouteProduct{
    @Override
    public void start() {
        System.out.println("启动小米路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭小米路由器");
    }

    @Override
    public void Wifi() {
        System.out.println("打开小米Wifi");
    }

    @Override
    public void setting() {
        System.out.println("小米设置");
    }
}

//抽象产品工厂
public interface ProductFactory {
    //生产手机
    PhoneProduct phoneProduct();
    //生成路由器
    RouteProduct routerProduct();
}

public class HuaweiFactory implements ProductFactory{
    @Override
    public PhoneProduct phoneProduct() {
        return new HuaweiPhone();
    }

    @Override
    public RouteProduct routerProduct() {
        return new HuaweiRouter();
    }
}

public class XiaomiFactory implements ProductFactory{
    @Override
    public PhoneProduct phoneProduct() {
        return new XiaomiPhone();
    }

    @Override
    public RouteProduct routerProduct() {
        return new XiaomiRouter();
    }
}

public class Customer {
    public static void main(String[] args) {
        System.out.println("==================小米系列产品=================");
        XiaomiFactory xiaomiFactory = new XiaomiFactory();

        PhoneProduct phoneProduct = xiaomiFactory.phoneProduct();
        phoneProduct.call();
        phoneProduct.sms();

        RouteProduct routeProduct = xiaomiFactory.routerProduct();
        routeProduct.start();
        routeProduct.shutdown();

        System.out.println("==================华为系列产品=================");
        HuaweiFactory huaweiFactory = new HuaweiFactory();

        PhoneProduct phoneProduct1 = huaweiFactory.phoneProduct();
        phoneProduct1.call();
        phoneProduct1.sms();

        RouteProduct routeProduct1 = huaweiFactory.routerProduct();
        routeProduct1.start();
        routeProduct1.shutdown();

    }
}
==================小米系列产品=================
小米打电话
小米发短信
启动小米路由器
关闭小米路由器
==================华为系列产品=================
华为打电话
华为发短信
启动华为路由器
关闭华为路由器

4、代理模式

代理模式是 Spring AOP 的底层实现

代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

4.1、静态代理模式

抽象角色 : 一般使用接口或者抽象类来实现

真实角色 : 被代理的角色

代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作。

客户 : 使用代理角色来进行一些操作。

  • 代码实现
package com.liuxiang.demo;

// 抽象角色
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
package com.liuxiang.demo;

// 真实对象
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}
package com.liuxiang.demo;

// 客户
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        userService.add();
    }
}

  • 此时要增加一个日志功能,我们不可以去修改源代码(避免把源代码改崩),因此需要代理角色。
package com.liuxiang.demo;

// 代理角色
public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    // 日志方法
    public void log(String msg){
        System.out.print("使用了"+msg+"方法:");
    }
}
package com.liuxiang.demo;

// 客户
public class Client {
    public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(new UserServiceImpl());
        userServiceProxy.add();
    }
}

  • 静态代理的好处:
  1. 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
  2. 公共的业务由代理来完成,实现了业务的分工。
  3. 公共业务发生扩展时变得更加集中和方便。
  • 静态代理的缺点:
  1. 类多了,多了代理类,工作量变大了,开发效率降低。

4.2、动态代理

动态代理的角色和静态代理的一样

动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的

动态代理分为两类:

  1. 基于接口动态代理 ----- JDK动态代理(原则:真实对象和代理对象实现相同的接口)
  2. 基于类的动态代理 ----- cglib(原则:代理对象继承真实对象)

现在用的比较多的是 javasist 来生成动态代理

JDK的动态代理类位于 java.lang.reflect 包下,需要了解两个类:

  1. Proxy:代理
  2. InvocationHandler:调用处理程序

Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

//生成代理类
public Object getProxy(){
   return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                 rent.getClass().getInterfaces(),this);
}

InvocationHandler是由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。

当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

该接口中仅定义了一个方法:

Object invoke(Object proxy, 方法 method, Object[] args);

参数详解:

proxy:调用该方法的代理实例。

method:所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。

args:包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如 java.lang.Integer 或 java.lang.Boolean。

  • 代码实现

动态代理步骤:

  1. 创建一个实现接口 InvocationHandler 的类,它必须实现invoke方法
  2. 创建被代理的类以及接口
  3. 通过Proxy的静态方法 new ProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
  4. 通过代理调用方法
package com.liuxiang.demo1;

//抽象角色:租房
public interface Rent {
    public void rent();
}
package com.liuxiang.demo1;

// 真实角色
public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("租房子");
    }
}
package com.liuxiang.demo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果  proxy :代理类 method : 代理类的调用处理程序的方法对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("找客户");
        //执行目标方法获得结果 核心:本质利用反射实现!
        Object result = method.invoke(rent, args);
        System.out.println("结束订单");
        return result;
    }
}
package com.liuxiang.demo1;

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        // 代理实例的调用处理程序
        ProxyInvocationHandler pro = new ProxyInvocationHandler();
        pro.setRent(host);  //将真实角色放置进去!
        Rent proxy = (Rent) pro.getProxy(); //动态生成对应的代理类!
        proxy.rent();
    }
}

  • 动态代理的优点
  1. 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
  2. 公共的业务由代理来完成,实现了业务的分工。
  3. 公共业务发生扩展时变得更加集中和方便。
  4. 一个动态代理,一般代理某一类业务。
  5. 一个动态代理可以代理多个类,代理的是接口!
posted @ 2021-08-20 20:17  有一个大佬梦  阅读(1498)  评论(0)    收藏  举报