[Java基础]反射技术

反射

反射就是拿到类文件, 然后操作类的成分(成员变量, 方法, 构造器等)

反射具体学什么?

通过加载类文件, 获取类的成分信息, 操作他们

  1. 反射第一步: 加载类,获取类的字节码 (Class对象)
  2. 获取类的构造器: Constructor对象
  3. 获取类的成员变量: Field对象
  4. 获取类的成员方法: Method对象

反射第一步: 加载类,获取类的字节码 (Class对象)

public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {
        // 目标: 获取类的Class对象 (类文件)
        // 1.通过类名获取类: 类.class
        Class c1 = Student.class;
        System.out.println(c1); // class com.itheima.Student

        // 2.通过Class提供的方法获取类: Class.forName("类的全名称")
        Class c2 = Class.forName("com.itheima.Student");
        System.out.println(c2); // class com.itheima.Student

        // 3.通过Object提供的方法获取类: 对象.getClass()
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3); // class com.itheima.Student

        // 类文件在系统中只有一份, 三种方式拿到的类文件都是同一个class对象
        System.out.println(c1 == c2); // true
        System.out.println(c2 == c3); // true
    }
}

获取类的构造器: Constructor对象

Class提供了从类中获取构造器的方法

使用获取到的构造器: 初始化对象返回

public class GetClassInfo {
    public static void main(String[] args) throws Exception {
        // 目标: 通过反射获取类的所有信息
        // 1.获取类文件的信息
        getClassInfo();
        // 2.获取类的构造器
    }

    // 2.获取类的构造器
    private static void getConstructors() throws Exception {
        Class c1 = Student.class;
        // 2.1获取类的全部构造器
        Constructor[] sons = c1.getDeclaredConstructors();
        for (Constructor son : sons) {
            System.out.println(son.getName() + "(" + son.getParameterCount() + "个参数)");
        }

        // 2.2获取单个构造器
        Constructor son = c1.getDeclaredConstructor(); // 获取无参构造器
        System.out.println(son.getName() + "(" + son.getParameterCount() + "个参数)");

        Constructor son2 = c1.getDeclaredConstructor(String.class, int.class); // 获取有参构造器
        System.out.println(son2.getName() + "(" + son2.getParameterCount() + "个参数)");

        // 2.3获取构造的作用就是创建对象
        Student s1 = (Student) son.newInstance();
        System.out.println(s1);

        // 2.4暴力反射 可以访问私有的构造器,成员变量和方法
        son2.setAccessible(true);
        Student s2 = (Student) son2.newInstance("张三", 19);
        System.out.println(s2);
    }

    // 1.获取类文件的信息
    private static void getClassInfo() {
        Class c1 = Student.class;
        System.out.println(c1.getName()); // 获取类的全类名 com.itheima.Student
        System.out.println(c1.getSimpleName()); // 获取类的简类名 Student
    }
}

获取类的成员变量: Field对象

Class提供了从类中获取成员变量的方法

为成员变量取值/赋值

public class GetClassInfo {
    public static void main(String[] args) throws Exception {
        // 目标: 通过反射获取类的所有信息
        // 3.获取类的成员变量
        getFields();
        // 4.获取类的成员方法
        getMethods();

    }

    // 3.获取类的成员变量
    private static void getFields() throws Exception {
        Class c1 = Student.class;
        // 3.1获取类的全部成员变量
        Field[] fields = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + "(" + field.getType().getName() + ")");
        }

        // 3.2获取单个成员变量
        Field name = c1.getDeclaredField("name"); // 获取name成员变量
        System.out.println(name.getName() + "(" + name.getType().getName() + ")");

        Field age = c1.getDeclaredField("age");
        System.out.println(age.getName() + "(" + age.getType().getName() + ")");

        // 3.3获取成员变量的作用就是取值赋值
        Student student = new Student(); // 得到学生对象
        name.setAccessible(true); // 暴力反射: 绕过访问权限修饰符
        name.set(student, "李四"); // 变量赋值
        age.set(student, 18); // 变量赋值

        String sname = (String) name.get(student); // 变量取值
        int sage = (int) age.get(student); // 变量取值
    }
}

获取类的成员方法: Method对象

Class提供了从类中获取成员方法的API

执行获取到的方法

package com.itheima;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class GetClassInfo {
    public static void main(String[] args) throws Exception {
        // 目标: 通过反射获取类的所有信息
        // 4.获取类的成员方法
        getMethods();

    }

    // 4.获取类的成员方法
    private static void getMethods() throws Exception {
        Class c1 = Student.class;
        // 4.1获取类的全部成员方法
        Method[] methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "(" + method.getParameterCount() + "个参数)");
        }

        // 4.2获取单个成员方法
        Method method = c1.getDeclaredMethod("setName", String.class); // 获取setName方法
        System.out.println(method.getName() + "(" + method.getParameterCount() + "个参数)");

        // 4.3获取成员方法的目的就是执行该方法
        Student student = new Student();
        method.setAccessible(true); // 暴力反射: 绕过访问权限修饰符
        method.invoke(student, "张三"); // 执行setName方法
    }

}

反射的作用

  1. 可以得到一个类的全部成分然后操作。

  2. 可以破坏封装性。

  3. 可以绕过泛型的约束

  4. 主流的框架都会基于反射设计出一些通用的功能。

    public class Demo {
    public static void main(String[] args) throws Exception {
    // 目标: 了解反射的作用
    ArrayList<String> list = new ArrayList<>();
    list.add("张无忌");
    list.add("令狐冲");
    list.add("东方不败");

         // 通过反射绕过泛型的约束
         Class c1 = list.getClass();
         Method add = c1.getDeclaredMethod("add",Object.class);
         add.invoke(list,98);
         add.invoke(list,true);
    
         System.out.println(list); // [张无忌, 令狐冲, 东方不败, 98, true]
     }
    

    }

案例

需求: 对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去

  1. 定义一个方法, 接收任意对象

  2. 通过反射获取对象的Class对象, 然后获取全部的成员变量

  3. 遍历成员变量, 然后提取成本成员变量的值

  4. 把成员变量名, 变量值, 写入到文件中

    public class Demo {
    public static void main(String[] args) throws Exception {
    // 反射是框架的核心技术
    Dog dog = new Dog("哈士奇", 4);
    Student student = new Student("小明", 19, 100);

         SaveObjectFrameWork.saveObject(dog); // 保存dog对象到文件中
         SaveObjectFrameWork.saveObject(student); // 保存student对象到文件中
     }
    

    }

    public class SaveObjectFrameWork {
    // 保存任意对象的静态方法
    public static void saveObject(Object obj) throws Exception {
    // 打印流
    PrintStream ps = new PrintStream(new FileOutputStream("day8-reflect\src\com\itheima\test3\obj.txt", true));

         // 获取类文件
         Class c = obj.getClass();
         String simpleName = c.getSimpleName();
         ps.println("==========" + simpleName + "==========");
         // 获取所有的成员变量
         Field[] fields = c.getDeclaredFields();
         for (Field field : fields) {
             String fieldName = field.getName(); // 获取变量名
             field.setAccessible(true); // 暴力反射, 绕过权限修饰符
             Object fileValue = field.get(obj); // 获取变量值
             ps.println(fieldName + "=" + fileValue);
         }
         ps.close();
     }
    

    }

相关推荐
Tisfy10 分钟前
LeetCode 1206.设计跳表:算法详解
算法·leetcode·职场和发展
阳光男孩0110 分钟前
力扣2454. 下一个更大元素 IV
算法·leetcode·职场和发展
tnnnnt19 分钟前
今天锐评一下C#
开发语言·c#
夏天的味道٥21 分钟前
36. Spring Boot 2.1.3.RELEASE 中实现监控信息可视化并添加邮件报警功能
java·spring boot·后端
oneouto30 分钟前
项目实践 之 pdf简历的解析和填充(若依+vue3)
java·vue
Knock man42 分钟前
C++ 文件操作
开发语言·c++
m0_dawn1 小时前
Python 3.11 69 个内置函数(完整版)
开发语言·python·数据分析
you来有去1 小时前
记录一下在k3s快速创建gitlab
java·kubernetes·gitlab
Matlab仿真实验室1 小时前
基于Matlab实现倒立摆仿真程序
开发语言·matlab·倒立摆仿真程序
笨鸟先飞的橘猫1 小时前
lua学习(一)
开发语言·学习·lua