目录

原型模式

模式结构

实现方法

实例

浅克隆与深克隆的区别

结构型模式

代理模式

模式结构

静态代理

JDK动态代理


原型模式

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

模式结构
  • 抽象原型类:规定了具体原型对象必须实现的clone()方法。

  • 具体原型类(Realizetype):实现抽象原型类的clone()方法,它是可被复制的对象。

  • 访问类:使用具体原型类中的clone()方法来复制新的对象。

实现方法

原型模式的克隆分为浅克隆和深克隆。

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。、

Java中的Object类中提供了clone()方法来实现浅克隆

具体原型类:

public class Realizetype implements Cloneable {
    
    @Override
    public Realizetype() {
        System.out.println(“具体的原型对象创建完成!”)
        
    }
    
    @Override 
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!")
        return (Realizetype) super.clone();
    }

测试访问类:

public class PrototypeTest {
    public static void main(String[] args) throws cloneNotSupportedException {
        Realizetype r1 = new Realizetype();
        Realizetype r2 = ri.clone();
        
        System.out.println(“对象r1和r2是同一个对象?” + (r1 == r2))
    }
}

实例

三好学生奖状(浅克隆)

lic class Citation implements Cloneable {
     private String name;
     
     public void setName(String name) {
         this.name = name;
     }
     
     public String getName() {
         return (this.name);
     }
     
     public void show() {
         System.out.println(name + "同学,在2024学年第一学期中表现优秀,被评为三好学生,特发此状!");
     }
     
     @Override
     public Citation clone() throws CloneNotSupportedException {
         return (Citation) super.clone();
     }
}

//测试访问类
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建原型对象
        Citation c1 = new Citation();
        //克隆奖状对象
        Citation c2 = citation.clone();
        
        c1.setName("张三");
        c2.setName("李四");
        
        //调用 Show()方法展示
        c1.show();
        c2.show();
    }
}

浅克隆的使用场景

  • 对象的创建非常复杂,可以使用原型模式快捷的创建对象。

  • 性能和安全要求比较高。

实例扩展(深克隆)

将上面的"三好学生"奖状中Citation类的name属性修改为Student类型的属性

public class Student {
    
    //学生的姓名
    private String name;
    
    public String getName() {
        return name;
    }
    
    @Override
    public String toString() {
        return "Student{" + 
               "name='" + name + '\'' + 
               '}'
    }
}

public class Citation implements Cloneable {

     private Student stu;
     
     public Student getStu() {
         return stu;
     }
     
     public void setStu(Student stu) {
         this.stu = stu
     }
     
     public void show() {
         System.out.println(stu.getName() + "同学,在2024学年第一学期中表现优秀,被评为三好学生,特发此状!");
     }
     
     @Override
     public Citation clone() throws CloneNotSupportedException {
         return (Citation) super.clone();
     }
}

public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建原型对象
        Citation c1 = new Citation();
        //创建张三学生对象
        Student stu = new Student();
        stu.setName("张三");
        citation.setStu(stu);
        
        //克隆奖状对象
        Citation citation1 = citation.clone();
        Student stu1 = citation1.getStu();
        stu1.setName("李四");
        
        //调用 Show()方法展示
        citation.show();
        citation1.show();
    }
}

stu对象和stu1对象是同一个对象,就会产生将stu1对象中name属性值改为"李四",两个Citation(奖状)对象中显示的都是李四。这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。这种情况需要使用深克隆,而进行深克隆需要使用对象流。

public class CitationTest {
    public static void main(String[] args) throws Exception {
        //创建原型对象
        Citation citation = new Citation();
        //创建张三学生对象
        Student stu = new Student();
        stu.setName("张三");
        citation.setStu(stu);
        
        //创建对象输出流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/robin/a.txt"));
        //写对象
        oos.writeObject(citation);
        //释放资源
        oos.close();
        
        //创建对象输入流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/robin/a.txt"));
        //读取对象
        Citation citation1 = (Citation) ois.readObject();
        //释放数据
        ois.close();
        Student stu1 = citation2.getStu();
        stu1.setName("李四");
        
        citation.show();
        citation1.show();
        
    }
}
浅克隆与深克隆的区别
  • 浅克隆:克隆出来的新对象与原始对象共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性也会被修改。在Java中,可以通过实现Cloneable接口和重写clone()方法来实现浅克隆。

  • 深克隆:克隆出来的新对象与原始对象不共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是新的对象,而不是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性不会被修改。

结构型模式

结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。

由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。

结构型模式分为:代理模式,适配器模式,装饰者模式,桥接模式,外观模式,组合模式,享元模式

代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。

模式结构
  • 抽象主题类(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

  • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

静态代理

火车票售卖模型

//买火车票的接口
public interface SellTickets {
    
    void sell();
}

//火车站类
public class TrainStation implements SellTickets {
   
   public void sell() {
       System.out.println("火车站卖票");
   }
}

//代售点类
public class ProxyPoint implements SellTickets {

    //声明火车站类对象
    private TrainStation trainStation = new TrainStation();

    public void sell() {
        System.out.println("代理点收取一些服务费用");
        trainStatino.sell();
        
    }
}

public class Client {
    public static void main(String[] args) {
        //创建代售点对象
        ProxyPoint proxyPoint = new ProxyPoint();
        //调用方法进行买票
        proxyPoint.sell();
    }
}
JDK动态代理(Java动态代理)

Java动态代理是Java提供的一种机制,它允许程序在运行时创建代理类,并在不改变代理类代码的情况下为方法调用添加额外的功能,从实现自定义类加载器到构建灵活高效的框架。动态代理的核心是 java.lang.reflect 包中的Proxy类和 InvocationHandler 接口。

动态代理的基本概念

动态代理是一种在运行时创建代理对象的技术,可以在不修改原始对象代码的情况下为方法调用添加额外的行为。Java提供了两种代理方式:

  • 接口代理(基于java.lang.reflect.Proxy):用于代理实现了接口的类。

  • 类代理(基于第三方库如CGLIB):用于代理没有实现接口的类。

接口代理

实现Java动态代理需要以下几个步骤:

  • 定义一个接口:首先,你需要定义一个接口,该接口声明了你想要代理的方法。

  • 实现InvocationHandler接口:创建一个实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中,你可以定义在调用目标对象的方法前后需要执行的逻辑。

  • 创建动态代理类:使用Proxy类的newProxyInstance方法在运行时动态创建代理对象。这个方法需要三个参数:使用的ClassLoader,需要实现的接口数组,以及用来处理接口方法调用的InvocationHandler实例。

  • 使用代理对象:通过代理对象调用方法时,实际会委托给InvocationHandler的invoke方法处理。

InvocationHandler接口

InvocationHandler是动态代理的核心接口。它定义了invoke方法,代理对象的每个方法调用都会被重定向到这个invoke方法。

public interface InvocationHandler {
    Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}

参数:

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

  • method:是在代理实例上调用的接口方法对应的Method实例。

  • args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null.

其返回值为,调用代理实例上的方法的返回值。

Proxy类

Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类。

代理类具有以下属性:

  • 代理类的名称以"$Proxy"开头,后面跟着一个数字序号。

  • 代理类继承了Proxy类。

  • 代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。

  • 每个代理类都有一个公共构造函数,它接受一个参数,即接口InvacationHandler的实现,用于设置代理实例的调用处理器。

Proxy提供了两个静态方法,用于获取代理对象。

getProxyClass

用于获取代理类的Class对象,再通过调用构造函数创建代理实例。

public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>...interfances)
        throws IllegalArgumentException

参数:

  • loader:为类加载器。

  • interfaces:为接口Class对象数组。

返回值为动态代理类的Class对象。

newProxyInstance

用于创建一个代理实例。

public static Object newProxyInstance(ClassLoader loader,
                                           Class<?>[] interfances,
                                           InvocationHandler h)
        throws IllegalArgumentException

参数

  • loader:为类加载器。

  • interfaces:为接口的Class对象数组。

  • h:指定的调用处理器。

返回值为指定接口的代理类的实例

Proxy类主要用来获取动态代理对象,InvocationHandler接口主要用于方法调用的约束与增强。

代码示例

创建目标接口及实现类

public interface Foo {
    String ping(String name);
}

//Foo接口的实现类RealFoo
public class RealFoo implements Foo {
    @Override
    public String ping(String name) {
        System.out.println("ping");
        return "pong";
    }
}

创建一个InvocationHandler

创建一个InvocationHandler接口的实现类MyInvocationHandler.该类的构造方法参数为要代理的目标对象。

public class MyInvocationHandler implements InvocationHandler {

    // 目标对象
    private final Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy - " + proxy.getClass());
        System.out.println("method - " + method);
        System.out.println("args - " + Arrays.toString(args));
        return method.invoke(target, args);
    }
}
综合案例

将上述SellTickets类TrainStation类放入jdk_proxy文件夹内

//新建类
//获取代理对象的工厂类——代理类也实现了对应的接口
public class ProxyFactory {

    //声明目标对象
    private TrainStation station = new TrainStation();
    
    //获取代理对象的方法
    public SellTickets getProxyObject() {
        //返回代理对象
        /*
           ClassLoader loader : 类加载器,用于加载代理类,可以通过目标对象获取类加载器
           Class<?>[] interfaces : 代理类实现的接口的字节码对象。
           InvocationHandler h : 代理对象的调用处理程序。
        */
        SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance(
               station.getClass().getClassLoader(),
               station.getClass().getInterfaces(),
               new InvocationHandler() {
                   //jvm内存结构
                   /*
                        Object proxy : 代理对象,和proxyObject对象是同一个对象,在invoke方法中基本不用
                        Method method : 对接口中的方法进行封装的method对象。
                        Object[] args : 调用方法的实际参数
                        
                        返回值 : 方法的返回值——调用sell()方法的返回值,没有则返回null,有则由invoke返回具体的值
                   */
                   public Object invoke(Object proxy,Method method,Object[] args) throw Throwable{
                       System.out.println("invoke方法执行了");
                       System.out.println("代售点收取一定的服务费用(jdk动态代理)");
                       //执行目标对象(火车站)的方法
                       Object obj = method.invoke(station.args);
                       return obj;
                   }
               }
        
        );
        return proxyObject;
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //获取代理对象
        //1.创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //2.使用factory对象的方法获取代理对象
        SellTickets proxyObject = factory.getProxyObject();
        //3.调用卖调用的方法
        proxyObject.sell();
    }
}
动态代理的优势
  • 代码复用:可以通过动态代理实现常见的横切关注点(如日志记录、事务管理、性能监控等),减少重复代码。

  • 解耦:通过代理对象和目标对象分离,降低模块之间的耦合度。

  • 灵活性:可以在运行时动态地改变代理行为,增强程序的灵活性和扩展性。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐