一、反射机制概念

  1. 通过java语言中的反射机制可以操作字节码文件。
  2. 通过反射机制可以操作代码片段。
  3. java.lang.reflect包下

    二、反射机制对应的类

  4. java.lang.Class代决整个宁节码,代丧一个类型,代表整个类。

  5. java,lang.reflect.Method代表字节码中的方法字节码。代表类中的方法。
  6. java.lang.reflect.Constructor.代表字节码中的构造方法字书码,代表类中的构造方法
  7. java.lang.reflect.Field代表字节码中的属性字节码。代表类中的成员变量静态变盘十实例变量)。

三、获取Class的三种方式

返回值为字节码对象 Class类型

  1. 第一种

    Class = Class.forname(“全类名”);

  2. 第二种

    对象.getClass(); Class objectClass = object.getClass();

  3. 第三种

    类.Class属性

四、通过反射机制相关操作

4.1 通过反射机制访问、赋值对象的属性
  1. 获取字节码对象,只能获取公共的属性,默认、私密都不能获取。 ```java Class userClass = Class.forName(“com.eaglslab.pojo.User”);

Field fieldName = userClass.getField(“name”);//只能获取公共的属性 Field fieldId = userClass.getField(“id”);//只能获取公共的属性

//获取属性字节码对象数组(只能获取公有的属性) Field[] fields = userClass.getFields();

  1. 2. 注意getField(“需要加入想要获取的属性”)
  2. 2. getFields()获取所有公共的属性;
  3. 2. 获取所有属性:包括私有的属性
  4. ```java
  5. //获取属性字节码对象数组(获取所有属性)
  6. Field[] declaredFields = userClass.getDeclaredFields();
  7. //获取属性字节码对象数组(获取所有属性)
  8. Field[] declaredFields = userClass.getDeclaredFields();
  1. 反射机制:通过反射机制可以给属性赋值、获取属性的修饰符、值。

    1. //获取属性的属性名称
    2. String fieldName = declaredField.getName();
    3. System.out.println("属性的属性名称为 "+fieldName);
    4. Class type = declaredField.getType();
    5. System.out.println("属性的类型为 "+type.getSimpleName());
    6. //获取属性的修饰符
    7. int modifiers = declaredField.getModifiers();//返回修饰符的代号
    8. //将修饰符的代号转换成修饰符字符串
    9. String toString = Modifier.toString(modifiers);

    4.2 通过反射机制创建实例对象
  2. 通过操作字节码创建一个实例,在学习框架时常用

    1. //通过反射机制创建一个类的实例
    2. Object instance = userClass.newInstance();

    通过反射机制创建对象需要注意:newinstance方法需要对应的类(就是全类名对应的类)需要有无参构造否则会报错。

  3. 使用反射机制获取修改属性,属性实例对象赋值

注意流程:
获取字节码对象—>通过字节码对象创建对象—>获取属性—>(打破封装)—>给属性赋值

  1. //创建字节码对象
  2. Class userClass = Class.forName("com.eaglslab.pojo.User");
  3. //通过反射机制创建一个类的实例
  4. Object instance = userClass.newInstance();
  5. //获取属性
  6. Field name = userClass.getDeclaredField("name");
  7. Field id = userClass.getDeclaredField("id");
  8. //给属性赋值的方法注意赋值的参数(对象,值)
  9. name.set(instance,"张三");
  10. //打破封装 可以对私有属性进行赋值,先打破封装
  11. id.setAccessible(true);
  12. //给私有的属性赋值
  13. id.set(instance,100);
  1. 获取方法的相关属性

    由于java有重载,所有一个方法名不能指定方法,需要传入参数; 注意传入的参数需要传入参数类型 Method declaredMethod = userClass.getDeclaredMethod(“login”, String.class, String.class);

  1. //创建字节码对象
  2. Class userClass = User.class;
  3. //获取方法字节码对象
  4. //由于java有重载,所有一个方法名不能指定方法,需要传入参数;
  5. //注意传入的参数需要传入参数类型
  6. Method declaredMethod = userClass.getDeclaredMethod("login", String.class, String.class);
  7. //获取方法的返回值类型
  8. System.out.println(declaredMethod.getReturnType().getSimpleName());
  9. //获取方法的修饰符
  10. System.out.println(Modifier.toString(declaredMethod.getModifiers()));
  11. System.out.println("------------------------------------------------");
  12. //获取方法的参数
  13. Class[] parameterTypes = declaredMethod.getParameterTypes();
  14. for (Class parameterType : parameterTypes) {
  15. System.out.println(parameterType.getSimpleName());
  16. }

4.3 通过反射机制调用方法
  1. 通过反射调用方法 ```java //创建字节码对象 Class userClass = User.class;

//通过反射来创建实例 Object instance = userClass.newInstance();

//获取方法字节码对象 Method declaredMethod = userClass.getDeclaredMethod(“login”, String.class, String.class);

//通过反射来调用方法 Object invoke = declaredMethod.invoke(instance, “zs002”, “123456”);

System.out.println(invoke);

  1. <a name="QOikL"></a>
  2. ##### 4.1 通过反射机制调用构造方法进行构造
  3. 10. 构造字节码对象
  4. 获取构造getDeclaredConstructor
  5. ```java
  6. //创建字节码对象
  7. Class userClass = User.class;
  8. //创建实例
  9. Object instance2 = userClass.newInstance();
  10. //获取构造字节码对象
  11. Constructor declaredConstructor = userClass.getDeclaredConstructor(Integer.class, String.class);
  12. Object instance = declaredConstructor.newInstance(100, "jack");
  13. Constructor declaredConstructor1 = userClass.getDeclaredConstructor();
  14. Object instance1 = declaredConstructor1.newInstance();
  15. System.out.println(instance1);
  16. }

五、反射在以后的用途

  1. 反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置。
  2. 当我们从容器中获Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。还有在struts2的struts.xml中配置action,也是通过反射调用的action
  3. 通过反射机制可以解耦,降低耦合,通过操作字节码的形式创建实例。相对于new对象来说可以降低类与类之间的耦合。
  4. 实际开发种使用少,但是可以帮助理解各大框架的底层。

六、案例

需求:写一个工具类,将java对象属性+值拼接为一个json串。
设计流程:

  1. 创建字符串对象
  2. 创建字节码对象
  3. 获取全部属性保存入数组
  4. 遍历数组 循环获取属性名与属性值,进行拼接,过程中注意json格式,字符串需要添加“”。

    1. public static String reflectToJson(Object object){
    2. //创建StringBuilder对象,用于完成字符串的追加
    3. StringBuilder stringBuilder = new StringBuilder();
    4. //获取字节码对象
    5. Class objectClass = object.getClass();
    6. //通过反射机制来获取全部属性的信息
    7. Field[] declaredFields = objectClass.getDeclaredFields();
    8. stringBuilder.append("{");
    9. //遍历属性
    10. for (Field declaredField : declaredFields) {
    11. //打破封装
    12. declaredField.setAccessible(true);
    13. //获取属性的名称
    14. String fieldName = declaredField.getName();
    15. //定义一个属性值
    16. Object value = null;
    17. try {
    18. //获取属性值
    19. value = declaredField.get(object);
    20. } catch (IllegalAccessException e) {
    21. e.printStackTrace();
    22. }
    23. stringBuilder.append(fieldName+":");
    24. //判断属性的数据类型是不是String
    25. if (declaredField.getType().getSimpleName().equals("String")){
    26. stringBuilder.append("\""+value+"\"");
    27. }else {
    28. stringBuilder.append(value);
    29. }
    30. stringBuilder.append(",");
    31. }
    32. //切割最后一个逗号
    33. stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
    34. stringBuilder.append("}");
    35. return stringBuilder.toString();
    36. }

七、注解

不会影响语义、但是会影响语句的执行。例如@overRide

7.1 什么是注解:
  1. 是元数据(Annotation) ,是一种对代码进行说明的数据,JDK1.5 入的新规 范,是与类、按口、枚举等<br /> 引用类型处于同个层次, 可以用在包、类、属性、方法、方法的谷数和局部变量等的前边。不影响代码的语义,可以对其进行解析。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26800293/1652708802909-86365d89-3bbd-44fb-8c31-6fc5e3d9b402.png#clientId=u1749e917-abf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=181&id=u82e680a3&margin=%5Bobject%20Object%5D&name=image.png&originHeight=181&originWidth=332&originalType=binary&ratio=1&rotation=0&showTitle=false&size=6823&status=done&style=none&taskId=ue9e41c23-99bc-4480-989e-755ed1fc473&title=&width=332)<br />元注解:注解的注解;

7.2 注解的基本语法
  1. 在注解中可以定义属性(只能是八大数据类型+枚举数据类型),注解可以拥有属性,但不能定义方法。
  2. 在注解声明了属性后,当该注解去修饰元素时一定要给属性赋值除非有定义默认值。

    赋值方式: 在使用时进行注解的赋值操作 @注解名 (name = “czf”)
    默认值方式 String name()default “默认值”

  3. 在注解中定义属性、只能是八大基本数据类型、String类型,枚举类型、以上类型所对应的数组

  4. 在定义属性时,如果属性名称时value,在给该属性赋值的时候,属性名称可以省略不写

JDK内置注解

7.3 元注解:注解的注解

注解与反射机制会配合使用。
@Target 使注解只能修饰特顶的属性 设置注解的作用域。
@Retention 设置保持性策略
-1、SOURCE设置当前注解在类加载时只 保留在源码层面,不会生产字节码对象,不可以被反射机制所获
-2、CLASS市在类加载时可以生产宇乃码文件,但是不能被反射机制获牧
-3、RUNTIME在类加花时可仪生成字节码文件,也可以被反时机制获取

  1. @Target({ElementType.FIELD, ElementType. TYPE})
  2. @Retention(RetentionPolicy. RUNTIME)
  3. public @interface MyAnnotation02 {
  4. }

7.4 注解 案例
  1. 需求:自定义一个注解,该注解可以被反射机制所获取。

    1. 如果在一个类上加上了该注解,那么该类必须包含一个行业私有id属性,如果<br /> 不包含会报错。
  2. 设计流程:主要就是通过字节码获取类中的属性、以及由Modifier获取类中获取属性修饰符。通过遍历获取的属性数组,判断是否包含有属性id,以及访问权限修饰符是否包含私有。