Java反射复习

1. 动态代理

动态代理是一种程序设计模式,它允许程序在运行时创建代理对象,而无需在编译时将对象接口显示的硬编码到程序中。动态代理可以帮助程序员在不改变现有代码的情况下,在运行时动态地添加功能或修改现有功能。

简单来说,动态代理允许程序创建一个中间人对象,该对象在运行时可以拦截对原始对象的调用,并向其添加额外的行为或修改其行为。动态代理通常用于实现AOP(面向切面编程)和RPC(远程过程调用)等技术,以提高程序的可维护性和灵活性。在Java中,动态代理可以使用Java自带的Proxy类和InvocationHandler接口来实现。

2. 创建动态代理

在这里插入图片描述
Java中的Proxy类可以用于实现动态代理。动态代理是一种代理设计模式,它允许在运行时创建代理对象并连接到底层方法。在Java中,动态代理是通过实现 InvocationHandler 接口和使用 Proxy 类来实现的。下面是一个实战示例。

假设我们有一个接口 UserService 和一个实现类 UserServiceImpl,我们希望为 UserService 创建一个代理,以便在访问它的方法时记录日志。代码如下:

public interface UserService {
    void saveUser(String name);
}

public class UserServiceImpl implements UserService {
    public void saveUser(String name) {
        // 保存用户到数据库
        System.out.println("User " + name + " saved.");
    }
}

现在,我们创建一个 InvocationHandler 来实现代理逻辑。这里的逻辑很简单,只是在方法调用前后记录一些日志。代码如下:

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

public class LoggingProxy implements InvocationHandler {

    private Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Entering " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("Exiting " + method.getName());
        return result;
    }
}

现在我们可以使用上述类来创建一个代理。代码如下:

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        LoggingProxy handler = new LoggingProxy(userService);
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            new Class[] { UserService.class },
            handler);
        proxy.saveUser("John");
    }
}

2.1 例子2

/**
 * 动态代理
 *
 * 我们可以把所有想要被代理的方法定义在接口当中
 */
public interface Star {

    /**
     * 唱歌
     *
     * @param name
     * @return
     */
    String sing(String name);

    /**
     * 跳舞
     */
    void dance();
}
package dynamic;

/**
 * 动态代理
 */
public class BigStar implements Star {
    private String name;

    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    /**
     * 唱歌
     *
     * @param name
     * @return
     */
    @Override
    public String sing(String name) {
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    /**
     * 跳舞
     */
    @Override
    public void dance() {
        System.out.println(this.name + "正在跳舞");
    }

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

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

/**
 * 类的作用:创建一个代理
 */
public class ProxyUtil {
    /*
     *
     * 方法的作用:
     *       给一个明星的对象,创建一个代理
     *
     *  形参:
     *       被代理的明星对象
     *
     *  返回值:
     *       给明星创建的代理
     *
     *
     *
     * 需求:
     *   外面的人想要大明星唱一首歌
     *   1. 获取代理的对象
     *      代理对象 = ProxyUtil.createProxy(大明星的对象);
     *   2. 再调用代理的唱歌方法
     *      代理对象.唱歌的方法("只因你太美");
     * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 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;

    }
}

package dynamic;

/**
 * 动态代理测试
 */
public class DynamicProxyTest {
    public static void main(String[] args) {

        /**
         * 需求:
         *      外面的人想要大明星唱一首歌
         *      1. 获取代理的对象
         *          代理对象 = ProxyUtil.createProxy(大明星的对象);
         *      2. 再调用代理的唱歌方法
         *          代理对象.唱歌的方法("只因你太美");
         */
        // 1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        // 2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);

        // 3. 调用跳舞歌的方法
        proxy.dance();
        System.out.println(result);
    }
}



result::
		准备话筒,收钱
		鸡哥正在唱只因你太美
		谢谢
		准备场地,收钱
		鸡哥正在跳舞
		谢谢

在这里插入图片描述

3. 反射

反射(Reflection)是编程中的一种机制,它允许程序在运行时访问和修改自身结构和行为。反射在很多编程语言中都有支持,包括 Java、C# 和 Python 等。

在 Java 中,反射主要用于以下几个方面:

  1. 获取类信息:可以在运行时获取类的名称、方法、字段、构造函数等信息。
  2. 创建对象:可以通过反射创建类的实例,即使在编译时并不知道类的具体类型。
  3. 调用方法:可以在运行时调用类的方法,即使在编译时并不知道方法的具体信息。
  4. 访问字段:可以在运行时访问和修改类的字段,即使在编译时并不知道字段的具体信息。
获取类信息:可以在运行时获取类的名称、方法、字段、构造函数等信息
Class<?> clazz = Class.forName("com.example.MyClass"); // 获取类Class对象
Constructor<?>[] constructors = clazz.getDeclaredConstructors(); // 构造方法

创建实例
Object instance = clazz.getDeclaredConstructor().newInstance(); // 不是单例的

调用方法
Method method = clazz.getMethod("myMethod", String.class);
Object result = method.invoke(instance, "argument");

访问字段
Field field = clazz.getDeclaredField("myField");
field.setAccessible(true); // 如果字段是私有的,需要设置访问权限
field.set(instance, "newValue");
Object fieldValue = field.get(instance);

反射的优缺点
优点:

  1. 提高了灵活性,可以在运行时动态操作类和对象。
  2. 支持框架开发,很多框架(如 Spring)大量使用反射来进行依赖注入动态代理等操作。

缺点:

  1. 性能开销较大,反射操作通常比直接调用慢。
  2. 代码安全性降低,反射可以绕过访问修饰符(如 private),可能导致不安全操作。
  3. 代码可读性和维护性降低,反射代码通常较为复杂,不易理解和调试。

反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
在这里插入图片描述
反射能获取到的
在这里插入图片描述


Java反射(Reflection)是Java程序中一种高级特性,它允许在程序运行时获取对象的属性和方法,并且可以动态地调用这些属性和方法,而不需要编译时确定这些属性和方法。

Java反射的优势在于它可以在运行时检查,创建和操作对象,这使得Java程序更加灵活和可扩展。在反射中,可以使用 Java.lang.reflect 包中的类,如Class、Method、Constructor、Field 等,来获取类的信息,并且操作对象的属性和方法。

通过Java反射,可以做到以下的事情:

  1. 获取任何一个类的class对象
  2. 创建任意类型的对象
  3. 获取字段的值或者方法的返回值
  4. 设置字段值或者调用方法
  5. 构造方法的调用
  6. 动态代理

总的来说,Java反射是一种非常有用的技术,它可以让程序员在编码的过程中更加灵活,同时也为框架的设计提供了更多的可能性。

4. 获取Class对象

三种方式

  1. Class.forName("全类名")
  2. 类名.class
  3. 对象.getClass();
public class Person {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
  1. 通过类名.class方式获取
Class<Person> clazz1 = Person.class;

Person person1 = clazz1.newInstance();
person1.setName("Tom");
person1.setAge(18);

System.out.println(person1.getName() + " " + person1.getAge());
  1. 通过对象.getClass()方法获取
Person person2 = new Person("Jack", 20);

Class<? extends Person> clazz2 = person2.getClass();

Person person3 = clazz2.newInstance();
person3.setName("Jerry");
person3.setAge(22);

System.out.println(person3.getName() + " " + person3.getAge());
  1. 通过Class.forName()方法获取
Class clazz3 = Class.forName("com.example.Person");

Person person4 = (Person) clazz3.newInstance();
person4.setName("Amy");
person4.setAge(24);

System.out.println(person4.getName() + " " + person4.getAge());

以上这三种方式都可以获取到Person类的Class对象,并且可以使用Class对象进行一些操作,例如创建新的实例,设置对象的属性值等。其中,第三种方式需要传入类的全限定名,相比于前两种方式稍显繁琐,但是它可以动态地加载类,更加灵活。

5. 反射获取构造方法

在这里插入图片描述
在这里插入图片描述


通过反射获取构造方法是Java反射的重要应用之一。Java中,使用Class类的getConstructor()方法和getDeclaredConstructor()方法可以获取类的构造方法,其中getConstructor()方法获取的是public修饰的构造方法,getDeclaredConstructor()方法获取的是全部的构造方法(包含private、protected和默认访问权限)。

下面以一个Student类为例,演示如何通过反射获取构造方法:

public class Student {
    private String name;
    private int age;

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
  1. 获取public修饰的构造方法
Class<Student> clazz = Student.class;

Constructor<Student> constructor1 = clazz.getConstructor(String.class, int.class);

Student student1 = constructor1.newInstance("Tom", 18);

System.out.println(student1.getName() + " " + student1.getAge());
  1. 获取全部的构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

for (Constructor<?> constructor : constructors) {
    System.out.println("constructor: " + constructor);
}

Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);

constructor2.setAccessible(true);

Student student2 = (Student) constructor2.newInstance("Jack");

System.out.println(student2.getName());

在第二个示例中,先使用Class类的getDeclaredConstructors()方法获取Student类的所有构造方法,然后通过遍历构造方法的方式展示构造方法的信息。接着,使用getDeclaredConstructor()方法获取Student类的private构造方法,由于private构造方法无法直接访问,所以需要使用setAccessible()方法将其设置为可访问,然后通过newInstance()方法创建Student类的实例。

通过反射获取构造方法可以使程序更加灵活,由此,我们可以动态地创建对象,而不需要知道具体的类名或者构造方法的参数列表。


6. 获取成员变量

规则:

​ get表示获取

​ Declared表示私有

​ 最后的s表示所有,复数形式

​ 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

方法名:

方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

在这里插入图片描述

代码示例:

public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获取成员变量对象

        //1.获取class对象
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");

        //2.获取成员变量的对象(Field对象)只能获取public修饰的
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }

        System.out.println("===============================");

        //获取成员变量的对象(public + private)
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        System.out.println("===============================");
        //获得单个成员变量对象
        //如果获取的属性是不存在的,那么会报异常
        //Field field3 = clazz.getField("aaa");
        //System.out.println(field3);//NoSuchFieldException

        Field field4 = clazz.getField("gender");
        System.out.println(field4);

        System.out.println("===============================");
        //获取单个成员变量(私有)
        Field field5 = clazz.getDeclaredField("name");
        System.out.println(field5);

    }
}



public class Student {
    private String name;

    private int age;

    public String gender;

    public String address;


    public Student() {
    }

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


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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
    }
}

7. 获取成员变量并获取值和修改值

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值

代码示例:

public class ReflectDemo5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Student s = new Student("zhangsan",23,"广州");
        Student ss = new Student("lisi",24,"北京");

        //需求:
        //利用反射获取成员变量并获取值和修改值

        //1.获取class对象
        Class clazz = Class.forName("com.itheima.reflectdemo.Student");

        //2.获取name成员变量
        //field就表示name这个属性的对象
        Field field = clazz.getDeclaredField("name");
        //临时修饰他的访问权限
        field.setAccessible(true);

        //3.设置(修改)name的值
        //参数一:表示要修改哪个对象的name?
        //参数二:表示要修改为多少?
        field.set(s,"wangwu");

        //3.获取name的值
        //表示我要获取这个对象的name的值
        String result = (String)field.get(s);

        //4.打印结果
        System.out.println(result);

        System.out.println(s);
        System.out.println(ss);

    }
}


public class Student {
    private String name;
    private int age;
    public String gender;
    public String address;


    public Student() {
    }

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


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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";
    }
}

8. 获取成员方法

规则:

​ get表示获取

​ Declared表示私有

​ 最后的s表示所有,复数形式

​ 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用

方法名说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到

代码示例:

public class ReflectDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.获取class对象
        Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");


        //2.获取方法
        //getMethods可以获取父类中public修饰的方法
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }

        System.out.println("===========================");
        //获取所有的方法(包含私有)
        //但是只能获取自己类中的方法
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }

        System.out.println("===========================");
        //获取指定的方法(空参)
        Method method3 = clazz.getMethod("sleep");
        System.out.println(method3);

        Method method4 = clazz.getMethod("eat",String.class);
        System.out.println(method4);

        //获取指定的私有方法
        Method method5 = clazz.getDeclaredMethod("playGame");
        System.out.println(method5);
    }
}

9. 获取成员方法并运行

方法

Object invoke(Object obj, Object… args) :运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

代码示例:

package com.itheima.a02reflectdemo1;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.获取字节码文件对象
        Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
		
        //2.获取一个对象
        //需要用这个对象去调用方法
        Student s = new Student();
        
        //3.获取一个指定的方法
        //参数一:方法名
        //参数二:参数列表,如果没有可以不写
        Method eatMethod = clazz.getMethod("eat",String.class);
        
        //运行
        //参数一:表示方法的调用对象
        //参数二:方法在运行时需要的实际参数
        //注意点:如果方法有返回值,那么需要接收invoke的结果
        //如果方法没有返回值,则不需要接收
        String result = (String) eatMethod.invoke(s, "重庆小面");
        System.out.println(result);

    }
}



public class Student {
    private String name;
    private int age;
    public String gender;
    public String address;


    public Student() {

    }

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

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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

    private void study(){
        System.out.println("学生在学习");
    }

    private void sleep(){
        System.out.println("学生在睡觉");
    }

    public String eat(String something){
        System.out.println("学生在吃" + something);
        return "学生已经吃完了,非常happy";
    }
}

10. 面试题:

​ 你觉得反射好不好?好,有两个方向

​ 第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。

​ 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

11. 练习泛型擦除

理解:(掌握)

​ 集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。

代码示例:(了解)

package com.itheima.reflectdemo;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectDemo8 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.创建集合对象
        ArrayList<Integer> list = new ArrayList<>();
        list.add(123);
//        list.add("aaa");

        //2.利用反射运行add方法去添加字符串
        //因为反射使用的是class字节码文件

        //获取class对象
        Class clazz = list.getClass();

        //获取add方法对象
        Method method = clazz.getMethod("add", Object.class);

        //运行方法
        method.invoke(list,"aaa");

        //打印集合
        System.out.println(list);
    }
}

12. 练习:修改字符串的内容

在这个练习中,我需要你掌握的是字符串不能修改的真正原因。

字符串,在底层是一个byte类型的字节数组,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private

final修饰value表示value记录的地址值不能修改。

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码示例:(了解)

String s = "abc";
String ss = "abc";
// private final byte[] value= {97,98,99};
// 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值
// 如果我们利用反射获取了value的地址值。
// 也是可以修改的,final修饰的value
// 真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,比较危险

//1.获取class对象
Class clazz = s.getClass();

//2.获取value成员变量(private)
Field field = clazz.getDeclaredField("value");
//但是这种操作非常危险
//JDK高版本已经屏蔽了这种操作,低版本还是可以的
//临时修改权限
field.setAccessible(true);

//3.获取value记录的地址值
byte[] bytes = (byte[]) field.get(s);
bytes[0] = 100;

System.out.println(s);//dbc
System.out.println(ss);//dbc

13. 练习,反射和配置文件结合动态获取的练习(重点)

需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

①通过Properties加载配置文件

②得到类名和方法名

③通过类名反射得到Class对象

④通过Class对象创建一个对象

⑤通过Class对象得到方法

⑥调用方法

代码示例:

public class ReflectDemo9 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.读取配置文件的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        String classname = prop.get("classname") + "";
        String methodname = prop.get("methodname") + "";

        //2.获取字节码文件对象
        Class clazz = Class.forName(classname);

        //3.要先创建这个类的对象
        Constructor con = clazz.getDeclaredConstructor();
        con.setAccessible(true);
        Object o = con.newInstance();
        System.out.println(o);

        //4.获取方法的对象
        Method method = clazz.getDeclaredMethod(methodname);
        method.setAccessible(true);

        //5.运行方法
        method.invoke(o);


    }
}

配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep

14. 利用发射保存对象中的信息(重点)

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

    //把对象里面所有的成员变量名和值保存到本地文件中
    public static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.获取字节码文件的对象
        Class clazz = obj.getClass();
        //2. 创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt"));
        //3. 获取所有的成员变量
        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();

    }
}
public class Student {
    private String name;
    private int age;
    private char gender;
    private double height;
    private String hobby;

    public Student() {
    }

    public Student(String name, int age, char gender, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public char getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(char gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return height
     */
    public double getHeight() {
        return height;
    }

    /**
     * 设置
     * @param height
     */
    public void setHeight(double height) {
        this.height = height;
    }

    /**
     * 获取
     * @return hobby
     */
    public String getHobby() {
        return hobby;
    }

    /**
     * 设置
     * @param hobby
     */
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
    }
}
public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}

15. 补充动态代理

15.1 好处:

​ 无侵入式的给方法增强功能

15.2 动态代理三要素:

1,真正干活的对象

2,代理对象

3,利用代理调用方法

切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

15.3 代码实现:

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

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}
/*
*
* 类的作用:
*       创建一个代理
*
* */
public class ProxyUtil {
    /*
    *
    * 方法的作用:
    *       给一个明星的对象,创建一个代理
    *
    *  形参:
    *       被代理的明星对象
    *
    *  返回值:
    *       给明星创建的代理
    *
    *
    *
    * 需求:
    *   外面的人想要大明星唱一首歌
    *   1. 获取代理的对象
    *      代理对象 = ProxyUtil.createProxy(大明星的对象);
    *   2. 再调用代理的唱歌方法
    *      代理对象.唱歌的方法("只因你太美");
    * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 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;
    }
}
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}
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 + "正在跳舞");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

15.4 额外扩展

动态代理,还可以拦截方法

比如:

​ 在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。

​ 但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

/*
* 类的作用:
*       创建一个代理
* */
public class ProxyUtil {
    public static Star createProxy(BigStar bigStar){
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 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 {
                        if("cleanWC".equals(method.getName())){
                            System.out.println("拦截,不调用大明星的方法");
                            return null;
                        }
                        //如果是其他方法,正常执行
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}

15.5 动态代理的练习

​ 对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强

public class MyProxyDemo1 {
    public static void main(String[] args) {
        //动态代码可以增强也可以拦截
        //1.创建真正干活的人
        ArrayList<String> list = new ArrayList<>();

        //2.创建代理对象
        //参数一:类加载器。当前类名.class.getClassLoader()
        //                 找到是谁,把当前的类,加载到内存中了,我再麻烦他帮我干一件事情,把后面的代理类,也加载到内存

        //参数二:是一个数组,在数组里面写接口的字节码文件对象。
        //                  如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截
        //                  但是,一定要写ArrayList真实实现的接口
        //                  假设在第二个参数中,写了MyInter接口,那么是错误的。
        //                  因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截
        //参数三:用来创建代理对象的匿名内部类
        List proxyList = (List) Proxy.newProxyInstance(
                //参数一:类加载器
                MyProxyDemo1.class.getClassLoader(),
                //参数二:是一个数组,表示代理对象能代理的方法范围
                new Class[]{List.class},
                //参数三:本质就是代理对象
                new InvocationHandler() {
                    @Override
                    //invoke方法参数的意义
                    //参数一:表示代理对象,一般不用(了解)
                    //参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截
                    //参数三:就是下面第三步调用方法时,传递的参数。
                    //举例1:
                    //list.add("阿玮好帅");
                    //此时参数二就是add这个方法名
                    //此时参数三 args[0] 就是 阿玮好帅
                    //举例2:
                    //list.set(1, "aaa");
                    //此时参数二就是set这个方法名
                    //此时参数三  args[0] 就是 1  args[1]"aaa"
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //对add方法做一个增强,统计耗时时间
                        if (method.getName().equals("add")) {
                            long start = System.currentTimeMillis();
                            //调用集合的方法,真正的添加数据
                            method.invoke(list, args);
                            long end = System.currentTimeMillis();
                            System.out.println("耗时时间:" + (end - start));
                            //需要进行返回,返回值要跟真正增强或者拦截的方法保持一致
                            return true;
                        }else if(method.getName().equals("remove") && args[0] instanceof Integer){
                            System.out.println("拦截了按照索引删除的方法");
                            return null;
                        }else if(method.getName().equals("remove")){
                            System.out.println("拦截了按照对象删除的方法");
                            return false;
                        }else{
                            //如果当前调用的是其他方法,我们既不增强,也不拦截
                            method.invoke(list,args);
                            return null;
                        }
                    }
                }
        );

        //3.调用方法
        //如果调用者是list,就好比绕过了第二步的代码,直接添加元素
        //如果调用者是代理对象,此时代理才能帮我们增强或者拦截

        //每次调用方法的时候,都不会直接操作集合
        //而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截
        proxyList.add("aaa");
        proxyList.add("bbb");
        proxyList.add("ccc");
        proxyList.add("ddd");

        proxyList.remove(0);
        proxyList.remove("aaa");


        //打印集合
        System.out.println(list);
    }
}

附录

  1. 相关代码: https://gitee.com/Blue_Pepsi_Cola/java-base-review
  2. 课程地址: https://www.bilibili.com/video/BV1ke4y1w7yn?p=9&vd_source=240d9002f7c7e3da63cd9a975639409a
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boy快快长大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值