Java设计模式

简介: Java设计模式

设计模式

单例singleton

应用程序在使用过程中每一次获取的实例都是同一个,主要是解决内存空间浪费的问题。有些对象全局公用一个即可

单例的的要求

1.构造方法私有

2.提供静态方法能够获得其实例

3.提供一个自身类型的静态成员变量

单例的实现

线程安全的立即加载–>静态成员变量实例化

public class MySingleton {
    // 静态成员变量直接实例化是什么时候执行的 → 类加载的时候执行的
    // 就是在第一次返回实例之前
    private static MySingleton mySingleton = new MySingleton();
    private MySingleton() {}
    public static MySingleton getInstance() {
        //类中的静态方法里能否使用其私有的构造方法 → 可以
        // 实例化的代码只执行一次,第一次返回实例之前
        //MySingleton mySingleton = new MySingleton();
        return mySingleton;
    }
}

线程安全的立即加载–>静态代码块

public class MySingleton2 {
    // 静态成员变量直接实例化是什么时候执行的 → 类加载的时候执行的
    // 就是在第一次返回实例之前
    private static MySingleton2 mySingleton;
    // 静态代码块也是类加载的时候执行 --> 只执行一次
    static {
        mySingleton = new MySingleton2();
    }
    private MySingleton2() {
    }
    public static MySingleton2 getInstance() {
        //类中的静态方法里能否使用其私有的构造方法 → 可以
        // 实例化的代码只执行一次,第一次返回实例之前
        //MySingleton mySingleton = new MySingleton();
        return mySingleton;
    }
}

静态成员变量和静态代码块都是在类加载的时候立即加载,无论如何都只会加载一次,因此可以保证线程安全

线程安全的懒加载–>加锁

public class MySingleton4 {
    // 就是在第一次返回实例之前
    private static MySingleton4 mySingleton;
    private MySingleton4() {
    }
    public static synchronized MySingleton4 getInstance() {
        //类中的静态方法里能否使用其私有的构造方法 → 可以
        // 实例化的代码只执行一次,第一次返回实例之前
        //MySingleton mySingleton = new MySingleton();
        //我们要在第一次调用方法的时候完成实例化
        // 我们如何知道这是第一次调用这个方法 → 有什么特征
        if (mySingleton == null) { //它没有实例化 --> 第一次调用方法
            mySingleton = new MySingleton4();
        }
        return mySingleton;
    }
}

线程安全的懒加载 --> 静态内部类

解决效率问题就需要不加锁,而保证线程安全需要使用静态的成员变量,因此可以使用静态内部类来完成实例化

public class MySingleton6 {
    private MySingleton6(){}
    public static MySingleton6 getInstance() {
        // 懒加载 → 实例化的代码要在getInstance方法里面
        // 内部类提供一个方法让外部类能够获得其实例
        return Inner.getInnerInstance();
    }
    static class Inner{
        private static MySingleton6 mySingleton6 = new MySingleton6();
        /*static {
            mySingleton6 = new MySingleton6();
        }*/
        public static MySingleton6 getInnerInstance() {
            return mySingleton6;
        }
    }
}

工厂

工厂类一般类名中有Factory

工厂的作用是隐藏实现的细节,获得实例的时候不需要关心实例化的细节过程

简单工厂

  • 根据传入的参数生成不同的实例对象
public class Animal2Factory {
    public Animal2 create(String name) {
        if ("pig".equals(name)) {
            return new Pig();
        } else if ("sheep".equals(name)) {
            return new Sheep();
        } else if ("chicken".equals(name)) {
            return new Chicken();
        } //开闭原则 
        return null;
    }
}

工厂方法

将工厂定义为接口,如果需要返回新的类型的实例,去新增一个实现了工厂类接口的实体类

public interface AnimalFactory {
    //接口中提供规范,提供一个返回值为Animal的方法
    public Animal create();
}
/**
 * @author stone
 * @date 2021/12/23 11:33
 */
public class PigFactory implements AnimalFactory{
    @Override
    public Animal create() {
        return new Pig();
    }
}

工厂的分类

根据工厂中的生产方法是静态方法还非静态方法来分类

静态工厂里的生产方法是静态方法,实例工厂里的方法是非静态方法

public class MyTest {
    @Test
    public void mytest1() {
        // 要先创建一个工厂的实例 → 实例工厂
        Animal2Factory factory = new Animal2Factory();
        Animal2 chicken = factory.create("chicken");
        System.out.println(chicken instanceof Chicken); //true
    }
    @Test
    public void mytest2() {
        // 可以直接调用静态方法 → 静态工厂
        Animal pig = AnimalFactory.create("pig");
    }
}

建造者

建造者的侧重点在于提供方法,让你设计细节

public class MoneyBuilder {
    // 成员变量实例化,当前类执行实例化调用构造方法的时候执行
    // 当我们去new了一个moneyBuilder的时候,同时new了一个money
    Money money = new Money(); // 在set方法和build方法之前执行的
    // 如果使用的是同一个builder就意味着是对同一个money做修饰
    public void setMoneyType(String type) {
        money.setType(type);
    }
    public void setMoneyFaceValue(int value) {
        money.setFaceValue(value);
    }
    public Money build() {
        // 要在build方法之前给实例设置参数
        // 实例化过程要在build方法之前
        return money;
    }
}
@Data
public class Money {
    String type;
    int faceValue;
}

代理

匿名内部类:定义一个类,并获得这个类的实例,一般用于直接获取抽象类或者接口

new Interface/AbstractClass(){
    //实现里面的方法
}

委托方:需要执行实际方法的类

代理方:帮助委托方做本身要做的事,除此之外还进行了额外的增强

代理的主要作用就是对多个已有的功能做相同的扩展

将需要反复执行的繁琐的内容提取出来,对一个类中所有的方法进行增强

代理的分类

  • 静态代理
  • 动态代理
  • JDK动态代理
  • CGlib动态代理

静态代理:代理类需要我们自己写;

动态代理:代理类是动态生成的,不需要我们自己去写

静态代理

儿子给父亲买早餐

委托方:Father

代理方:Son

谁的方法需要被增强,谁就是委托方

代理类中需要包含委托类的成员变量,要么维护一个对象,要么通过继承

代理类中的方法与委托类中定义的方法需要相同:修饰符、返回值、方法名、参数

所以一般使用接口来制定规范

public class Father {
    public void buyBreakFast() {
        System.out.println("早餐");
    }
    public void buyLunch() {
        System.out.println("中餐");
    }
    public void buySupper() {
        System.out.println("晚餐");
    }
}
public class Son {
//通过维护一个为委托类的对象
    Father f = new Father();
    public void buyBreakFast() {
        f.buyBreakFast();
        Stronger();
    }
    public void buyLunch() {
        f.buyLunch();
        Stronger();
    }
    public void buySupper() {
        f.buySupper();
        Stronger();
    }
    public void Stronger(){
        System.out.println("加个蛋");
    }
}

动态代理

代理类的方法:

1.委托类的方法

2.增强的方法

动态代理通过InvocationHandler中的invoke方法来调用委托类中的方法,并可以在其中实现自定义的增强

JDK中的动态代理

获得代理对象的步骤:

1.类加载器

2.委托类所实现的接口数组–>委托类必须要实现接口–>代理类实现了和委托类相同的接口–>通过接口保证方法的规范性

3.获取InvocationHandler的实例

通过实现类来获取实例

匿名内部类

4.接收代理对象:使用委托类实现的接口来接收,因为代理类同样也实现了该接口

自定义的Invocation

public class CustomHandler implements InvocationHandler {
    Father f = new Father();
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(f,args);
        System.out.println("来三斤牛肉");
        return null;
    }
}
@Test
public void myTest1(){
    FatherInterface f = (FatherInterface) Proxy.newProxyInstance(Father.class.getClassLoader(), Father.class.getInterfaces(),
            new CustomHandler());
    f.buyBreakFast();
    f.buyLunch();
    f.buySupper();
    f.buyFood("吃鸡");
    f.buyFoods("可乐","雪碧");
}

使用代理实例化时需要3个参数:第一个是类加载器,第二个是委托类接口列表,第三个是handler

JDK中实现动态代理的过程:

package proxy.dynamic;
import java.lang.reflect.Method;
//动态代理类要和委托类实现一样的接口
//模仿jdk来实现自定义的动态代理对象
public class JdkProxy implements FatherInterface {
    Method m1;
    Method m2;
    Method m3;
    Method m4;
    Method m5;
    {
        try {
            m1 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyBreakFast");
            m2 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyLunch");
            m3 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buySupper");
            m4 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyFood",String.class);
            m5 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyFoods",String.class,String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    CustomHandler handler = new CustomHandler();
    @Override
    public void buyBreakFast() {
        try {
            handler.invoke(this,m1,null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    @Override
    public void buyLunch()   {
        try {
            handler.invoke(this,m2,null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    @Override
    public void buySupper() {
        try {
            handler.invoke(this,m3,null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    @Override
    public void buyFood(String food) {
        try {
            handler.invoke(this,m4,new Object[]{food});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    @Override
    public void buyFoods(String food1, String food2) {
        try {
            handler.invoke(this,m5,new Object[]{food1,food2});
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

在使用debug的时候要注意debug会自动调用tostring方法

CGlib动态代理

仍然是使用InvocationHandler来生成代理对象

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>
@Test
    public void test1(){
        Father f = new Father();
        FatherInterface fProxy = (FatherInterface) Enhancer.create(Father.class, new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                method.invoke(f, objects);
                System.out.println("我是增强方法");
                return null;
            }
        });
        fProxy.buyFoods("苹果","香蕉");
    }

第一个参数仍然是委托类的.class文件,第二个是InvocationHandler,使用匿名内部类来实现

在使用CGlib的使用,接收代理对象可以使用委托类本身,也可以使用接口

一般使用接口,兼容性会更好

代理类的字节码文件

jdk
// 使用main方法,执行过程中会有一个工作路径
public static void main(String[] args) {    //通知应用程序把字节码保存起来,默认是不保存 → com.sun.proxy    // ProxyGenerator   
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");}

生成的代理类

public final class $Proxy0 extends Proxy implements JingTianInterface {
    private static Method m6;
    static {
        try {
            m6 = Class.forName("com.cskaoyan.bean.JingTianInterface").getMethod("buyDoubleFood", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    public final void buyDoubleFood(String var1, String var2) throws  {
        try {
            super.h.invoke(this, m6, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }
}
cglib
public static void main(String[] args) {
    //字节码文件也是默认不保存 → value写的是路径
    // 和委托类相同的包目录
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\WorkSpace\\j36_workspace\\codes\\day03-proxy-spring\\demo4-proxy-save");
}
public class JingTian$$EnhancerByCGLIB$$75eacb68 extends JingTian implements Factory {
    private InvocationHandler CGLIB$CALLBACK_0;
    private static final Method CGLIB$buyFood$1;
    static void CGLIB$STATICHOOK1() {
        CGLIB$buyFood$1 = Class.forName("com.cskaoyan.bean.JingTian").getDeclaredMethod("buyFood", Class.forName("java.lang.String"));    
    }
  public final void buyFood(String var1) {
        try {
            InvocationHandler var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
            var10000.invoke(this, CGLIB$buyFood$1, new Object[]{var1});
        } catch (Error | RuntimeException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

java.lang.String"));

}

public final void buyFood(String var1) {

try {

InvocationHandler var10000 = this.CGLIBKaTeX parse error: Expected '}', got 'EOF' at end of input: … CGLIBBIND_CALLBACKS(this);

var10000 = this.CGLIBKaTeX parse error: Expected 'EOF', got '}' at position 25: …0; }̲ va…buyFood$1, new Object[]{var1});

} catch (Error | RuntimeException var2) {

throw var2;

} catch (Throwable var3) {

throw new UndeclaredThrowableException(var3);

}

}

}

目录
相关文章
|
2月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
42 4
|
3月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
58 0
[Java]23种设计模式
|
2月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
3月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
4月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
4月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
4月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
4月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)
|
4月前
|
设计模式 缓存 监控
Java设计模式-责任链模式(17)
Java设计模式-责任链模式(17)