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

}

}

}

目录
相关文章
|
10天前
|
设计模式 Java
Java中的设计模式及其应用场景解析
Java中的设计模式及其应用场景解析
|
7天前
|
设计模式 测试技术 Python
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
【7月更文挑战第10天】Page Object Model (POM)是Selenium自动化测试中的设计模式,用于提高代码的可读性和维护性。POM将每个页面表示为一个类,封装元素定位和交互操作,使得测试脚本与页面元素分离。当页面元素改变时,只需更新对应页面类,减少了脚本的重复工作和维护复杂度,有利于团队协作。POM通过创建页面对象,管理页面元素集合,将业务逻辑与元素定位解耦合,增强了代码的复用性。示例展示了不使用POM时,脚本直接混杂了元素定位和业务逻辑,而POM则能解决这一问题。
25 6
|
5天前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
【7月更文挑战第12天】在本文中,作者宏哥介绍了如何在不使用PageFactory的情况下,用Java和Selenium实现Page Object Model (POM)。文章通过一个百度首页登录的实战例子来说明。首先,创建了一个名为`BaiduHomePage1`的页面对象类,其中包含了页面元素的定位和相关操作方法。接着,创建了测试类`TestWithPOM1`,在测试类中初始化WebDriver,设置驱动路径,最大化窗口,并调用页面对象类的方法进行登录操作。这样,测试脚本保持简洁,遵循了POM模式的高可读性和可维护性原则。
13 2
|
8天前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
24 1
|
9天前
|
设计模式 Java 数据库连接
Java中的设计模式在实际项目中的应用
Java中的设计模式在实际项目中的应用
|
17天前
|
设计模式 Java
设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用
|
16天前
|
设计模式 消息中间件 负载均衡
实现可扩展和可靠的分布式系统的Java设计模式
实现可扩展和可靠的分布式系统的Java设计模式
|
17天前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式
|
15天前
|
设计模式 Java 数据安全/隐私保护
Java中的设计模式:从入门到精通
Java中的设计模式:从入门到精通
|
16天前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式