[java学习日记]反射、动态代理

目录

一.反射的简单解释与获取字节码文件对象

二.获取构造方法对象Constructor

三.反射获取字节码文件中的成员变量Field

四.反射获取字节码文件中的成员方法:Method

五.反射练习:保存信息

六.反射练习:利用配置文件(存储类名和方法名),动态创建对象并调用方法

七.动态代理


一.反射的简单解释与获取字节码文件对象

反射:允许对封装类的成员变量、构造方法、成员方法获取出来然后进行操作,或者获取到如修饰符,名字等更加详细的信息
没有反射怎么拿?使用IO流!但是比较麻烦,这里就用反射咯
1.获取Class的有哪三种方式?
    1.Class.forName("全类名")
    2.类名.class
    3.对象.getClass()
对应三个不同的阶段
    1.源代码:编译时期
    2.加载阶段:在内存当中的时候
    3.运行阶段:已经有对象了
2.这三种方法获取的是什么对象?
public class Demo351 {
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println("(1)获取Class的第一种方式:需要传入全类名的字符串:包含包名,最为常用");
        System.out.println("IDEA中获取:选择类名,然后右键选择copy_Reference");
        Class class1 = Class.forName("Day35_Reflect_DynamicAgent.Demo351");
        System.out.println(class1);

        System.out.println("(2)获取Class的第二种方式,通过类名调用");
        System.out.println("第二种则是多用于作为参数作为传递:比如当作锁");
        Class class2 = Demo351.class;

        System.out.println("(3)获取Class的第三种方式,通过对象调用,也是获取的字节码文件");
        //已经有对象的时候才可以使用
        Class class3 = new Student().getClass();

        System.out.print("2.前两个类对象都是获取的字节码文件,是同一个对象:");
        System.out.println(class2==class1);
    }
}

学生类JavaBean,下面案例都是使用的此学生类

//四个构造方法:两个是私有的
//有个成员方法抛出了异常
public class Student {
    private String name;
    private int age;
    public Student() {
    }

    private Student(String name) {
        this.name = name;
    }

    private Student(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

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

[java学习日记]反射、动态代理_第1张图片


二.获取构造方法对象Constructor

从字节码文件里面获取数据对象的内容:本类测试获取构造方法对象Constructor
1.使用什么方法获取所有公共构造方法呢?
2.使用什么方法获取所有构造方法呢(包括私有)?
3.获取单个构造方法时传入的参数应该是?
4.如何获取构造方法内的权限修饰符?
5.如何读取方法中的参数?
6.如何使用私有构造方法创建变量?
public class Demo352 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取字节码文件对象
        Class studentClass = Class.forName("Day35_Reflect_DynamicAgent.Student");

        System.out.println("1.使getConstructors获取构造方法,获取所有公共构造方法");
        Constructor[] constructors = studentClass.getConstructors();
        for (Constructor constructor : constructors) System.out.println(constructor);

        System.out.println("2.获取所有构造方法(Declared:公开声明的)");
        constructors = studentClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) System.out.println(constructor);

        System.out.println("3.获取单个构造方法:指定构造方法可以传递参数:数据类型的字节码文件,基本数据类型也要传.class字节码文件");
        Constructor constructor = studentClass.getConstructor();
        System.out.println(constructor);
        constructor = studentClass.getConstructor(String.class,int.class);
        System.out.println(constructor);
        constructor = studentClass.getDeclaredConstructor(int.class);
        System.out.println(constructor);

        //获取构造方法里面的内容
        System.out.println("4.使用getModifier方法读取权限修饰符:内容是2的整数倍(和底层运算效率有关,左右移就能改变)");
        //有什么用呢?写IDEA源码有用,提示能够直接看到能传上面参数
        System.out.println(constructor.getModifiers());

        System.out.println("5.使用getParameters方法读取有哪些参数");
        Parameter[] parameters = constructor.getParameters();
        for (Parameter parameter : parameters) System.out.println(parameter);

        System.out.println("6.使用构造方法创建对象:私有方法不能够创建对象,需要临时取消权限校验(暴力反射)");
        System.out.println("首先把构造方法调用setAccessible方法,传入true为参数");
        constructor.setAccessible(true);
        System.out.println("再使用构造方法的newInstance方法构造其对象");
        Student student = (Student) constructor.newInstance(23);
        System.out.println(student);
    }
}

[java学习日记]反射、动态代理_第2张图片


三.反射获取字节码文件中的成员变量Field

反射获取字节码文件中的成员变量Field
1.获取单个变量需要传入的参数?
2.如何获取变量名?
3.如何获取变量类型?
4.如何获取对象中的变量的值?
5.如何修改对象中变量的值?
public class Demo353 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //获取Field也是一样的方法
        Class studentClass = Class.forName("Day35_Reflect_DynamicAgent.Student");
        Field[] fields = studentClass.getDeclaredFields();
        for (Field field : fields) System.out.println(field);
        System.out.println("1.获取单个成员变量:传入变量名字符串");
        Field field = studentClass.getDeclaredField("age");
        //获取成员变量信息
        System.out.println(field.getModifiers());
        System.out.println("2.使用getName方法获取变量名");
        System.out.println(field.getName());
        System.out.println("3.使用getType方法获取变量类型");
        Class type = field.getType();
        System.out.println(type);

        System.out.println("4.使用get方法传入某对象,获取这个成员变量在该对象中的值,同样也需要取消权限校验");
        field.setAccessible(true);
        Student student = new Student("zhangsan", 123);
        Object age = field.get(student);
        System.out.println(age);

        System.out.println("5.使用set方法,传入某对象与修改的值,对该对象中的成员变量修改");
        field.set(student,1234);
        System.out.println(student);
    }
}

[java学习日记]反射、动态代理_第3张图片


四.反射获取字节码文件中的成员方法:Method

反射获取字节码文件中的成员方法:Method
1.使用getMethods方法与getDeclaredMethods方法有什么区别?
2.如何获取单个方法,传入的参数是?
3.如何返回方法抛出的异常?
4.如何使用method对某对象调用方法?传入的参数与返回的参数有什么特点?
public class Demo354 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class studentClass = Class.forName("Day35_Reflect_DynamicAgent.Student");
        Method[] methods = studentClass.getMethods();
        System.out.println("1.使用getMethods方法同时也有继承与Object中的方法");
        for (Method method : methods)System.out.println(method);

        methods = studentClass.getDeclaredMethods();
        System.out.println("获取所有方法的时候不会包含继承下来的方法");
        for (Method method : methods)System.out.println(method);

        System.out.println("2.通过名字和形参获取一个方法,可以不加形参");
        Method method = studentClass.getMethod("setName",String.class);
        System.out.println(method);

        //修饰符,名字,参数
        System.out.println(method.getModifiers());
        System.out.println(method.getName());
        for (Parameter parameter : method.getParameters())System.out.println(parameter);

        System.out.println("3.使用getExceptionTypes返回方法抛出的异常数组");
        System.out.println(Arrays.toString(method.getExceptionTypes()));

        System.out.println("4.调用invoke(调用)方法,第一个参数传入对象,第二个参数没有就不写,方法没有返回也可以不写");
        Student student = new Student();
        method.invoke(student,"zhangsan");
        System.out.println(student);
    }
}

 [java学习日记]反射、动态代理_第4张图片


五.反射练习:保存信息

对于任意一个对象:把对象所有信息保存到文件当中,对于不同的对象都可以如此操作
public class Demo355 {
    final static String FILE_STR = "D:\\IDEACode\\demo1\\JAVA基础\\src\\Day35_Reflect_DynamicAgent\\message.properties";
    public static void main(String[] args) throws IllegalAccessException, IOException {
        Student student = new Student("zhansgan",123);
        Properties properties =new Properties();
        Class studentClass = student.getClass();
        //获取字节码文件中的所有成员变量
        Field[] fields = studentClass.getDeclaredFields();
        for (Field field : fields) {
            //设置取消检验
            field.setAccessible(true);
            //获取变量名
            String name = field.getName();
            //获取对象中的该变量
            Object o = field.get(student);
            properties.put(name,o.toString());
        }
        properties.store(new FileWriter(FILE_STR),"addStudent");
    }
}

[java学习日记]反射、动态代理_第5张图片


六.反射练习:利用配置文件(存储类名和方法名),动态创建对象并调用方法

public class Demo356 {
    final static String FILE_STR = "D:\\IDEACode\\demo1\\JAVA基础\\src\\Day35_Reflect_DynamicAgent\\prop.properties";
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Properties properties = new Properties();
        properties.load(new FileReader(FILE_STR));
        //获取字节码文件
        Class aClass = Class.forName((properties.get("classname").toString()));
        //获取构造方法,创建对象
        Constructor constructor = aClass.getConstructor();
        Object o = constructor.newInstance();
        //获取方法名并调用
        Method method = aClass.getMethod(properties.get("method").toString());
        System.out.println(method.invoke(o));
    }
}

[java学习日记]反射、动态代理_第6张图片


七.动态代理

动态代理:不修改原有的代码,又需要增加额外功能
为了程序的健壮性,不改变原有代码的功能,使用动态代理完成一些额外的事情
想要找某个核心人物做事情,需要先找它的代理人去说要做的事情,代理人里面也要有核心任务的方法
如何确定需要代理的方法呢?
为了确定需要代理的方法,需要让核心人物与代理人都实现某一个接口

需要代理的类

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("跳舞");
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "BigStar{" +
                "name='" + name + '\'' +
                '}';
    }
}

需要代理的方法的接口

public interface Star {
    String sing(String name);
    void dance();
}

代理工具类 :用来创建代理对象

public class ProxyUtil {

    public static Star createProxy(BigStar bigStar) {
        System.out.println("""
                1.创建静态方法返回一个代理人(类型为BigStar实现的结构)
                方法作用:给明星对象创建一个代理
                形参:被代理的明星对象
                返回一个接口,也是返回代理对象
                """);
        System.out.println("2.使用Proxy的newProxyInstance方法创建代理人对象,传入三个参数");
        System.out.println("""
                参数一:指定类加载器加载生成的代理类:找到谁加载了这个类
                参数二:指定接口数组,用于指定代理能代理哪些方法
                参数三:指定代理要干什么事情,在这个参数需要的对象需要实现的方法参数有三个
                
                参数一:代理的对象,暂时用不到
                参数二:要运行的方法
                参数三:调用方法的时候需要传递的实参
                """);
        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 {
                        /*
                        参数一:代理的对象,暂时用不到
                        参数二:要运行的方法
                        参数三:调用方法的时候需要传递的实参
                        */
                        if ("sing".equals(method.getName()))
                            System.out.println("代理人:准备话筒");
                        else if ("dance".equals(method.getName()))
                            System.out.println("代理人:准备场地");
                        //返回运行结果
                        return method.invoke(bigStar, args);
                    }
                }
        );
        return star;
    }
}

测试类 

public class Demo361 {
    public static void main(String[] args) {
        //创建代理人
        Star proxy = ProxyUtil.createProxy(new BigStar("mona"));
        //使用代理人去调用方法
        System.out.println(proxy.sing("偶像宣言"));
        proxy.dance();
    }
}

[java学习日记]反射、动态代理_第7张图片

你可能感兴趣的:(JAVA黑马程序员笔记分享,java,开发语言,学习)