Java之反射

什么是反射

  • Java中的反射(Reflection)是指在程序运行时动态地获取类的信息(如类名、方法、属性等),并能够操作对象的方法和属性,而不需要预先知道这些信息。反射使得程序具有更高的灵活性和可扩展性,但也会带来一定的性能开销。

反射的种类

类名说明
Field类类的成员变量
Method类类的方法
Constructor类的构造方法

反射获取Class对象的三种方式

  • Class.forName("全类名");
  • 类名.class
  • 对象.getClass();
package Demo;

public class Person {
    String name;

    int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

import org.junit.Test;

public class ReflectionTest {

    //第一种方式:Class.forName
    // 全类名: 包名+类名 (全类名)   通常用第一种方式
    @Test
    public void show() throws ClassNotFoundException {
        //Class.forName("全类名").var
        Class<?> aClass = Class.forName("Demo.Person");
        System.out.println(aClass); //class Demo.Person
    }


    //第二种方式:类名.class
    // 通常在多线程中使用 用来当对象锁 synchronized
    @Test
    public void show1() {
        //Person.class.var
        Class<Person> personClass = Person.class;
        System.out.println(personClass); //class Demo.Person
    }

    //第三种方式: 对象.getClass
    //有了这个类当对象的时候才可以使用
    @Test
    public void show2() {
      //先实例化对象 通过对象去调用getClass
        Person p = new Person();

        //p.getClass().var
        Class<? extends Person> aClass = p.getClass();
        System.out.println(aClass);   //class Demo.Person
    }
}

反射获取构造方法

属性说明
newInstance创建对象
getConstructors获取公共的构造方法
getDeclaredConstructor获取空参的构造方法
getDeclaredConstructors获取所有的构造方法
public class Person {
    String name;

    private int age;

    public Person() {
    }

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

    //受保护的构造方法
    protected Person(int age) {
        this.age = age;
    }

    //私有构造方法 通过暴力反射可以进行访问
    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class ReflectionTest02 {
    public static void main(String[] args) throws Exception {
        //1.获取Class字节码文件对象
        Class aClass = Class.forName("Demo.Person");

        //2.获取公共的构造方法
        Constructor[] constructors = aClass.getConstructors();

        //3.获取所有的构造方法
        Constructor[] declaredConstructors = aClass.getDeclaredConstructors();

        //4.获取空参的构造方法
        Constructor dc = aClass.getDeclaredConstructor();

        //5.获取Sring构造方法
        Constructor dc1 = aClass.getDeclaredConstructor(String.class);

        //6.获取int构造方法
        Constructor dc2 = aClass.getDeclaredConstructor(int.class);

        //7.获取String和int构造方法
        Constructor dc3 = aClass.getDeclaredConstructor(String.class, int.class);

        //8.通过7来创建对象
        //setAccessible:暴力反射 取消权限检验
        dc3.setAccessible(true);
        //创建对象
        Person person = (Person) dc3.newInstance("阿乐的小屋", 22);
        System.out.println(person);
    }
}

反射获取成员变量

属性说明
getFields获取公开的成员变量对象
getDeclaredFields获取所有成员变量对象
getDeclaredField获取单个成员变量
getModifiers获取权限修饰符
getName获取成员变量的名字
getType获取成员变量的数据类型
setAccessible取消修饰的权限检测
public class Person {
    private String name;
    private int age;
    public int id;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}
public class ReflectionTest02 {
    public static void main(String[] args) throws Exception {
        //1.获取Class字节码文件的对象
        Class aClass = Class.forName("Demo.Person");

        //2.获取公开的成员变量的对象
        Field[] fields = aClass.getFields(); //public int Demo.Person.id 数组

        //3.获取所有的成员变量对象
        Field[] fields1 = aClass.getDeclaredFields(); 

        //4.获取单个成员变量
        Field name = aClass.getDeclaredField("name");

        //5.获取权限修饰符
        int modifiers = name.getModifiers(); //2

        //6.获取成员变量的名字
        String name1 = name.getName(); //name

        //7.获取成员变量的数据类型
        Class<?> type = name.getType(); //class java.lang.String

        //8.获取成员变量记录的值
        Person p = new Person("blogdx.com",22,1);
        name.setAccessible(true);
        String value = (String) name.get(p); //blogdx.com

        //修改对象里面记录的值
        name.set(p,"阿乐的小屋"); //Person{name='阿乐的小屋', age=22, id=1}

        //获取成员变量int,将int修改为20
        //获取age的成员变量
        Field age = aClass.getDeclaredField("age");
        //暴力反射取消权限检测
        age.setAccessible(true);
        //读取age的值
        int i1 = (int) age.get(p); //22
        //修改age的值
        age.set(p,20);

        System.out.println(p); //Person{name='阿乐的小屋', age=20, id=1}
    }
}

反射获取成员方法

属性说明
getMethods获取所有方法对象包含父类方法
getDeclaredMethods获取本类所有方法对象包含私有方法
getDeclaredMethod获取指定单一方法
getModifiers获取方法的修饰符
getName获取方法的名字
getParameters获取方法的形参
getExceptionTypes获取方法抛出的异常
invoke("实例","参数")指定实例运行方法
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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


    public void sleep(){
        System.out.println("睡觉");
    }
    
    //私有方法
    private void eat (String name) throws RuntimeException,NullPointerException{
        System.out.println(name + "在吃东西");
    }

    //公开方法有返回值
    public String fly(String name){
        System.out.println(name + "在飞翔");
        return  "在飞翔";
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ReflectionTest02 {
    public static void main(String[] args) throws Exception {
        //获取class字节码文件对象
        Class aClass = Class.forName("Demo.Person");

        //1.获取所有的方法对象(包含父类的公共方法)
        Method[] methods = aClass.getMethods();

        //2.获取本类里面的所有方法和对象(包含私有的方法)
        Method[] methods1 = aClass.getDeclaredMethods();

        //3.获取指定的单一方法
        Method m = aClass.getDeclaredMethod("eat", String.class); //private void Demo.Person.eat(java.lang.String)

        //4.获取方法的修饰符
        int modifiers = m.getModifiers(); //2

        //5.获取方法的名字
        String name = m.getName();
        System.out.println(name);

        //6.获取方法的形参
        Parameter[] parameters = m.getParameters(); //java.lang.String arg0

        //7.获取方法抛出的异常
        Class<?>[] exceptionTypes = m.getExceptionTypes();//class java.lang.RuntimeException class java.lang.NullPointerException

        //8.运行方法
        Person p = new Person();
        m.setAccessible(true);
        //p是方法的调用者
        //"张三" 在调用方法的时候传入的参数
        m.invoke(p,"张三");

        //9.获取方法的返回值
        Method f = aClass.getDeclaredMethod("fly", String.class);

        Person pp = new Person();
        //返回值
        String bird = (String) f.invoke(pp, "鸟儿");
        System.out.println(bird); //在飞翔

    }
}

反射的好处

  • 作用:获取任意一个类中的所有信息 结合配置文件动态创建对象
  1. 动态性:反射允许程序在运行时动态地获取类的信息,从而可以根据需要创建对象、调用方法、访问属性等。
  2. 灵活性:通过反射可以访问类的私有成员,以及无法直接访问的成员,从而增强了程序的灵活性。
  3. 扩展性:反射机制使得程序可以利用外部的类或者对象,而不需要在编译时就将其引入到程序中。
  4. 逆向工程:反射允许程序在运行时检查类的结构,进而进行逆向工程,例如生成Java代码、序列化和反序列化等。
  5. 框架开发:许多框架都使用了反射,例如Spring框架就利用反射来实现依赖注入、AOP等功能。这些框架因此具有高度的灵活性和可扩展性。

作业练习

  • 创建一个Person类,用反射创建对象并且将内容保存到本地文本中
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class PersonTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        //1.获取字节码文件
        Class aClass = Class.forName("Demo.Person");

        //2.创建缓冲流
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));

        //3.获取成员变量
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            //4.获取构造方法
            Constructor con = aClass.getDeclaredConstructor(String.class, int.class);
            Person p = (Person) con.newInstance("阿乐的小屋",22);

            field.setAccessible(true);
            bw.write(field.getName() + "=" + field.get(p));
            bw.newLine();
        }

        bw.close();
    }
}
  1. 通过 Class.forName() 方法获取类 Demo.Person 的字节码文件。
  2. 创建缓冲流BufferedWriter 并指定输出文件 a.txt。
  3. 通过 aClass.getDeclaredFields() 方法获取类 Demo.Person 中所有声明的成员变量。
  4. 遍历 fields 数组,对于每个成员变量,通过 getDeclaredConstructor() 方法获取构造方法,然后利用 newInstance() 方法创建一个新的 Person 对象。
  5. 调用 field.setAccessible(true) 方法,使得可以访问非公有成员变量。然后利用 field.getName() field.get() 方法分别获取成员变量的名称和值,并将其写入文件 bw 中。
  6. 关闭缓冲流 bw。
  • 创建一个properties配置文件 动态的调用配置文件中的方法输出到控制台 配置文件内容如下
classname=Demo.Person
method=show
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    //方法一
    public void show(){
        System.out.println("阿乐在学习");
    }

    //方法二
    public void run(){
        System.out.println("阿乐的小屋");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class PersonTest02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        //1.创建一个io流读取配置文件
        FileInputStream fis = new FileInputStream("prop.properties");

        Properties prop = new Properties();
        //将配置文件加载到prop中
        prop.load(fis);
        //关闭流
        fis.close();

        //prop.get获取键
        String classname = (String) prop.get("classname");
        String method = (String) prop.get("method");


        //3.获取class字节码对象
        Class<?> aClass = Class.forName(classname);

        //4.获取方法
        Method show = aClass.getDeclaredMethod(method);

        //4.实例化Person类
        Person p = new Person();
        //运行方法
        show.invoke(p);
    }
}
  1. 通过 FileInputStream 类创建一个输入流对象 fis,用于读取配置文件 "prop.properties"
  2. 创建 Properties 对象 prop,将配置文件加载到 prop 中。这样就可以使用 prop.get 方法获取键对应的值(类名和方法名)。
  3. 调用 Class.forName 方法获取指定类名 classname 的 Class 对象 aClass
  4. 使用 aClass.getDeclaredMethod 方法获取指定方法名 method 的 Method 对象 show
  5. 实例化 Person 类并用 show.invoke(p) 方法调用指定的方法。
最后修改:2023 年 04 月 02 日
如果觉得我的文章对你有用,请随意赞赏