Java反射机制和动态代理

2024-01-03 00:55:54

反射

前言

什么是反射?
反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
为什么用反射 / 反射的作用?
可以轻易地获取成员变量、构造方法和成员方法的所有信息。
①获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑。
②结合配置文件,动态的创建对象并调用方法。

获取class对象的方式

三种方式
Class.forName("全类名");
类名.class
对象.getClass()
使用时机
这三种方式的使用时机实际上和Java文件的编译过程息息相关:
在这里插入图片描述

源代码阶段没有把代码加载到内存当中,只是在硬盘中进行的操作,这一阶段使用第一种方式获得class字节码文件的对象;运行Java代码时,需要将类的字节码文件加载到内存中,这个阶段是加载阶段,该阶段使用第二种方式;在内存中创建某个类的对象时为运行阶段,该阶段使用第三种方式。

具体实现

//1. Class.forName("全类名") 全类名就是:包名+类名
//clazz_one就是Student类的字节码文件
//第一种方式最为常用
Class clazz_one = Class.forName("com.wmy.myreflect1.Student");
System.out.println(clazz_one);

//2. 类名.class
//第二种方式更多的是当做参数来传递
Class clazz_two = Student.class;
System.out.println(clazz_two);

//3. 对象.getClass()
//当我们已经有了这个类的对象时 才可以使用方式三
Class clazz_three = new Student().getClass();
System.out.println(clazz_three);
System.out.println(clazz_one == clazz_two);
System.out.println(clazz_two == clazz_three);
System.out.println(clazz_one == clazz_three);

运行结果
在这里插入图片描述

反射获取构造方法

在Java中,存在“万物皆对象”的思想,比如说class字节码文件是Class类的对象,那么构造方法可以看作是Constructor类的对象、成员变量(字段)可以看作是Field类的对象、成员方法可以看作是Method类的对象。

//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo02.Student02");
//2. 获取构造方法
System.out.println("获取公共构造方法:");
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1){
	System.out.println(con);
}
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取所有构造方法(含私有):");
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2){
	System.out.println(con);
}
System.out.println("注:虽然该方法可以获得私有构造方法,但是无法通过这种方式完成私有构造方法new一个对象出来");
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("根据构造方法中参数类型和参数数量的不同 向getDeclaredConstructor中传入参数类型的字节码文件即可获得对应的构造方法:");
Constructor con1 = clazz.getDeclaredConstructor();
Constructor con2 = clazz.getDeclaredConstructor(String.class);
Constructor con3 = clazz.getDeclaredConstructor(int.class);
Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(con1);
System.out.println(con2);
System.out.println(con3);
System.out.println(con4);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取权限修饰符");
int modifiers = con4.getModifiers();
System.out.println("1表示公有public 2表示私有private");
System.out.println(modifiers);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("暴力反射:表示临时取消权限校验");
con4.setAccessible(true);
Student02 stu = (Student02)con4.newInstance("张三", 23);
System.out.println(stu);

在这里插入图片描述

反射获取成员变量

//1. 获取class字节码文件的对象
Class clazz = Class.forName("com.wmy.myreflect1.demo03.Student03");
//2. 获取成员变量
System.out.println("获取所有公共成员变量:");
Field[] fields1 = clazz.getFields();
for (Field field : fields1){
 	System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有成员变量(含私有):");
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2){
	System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取单个成员变量:");
Field gender = clazz.getField("gender");
System.out.println(gender);
Field name = clazz.getDeclaredField("name");
System.out.println(name);
Field age = clazz.getDeclaredField("age");
System.out.println(age);
System.out.println("分割线-----------------"+"\n");
System.out.println("获取权限修饰符:");
int modifiers = name.getModifiers();
System.out.println(modifiers);
System.out.println("获取成员变量名称:");
String n = name.getName();
System.out.println(n);
System.out.println("获取成员变量类型:");
Class t = name.getType();
System.out.println(t);
System.out.println("获取成员变量记录的值:");
Student03 s = new Student03("张三",23,"男");
name.setAccessible(true);
String value = (String) name.get(s);
System.out.println(value);
System.out.println("修改对象里面记录的值:");
name.set(s,"李四");
System.out.println(s);

在这里插入图片描述

反射获取成员方法

//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo04.Student04");
//2. 获取成员方法
System.out.println("获取所有方法对象(包含父类中所有的公共方法):");
Method[] methods1 = clazz.getMethods();
for (Method method : methods1){
    System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有方法对象(不能获取父类的,但是可以获取本类中私有的方法):");
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2){
    System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取指定的单一方法:");
Method m = clazz.getDeclaredMethod("eat", String.class, int.class);
System.out.println(m);
System.out.println("获取方法的修饰符:");
int modifiers = m.getModifiers();
System.out.println(modifiers);
System.out.println("获取方法的名字:");
String name = m.getName();
System.out.println(name);
System.out.println("获取方法的形参:");
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
    System.out.println(parameter);
}
System.out.println("获取方法抛出的异常:");
Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
    System.out.println(exceptionType);
}
System.out.println("获取方法的返回值:");
Student04 s = new Student04();
m.setAccessible(true);
String result = (String)m.invoke(s,"石乐志",996);
System.out.println(result);

实例

需求1
保存信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。
实现

public class MyReflectDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        /*
        * 对于任意一个对象 都可以把对象所有的字段名和值 保存到文件中去
        * */
        Student s = new Student("小A" , 23, '女' , 167.5 , "睡觉");
        Teacher t = new Teacher("gls" , 1000);

        saveObject(t);
    }
    //把对象的所有成员变量名和值保存到本地文件中
    private static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1. 获取字节码文件的对象
        Class clazz = obj.getClass();
        //创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\JavaWorks_IntelliJIDEA\\myreflect\\src\\main\\java\\com\\wmy\\myreflect1\\record.txt"));

        //2. 获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
            //写出数据
            bw.write(name+"="+value);
            bw.newLine();
        }
        bw.close();
    }
}

结果
在这里插入图片描述
需求2
跟配置文件结合动态创建:反射可以和配置文件结合的方式,动态的创建对象,并调用方法。
实现
prop.properties

classname=com.wmy.myreflect1.case2.Student
method=study

com/wmy/myreflect1/case2/MyReflectDemo.java

public class MyReflectDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        /*
        * 反射可以和配置文件结合的方式 动态地创建对象 并调用方法
        * */
        //1.读取配置文件中的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("G:\\JavaWorks_IntelliJIDEA\\myreflect\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        //2.获取全类名和方法名
        String className = (String)prop.get("classname");
        String methodName = (String)prop.get("method");
        System.out.println(className);
        System.out.println(methodName);

        //3.利用反射去创建对象并运行方法
        Class clazz = Class.forName(className);

        //获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        Object o = con.newInstance();
        System.out.println(o);

        //获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(o);
    }
}

动态代理

1.什么是动态代理?
动态代理可以无侵入式的给代码增加额外的功能。
在这里插入图片描述
2.程序为什么需要代理?
对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。
对象有什么方法想被代理,代理就一定要有对应的方法。
3.代理长什么样?
代理里面就是对象要被代理的方法。
4.Java通过什么来保证代理的样子?
通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法。
5.如何为Java对象创建一个代理对象?
java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,InvocationHandler h)
参数一:用于指定用哪个类加载器 去加载生成的代理类
参数二:指定接口 这些接口用于指定生成的代理长什么样 也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情

需求
实现BigStar类中成员方法的动态代理。
步骤
1.创建BigStar类(记得实现步骤2中的接口)
com/wmy/mydynamicproxy/BigStar.java

public class BigStar implements Star{
    private String name;
    public BigStar(){

    }
    public BigStar(String name){
        this.name = name;
    }
    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name+"正在唱"+name);
        return "谢谢大家";
    }
    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name+"正在让你蠢蠢欲动");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "BigStar{" +
                "name='" + name + '\'' +
                '}';
    }
}

2.封装要代理的方法为接口
com/wmy/mydynamicproxy/Star.java

public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}

3.创建ProxyUtil类用于创建代理
com/wmy/mydynamicproxy/ProxyUtil.java

/*
* 类的作用:
*       创建一个代理
* */
public class ProxyUtil {
    /*
    * 方法的作用:
    *       给一个明星的对象创建一个代理
    * 形参:
    *       被代理的明星对象
    * 返回值:
    *       给明星创建的代理
    * 需求:
    *       外面的人想要大明星唱一首歌
    *       1.获取代理的对象
    *           代理对象 = ProxyUtil.createProxy(大明星的对象);
    *       2.再调用代理的唱歌的方法
    *           代理对象.唱歌的方法();
    * */
    public static Star createProxy(BigStar bigStar){
        /*
        * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,InvocationHandler h)
        * 参数一:用于指定用哪个类加载器(将字节码文件加载到内存中的工具) 去加载生成的代理类
        * 参数二:指定接口 这些接口用于指定生成的代理长什么样 也就是有哪些方法
        * 参数三:用来指定生成的代理对象要干什么事情
        * */
        Star star = (Star)Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),
                new Class[]{Star.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                         * 参数一:代理的对象
                         * 参数二:要运行的方法 sing
                         * 参数三:调用sing方法时,传递的实参
                         * */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒 收钱!");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地 收钱!");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

4.创建测试类测试动态代理的过程
com/wmy/mydynamicproxy/Test.java

    /*
        * 需求:
        *       外面的人想要大明星唱一首歌
        *       1.获取代理的对象
        *           代理对象 = ProxyUtil.createProxy(大明星的对象);
        *       2.再调用代理的唱歌的方法
        *           代理对象.唱歌的方法();
        * */
public class Test {
    public static void main(String[] args) {
        //1.获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);
        //2.调用唱歌的方法
        String result = proxy.sing("姬霓太美");
        System.out.println(result);
        //2.调用跳舞的方法
        proxy.dance();
    }
}

5.效果
在这里插入图片描述

文章来源:https://blog.csdn.net/qq_51580006/article/details/135309071
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。