Java-反射

前言

动态语言与静态语言

  • 动态语言
    • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
    • 主要动态语言有:Object-CC#JavaScriptPHPPythonErlang
  • 静态语言
    • 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如JavaCC++

Java不是动态语言,但Java可以称之为==“准动态语言”==。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候可以更加灵活!

Java反射机制

  • Reflction(反射)是被视为动态语言的关键,反射机制允许程序在执行期,借助于Reflection API取得任何类的内部信息,并能直接操作认一堆小的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

Java-反射_第1张图片

1、理解Class类并获取Class实例

1.1、Class类的理解

  1. 类的加载过程
    1. 程序经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就成为运行时的类,此运行时的类,就作为Class的一个实例
  2. 换句话说,Class的实例就对应着一个运行时的类
  3. 加载到内存中的运行时的类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时的类

哪些类型可以有Class对象?

  • class:内部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interfase:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void

1.2、获取Class实例

总共有四种方法

  • 调用运行时类的属性:.class
  • 通过运行时类的对象,调用getClass()
  • 调用Class的静态方法:forName(String classPath)
  • 使用类的加载器:Classloader
package com.tcc.test;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args){
        // 方式一:调用运行时类的属性:.class
        Class<Person> p1 = Person.class;

        // 方式二:通过运行时类的对象,调用getClass()
        Person person = new Person();
        Class<? extends Person> p2 = person.getClass();

        // 方式三:调用Class的静态方法:forName(String classPath)
        try {
            Class<?> p3 = Class.forName("com.tcc.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 方式四:使用类的加载器:Classloader
        ClassLoader classLoader = Person.class.getClassLoader();
        try {
            Class<?> p4 = classLoader.loadClass("com.tcc.test.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class Person{

}

1.3、获取特殊类型的Class

public class Test{
    public static void main(String[] args){
        Class<Object> c1 = Object.class;
        Class<Comparable> c2 = Comparable.class;
        Class<String[]> c3 = String[].class;
        Class<int[][]> c4 = int[][].class;
        Class<ElementType> c5 = ElementType.class;
        Class<Override> c6 = Override.class;
        Class<Integer> c7 = int.class;
        Class<Void> c8 = void.class;
        Class<Class> c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class<? extends int[]> c10 = a.getClass();
        Class<? extends int[]> c11 = b.getClass();
        // true 只要元素类型与维度一样,就是同一个Class
        System.out.println(c10 == c11);
    }
}

2、类的加载与ClassLoader的理解

2.1、类的加载

类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
    • 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
    • 准备:正常为类变量(static)分配内存并设置类遍历默认初始值的阶段,这些内存都将在方法区中进行分配
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • 初始化
    • 执行类构造器()方法的过程。类构造器()方法是由编译器自动收集类中所有类遍历的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

Java-反射_第2张图片

类加载器作用

  • 类加载的作用
    • 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
  • 类缓存
    • 标准的JavaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象

Java-反射_第3张图片

2.2、ClassLoader

类加载器作用是用来吧类(class)装载进内存的。JVM规范定义了如下类型的类的加载器

Java-反射_第4张图片

package com.tcc.test;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args) throws ClassNotFoundException {
        // 对应自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = Test.class.getClassLoader();
        System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

        // 调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1); // sun.misc.Launcher$ExtClassLoader@1b6d3586
        
        // 调用扩展类加载器的getParent():无法获取引导类加载器
        // 引导类加载器主要负责加载java的核心类库,无法加载自定义的类
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2); // null

        // String是java的核心类库,由引导类加载器加载,所以获取不到
        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3); // null
    }
}

ClassLoader拓展使用

前面讲解IO流的时候,可以使用Properties类来加载配置文件,下面演示使用ClassLoader加载配置文件

区别:

  • Properties类加载可以获取跟目录下的配置文件
  • ClassLoader只能获取src目录下的配置文件

准备:依旧在src目录下创建一个配置文件,里面写入测试内容

Java-反射_第5张图片

使用Properties读取配置文件


package com.tcc.test;

import java.io.FileReader;
import java.util.Properties;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new FileReader("db.properties"));

        String name = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println(name); // 1234
        System.out.println(password); // admin
    }
}

使用ClassLoader读取配置文件


准备:需要先把db.properties文件粘贴到src文件夹里面

Java-反射_第6张图片

代码

package com.tcc.test;

import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
//        properties.load(new FileReader("db.properties"));

        ClassLoader classLoader = Test.class.getClassLoader();
        // NullPointerException
//        InputStream stream = classLoader.getResourceAsStream("db.properties");
        InputStream stream = classLoader.getResourceAsStream("db1.properties");
        properties.load(stream);

        String name = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println(name); // 1234
        System.out.println(password); // admin
    }
}

3、创建运行时类的对象

Class类对象提供了一个newInstance()方法,创建对应的运行时类的对象。内部调用了运行时类的空参构造器

想要此方法正常的创建运行时类的对象,要求:

  • 运行时类必须提供空参的构造器
  • 空参的构造器的访问权限得够。通常,设置为public

在javabean中要求提供一个public的空参构造器。原因:

  • 便于通过反射,创建运行时类的对象
  • 便于子类继承此运行时类时,默认调用super时,保证父类有此构造器
package com.tcc.test;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
    }
}
class Person{

}

动态创建运行时类的对象演示

package com.tcc.test;

import java.util.Random;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args) throws Exception {
        Random random = new Random();
        int i = random.nextInt(3); //0,1,2
        String className = "";
        switch (i){
            case 0:
                className = "java.util.Date";
                break;
            case 1:
                className = "java.lang.Object";
                break;
            case 2:
                className = "java.sql.Date";
                break;
        }

        Class<?> clazz = Class.forName(className);
        System.out.println(clazz); // 随机 class java.util.Date
    }
}

4、获取运行时类的所有结构

准备

因为下面要获取类中所有结构,所以需要创建如下结构

  • 创建两个类,子父类关系(Person,Teacher)
    • 在子类父类中个创建两个构造器,一个无参,一个有参。
      • Person的有参构造设置为private权限
      • Teacher类中的无参设置为private权限
    • 在子类父类中各写一些属性,private和public权限都有,各抛出不同异常
    • 在父类子类中各写一些方法,依然是private和public权限都有,有参无参也是,各抛出不同异常
    • 子父类中各定义泛型
  • 再创建一个接口,两个类各实现
  • 声明一个接口,两个类上各加一个

代码如下,全部使用的内部类声明(方便)

@MyInterface("Person")
class Person<T> implements Action{
    private String name;
    public Integer age;

    public void pSayHi(){
        System.out.println("pSayHi方法");
    }

    private String pMethod(String test) throws RuntimeException{
        return "pMethod方法:" + test;
    }

    public Person() {
    }

    
    private Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

@MyInterface
class Teacher<E> extends Person<String> implements Action{
    @MyInterface("name")
    private String name;
    public String address;

    private void tSayHi(){
        System.out.println("tSayHi方法");
    }

    @MyInterface("tMethod")
    public Integer tMethod(int test) throws NullPointerException{
        return test;
    }

    public Teacher() {
    }
    
    public Teacher(String address){
        this.address = address;
    }

    @MyInterface("Person")
    private Teacher(String name, String address) throws Exception {
        this.name = name;
        this.address = address;
    }
    
    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

interface Action{

}

// 可以放到类、方法、构造器、属性上
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.FIELD})
// 必须声明为RUNTIME,才能使用反射获取到
@Retention(RetentionPolicy.RUNTIME)
@interface MyInterface{
    String value() default "hello";
}

4.1、获取类的所有构造器(类的构造器)

获取构造器主要有如下四种方法

  • Constructor[] getConstructors():获取自身类所有的public权限的构造器
  • Constructor getConstructor(Class… parameterTypes):根据参数获取指定自身类中public权限的一个构造器
  • Constructor[] getDeclaredConstructors():获取自身所有权限的构造器(不区分private)
  • Constructor getDeclaredConstructor(Class… parameterTypes):根据参数,获取自身所有构造器的指定构造器

(1): Constructor[] getConstructors()


public static void main(String[] args) {
        Class<Teacher> clazz = Teacher.class;
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor); // public com.tcc.test.Teacher(java.lang.String)
        }
    }

(2): Constructor getConstructor(Class… parameterTypes)


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    //        Constructor constructor1 = clazz.getConstructor(String.class,String.class);
    // System.out.println(constructor1); // 异常:NoSuchMethodException,因为这个构造器是private的
    //
    Constructor<Teacher> constructor2 = clazz.getConstructor(String.class);
    System.out.println(constructor2); // public com.tcc.test.Teacher(java.lang.String)
}

(3): Constructor[] getDeclaredConstructors()


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor<?> declaredConstructor : declaredConstructors) {
        /*
                private com.tcc.test.Teacher(java.lang.String,java.lang.String)
                public com.tcc.test.Teacher(java.lang.String)
             */
        System.out.println(declaredConstructor);
    }
}

(4): Constructor getDeclaredConstructor(Class… parameterTypes)


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Constructor<Teacher> declaredConstructor = clazz.getDeclaredConstructor(String.class, String.class);
    System.out.println(declaredConstructor); // private com.tcc.test.Teacher(java.lang.String,java.lang.String)
}

4.2、获取类的所有属性(类的属性)

获取类的属性主要有如下四种方法

  • Field[] getFields():获取自身类及父类所有public权限的属性
  • Field getField(String name):获取自身类及父类指定属性名称的public权限的属性
  • Field[] getDeclaredFields():获取自身类所有权限的属性
  • Field getDeclaredField(String name):获取自身类所有权限的指定属性名称的属性

(1): Field[] getFields()


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Field[] fields = clazz.getFields();
    /*
            public java.lang.String com.tcc.test.Teacher.address
            public java.lang.Integer com.tcc.test.Person.age
         */
    for (Field field : fields) {
        System.out.println(field);
    }
}

(2): Field getField(String name)


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Field address = clazz.getField("address");
    System.out.println(address); // public java.lang.String com.tcc.test.Teacher.address
}

(3): Field[] getDeclaredFields()


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Field[] fields = clazz.getDeclaredFields();
    /*
            private java.lang.String com.tcc.test.Teacher.name
            public java.lang.String com.tcc.test.Teacher.address
         */
    for (Field field : fields) {
        System.out.println(field);
    }
}

(4): Field getDeclaredField(String name)


public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Field name = clazz.getDeclaredField("name");
    System.out.println(name); // private java.lang.String com.tcc.test.Teacher.name
}

4.3、获取类的所有方法(类的方法)

获取类的方法主要有如下四种方法

  • Method[] getMethods():获取自身类及父类所有public权限的方法
  • Method getMethod(String name, Class… parameterTypes):获取自身类及父类指定方法名称及参数的public权限的方法
  • Method[] getDeclaredMethods():获取自身类所有权限的方法
  • Method getDeclaredMethod(String name, Class… parameterTypes):获取自身类指定方法名称及参数的权限的方法

注意:

  • 因为所有类都继承Object类,所以Object是所有类的父类,也就能获取它里面的方法

(1): Method[] getMethods()

public static void main(String[] args) throws Exception{
        Class<Teacher> clazz = Teacher.class;
    Method[] methods = clazz.getMethods();
    /*
            public java.lang.Integer com.tcc.test.Teacher.tMethod(int) throws java.lang.NullPointerException
            public void com.tcc.test.Person.pSayHi()
            public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            public final void java.lang.Object.wait() throws java.lang.InterruptedException
            public boolean java.lang.Object.equals(java.lang.Object)
            public java.lang.String java.lang.Object.toString()
            public native int java.lang.Object.hashCode()
            public final native java.lang.Class java.lang.Object.getClass()
            public final native void java.lang.Object.notify()
            public final native void java.lang.Object.notifyAll()
         */
    for (Method method : methods) {
        System.out.println(method);
    }
}

(2): Method getMethod(String name, Class… parameterTypes)

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Method tMethod = clazz.getMethod("tMethod", int.class);
    System.out.println(tMethod); // public java.lang.Integer com.tcc.test.Teacher.tMethod(int) throws java.lang.NullPointerException
}

(3): Method[] getDeclaredMethods()

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Method[] methods = clazz.getDeclaredMethods();
    /*
            private void com.tcc.test.Teacher.tSayHi()
            public java.lang.Integer com.tcc.test.Teacher.tMethod(int) throws java.lang.NullPointerException
         */
    for (Method method : methods) {
        System.out.println(method);
    }
}

(4): Method getDeclaredMethod(String name, Class… parameterTypes)

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Method tSayHi = clazz.getDeclaredMethod("tSayHi");
    System.out.println(tSayHi); // private void com.tcc.test.Teacher.tSayHi()
}

5、获取运行时类的所有结构的结构(Constructor、Field、Method)

获取非public权限的结构,调用或赋值时,需要先打开使用权限!

constructor.setAccessible(true);

5.1、Constructor

T newInstance(Object … initargs):根据指定参数的构造器创建运行时对象

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    // 获取公共的构造器
    Constructor<Teacher> constructor = clazz.getDeclaredConstructor(String.class);
    Teacher teacher = constructor.newInstance("南京市"); // 传给构造器的参数
    System.out.println(teacher); // com.tcc.test.Teacher@1b6d3586

    // 私有的
    Constructor<Teacher> constructor1 = clazz.getDeclaredConstructor(String.class, String.class);
    // 需要打开可使用的权限,否则会报错:Class com.tcc.test.Test can not access a member of class com.tcc.test.Teacher with modifiers "private"
    constructor1.setAccessible(true);
    Teacher teacher1 = constructor1.newInstance("张三","南京市");
    System.out.println(teacher1); // com.tcc.test.Teacher@4554617c
}

拼接为一个完整的构造方法

注解

访问修饰符 方法名称 (参数)

  • int getModifiers():获取访问修饰符权限,返回值为int,可以使用Modifier.toString(int mod)进行转换
  • String getName():获取方法的权限定名
  • Class[] getParameterTypes():获取所有参数的类型的权限定名
  • Annotation[] getAnnotations():获取所有方法上的注解
public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    // 访问修饰符 方法名 (参数){}
    Constructor<Teacher> constructor = clazz.getDeclaredConstructor(String.class,String.class);
    // 在编译时不知道是什么权限的构造器,所以不管什么类型全部都设置为true
    constructor.setAccessible(true);

    // 获取的为数字,原因可以自己去百度查    跟java.lang.reflect.Modifier类有关
    // 访问修饰符
    int modifiers = constructor.getModifiers();
    String modifier = Modifier.toString(modifiers);

    // 方法名称
    String name = constructor.getName();

    // 形参类型
    Class<?>[] parameterTypes = constructor.getParameterTypes();
    String parameterType = "(";
    for (Class<?> pt : parameterTypes) {
        // 多参数后面需要添加,
        if (!"(".equals(parameterType)){
            parameterType += ", ";
        }
        parameterType += pt.getName();
    }
    parameterType += ")";

    // 注解
    String anno = "";
    Annotation[] annotations = constructor.getAnnotations();
    for (Annotation annotation : annotations) {
        // 多注解需要换行
        if(anno != ""){
            anno += "\n";
        }
        anno += annotation;
    }

    /*
            @com.tcc.test.MyInterface(value=Person)
            private com.tcc.test.Teacher (java.lang.String, java.lang.String)
         */
    System.out.println(anno + "\n" +modifier + " " + name + " " + parameterType);
}

5.2、Field

void set(Object obj, Object value):给通过反射获取的属性赋值,需要先通过反射创建实例

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Teacher teacher = clazz.newInstance();
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    // 修改类的属性,第一个参数为创建好的运行时的类,第二个参数为要给属性赋的值
    name.set(teacher,"张三");
    System.out.println(teacher); // Teacher{age=null, name='张三', address='null'}
}

拼接为一个完整的属性

注解

访问修饰符 属性类型 属性名称

  • int getModifiers():访问修饰符,不多说,上面有
  • Class getType():获取属性类型
  • String getName():获取属性名
  • Annotation[] getAnnotations():获取属性上的所有注解
public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Field field = clazz.getDeclaredField("name");
    field.setAccessible(true);
    // 访问修饰符
    int modifiers = field.getModifiers();
    String modifier = Modifier.toString(modifiers);

    // 属性类型
    Class<?> type = field.getType();

    // 属性名
    String name = field.getName();

    // 注解
    String anno = "";
    Annotation[] annotations = field.getAnnotations();
    for (Annotation annotation : annotations) {
        if(!"".equals(anno)){
            anno += "\n";
        }
        anno += annotation;
    }

    /*
            @com.tcc.test.MyInterface(value=name)
            private java.lang.String name
         */
    System.out.println(anno + "\n" +modifier + " " + type.getName() + " " + name);
}

5.3、Method

Object invoke(Object obj, Object… args):调用运行时的方法,需要先通过反射创建实例

public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Teacher teacher = clazz.newInstance();
    Method method = clazz.getDeclaredMethod("tMethod", int.class);
    method.setAccessible(true);

    // 第一个参数为:运行时的类,第二个参数为方法的参数(可以多个),方法返回的值用invoke接收,如果返回值为void,则为null
    Object invoke = method.invoke(teacher, 520);
    System.out.println(invoke); // 520
}

拼接为一个完整的方法

注解

访问修饰符 返回值类型 方法名称(参数) throws 异常

  • int getModifiers():获取访问修饰符
  • Class getReturnType():获取返回值类型
  • String getName():获取方法名称
  • Class[] getParameterTypes():获取方法所有参数类型
  • Class[] getExceptionTypes():获取方法抛出的所有异常
  • Annotation[] getAnnotations():获取方法上的所有注解
public static void main(String[] args) throws Exception{
    Class<Teacher> clazz = Teacher.class;
    Method method = clazz.getDeclaredMethod("tMethod", int.class);

    // 访问修饰符
    int modifiers = method.getModifiers();
    String modifier = Modifier.toString(modifiers);

    // 返回值类型
    Class<?> returnType = method.getReturnType();

    // 方法名称
    String name = method.getName();

    // 参数
    String parameter = "(";
    Class<?>[] parameterTypes = method.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        if (!"(".equals(parameter)){
            parameter += ", ";
        }
        parameter += parameterType;
    }
    parameter += ")";

    // 异常
    String exception = "";
    Class<?>[] exceptionTypes = method.getExceptionTypes();
    if(exceptionTypes.length > 0){
        exception += " throw ";
        for (Class<?> exceptionType : exceptionTypes) {
            if (!" throw ".equals(exception)){
                exception += ", ";
            }
            exception += exceptionType.getName();
        }
    }

    // 注解
    String anno = "";
    Annotation[] annotations = method.getAnnotations();
    for (Annotation annotation : annotations) {
        if(!"".equals(anno)){
            anno += "\n";
        }
        anno += annotation;
    }

    /*
            @com.tcc.test.MyInterface(value=tMethod)
            public java.lang.Integer tMethod (int) throw java.lang.NullPointerException
         */
    System.out.println(anno + "\n" +modifier + " " + returnType.getName() + " " + name + " " + parameter + exception);

}

7、动/静态代理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对院士对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上

代理的原理就是

  • 你把你的类给我
  • 我实现和你同样实现的接口
  • 然后调我重写接口的方法,实际上就是调用你重写接口的方法
  • 但是我可以在掉我自己(你的)方法前后做一些其他操作

7.1、静态代理

静态代理劣势

  • 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展
  • 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理
  • 创建静态代理需要共同实现的接口、代理类、被代理类

下面举一个卖货的例子,卖货前需要准备货物,卖完货需要数钱。

package com.tcc.test;

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args){
        ASUS asus = new ASUS();
        ASUSProxy asusProxy = new ASUSProxy(asus);
        /*
            卖货前准备货物
            华硕要卖笔记本了
            卖完货了数钱
         */
        asusProxy.sellGoods();
    }
}

/**
 * 共同实现卖货的接口
 * @Author: tcc
 * @Description
 * @Date: 2022/11/20 13:20
 * @params:
 * @return
 */
interface SellingGoodsFactory{
    void sellGoods();
}

/**
 * 被代理类
 * @Author: tcc
 * @Description
 * @Date: 2022/11/20 13:22
 * @params:
 * @return
 */
class ASUS implements SellingGoodsFactory{

    @Override
    public void sellGoods() {
        System.out.println("华硕要卖笔记本了");
    }
}

/**
 * 代理类,代理ASUS类的sellGoods方法
 * @Author: tcc
 * @Description 
 * @Date: 2022/11/20 13:27
 * @params: 
 * @return 
 */
class ASUSProxy implements SellingGoodsFactory{
    private SellingGoodsFactory sellingGoodsFactory;

    public ASUSProxy(SellingGoodsFactory sellingGoodsFactory) {
        this.sellingGoodsFactory = sellingGoodsFactory;
    }

    @Override
    public void sellGoods() {
        System.out.println("卖货前准备货物");
        sellingGoodsFactory.sellGoods();
        System.out.println("卖完货了数钱");
    }
}

7.2、动态代理

是在运行时创建的代理类,灵活,可变

package com.tcc.test;

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

/**
 * @author 宇辰
 * @date 2022/8/31-8:53
 **/
public class Test{
    public static void main(String[] args){
        ASUS asus = new ASUS();
        /*
            这里可以进行强转,但是强转类型不能是自己了,得是实现的接口。因为代理是又创建了一个类,并不是本身的类ASUS
            通过ProxyFactory.getProxyInstance方法可以生成指定类的代理类对象
            然后通过代理类对象调用被代理类的方法
         */
        SellingGoodsFactory instance = (SellingGoodsFactory) ProxyFactory.getProxyInstance(asus);
        instance.sellGoods();

        System.out.println("**********正在卖鞋***********");

        Double money = instance.countMoney(520.5, 1314.5);
        System.out.println("总共赚了;" + money);
    }
}

/**
 * 共同实现卖货的接口
 * @Author: tcc
 * @Description
 * @Date: 2022/11/20 13:20
 * @params:
 * @return
 */
interface SellingGoodsFactory{
    void sellGoods();

    Double countMoney(Double startMoney,Double endMoney);
}

/**
 * 代理类
 * @Author: tcc
 * @Description
 * @Date: 2022/11/20 13:29
 * @params:
 * @return
 */
class ProxyFactory{

    // 调用此方法,获得一个代理类对象
    public static Object getProxyInstance(Object obj){
        // 需要传入被代理类,否则invoke方法第一个参数获取不到被代理类,就调用不了它的方法
        MyInvocationHandler handler = new MyInvocationHandler(obj);
        /*
            Proxy.newProxyInstance是Java反射下的一个方法
            第一个参数:获取被代理类的类加载器,这样就能在运行时用这个加载器加载代理类
            第二个参数:获取被代理类实现的接口,这里就能让代理类 实现 被代理类所实现的接口
            第三个参数:被代理类所执行的方法将经过InvocationHandler代理执行
         */
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }

}

class MyInvocationHandler implements InvocationHandler{
    private Object obj;

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

    /*
        第一个参数:代理类对象
        第二个参数:被代理类所调用的方法,可以通过method.invoke()方法执行被代理类的方法
        第三个参数:方法的所有参数
        returnValue:方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用方法前后可以增加一些日志等操作
        System.out.println("方法执行前");
        Object returnValue = method.invoke(obj, args);
        System.out.println("方法执行后");
        return returnValue;
    }
}

class ASUS implements SellingGoodsFactory{

    @Override
    public void sellGoods() {
        System.out.println("我要卖鞋");
    }

    @Override
    public Double countMoney(Double startMoney, Double endMoney) {
        System.out.println("收入-成本=总利润");
        return endMoney - startMoney;
    }
}

结果

Java-反射_第7张图片

你可能感兴趣的:(java,开发语言,jvm)