【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);
        }
    }
}
  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值