Java反射和注解

简介

被称为框架设计的灵魂

  • 介绍:将类的各个组成部分封装为其他对象,称为反射机制
  • 好处:
    1. 可以在程序运行过程中,操作这些对象
    2. 可以解耦,提高程序可扩展性

获取Class对象的方式

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
    • 多用于配置文件,将类定义在配置文件中,读取文件,加载类
      1
      Class cls1 = Class.forName("io.jtxyh.demo.Person");
  2. 类名.class:通过类名的属性class获取
    • 多用于参数的传递
      1
      Class cls2 = Person.class;
  3. 对象.getClass():getClass()方法在Object类中定义,所有对象都有这个方法
    • 多用于对象获取字节码的方式
      1
      2
      Person p = new Person();
      Class cls3 = p.getClass();
  • 结论:同一个字节码文件(*.class)在一次程序的运行过程中,只会被加载一次,不论是通过哪一种方式获取的Class对象都是同一个

使用Class对象功能

  1. 获取成员变量
    • Field[] getFields():获取所有public修饰的成员变量
    • Field getField(String name):获取指定名称public修饰的成员变量
    • Field[] **getDeclaredFields()**:获取所有的成员变量
    • Field getDeclaredField(String name):获取指定名称的成员变量
      1
      2
      3
      4
      Field[] fie1 = cls1.getFields();
      for (Field field : fie1) {
      System.out.println(field); // public java.lang.String io.jtxyh.demo.Person.a
      }
  2. 获取构造方法
    • Constructor<?>[] getConstructors():获取所有public修饰的构造器
    • Constructor<T> getConstructor(类<?>… parameterTypes):获取一个指定名称public修饰的构造器
    • Constructor<?>[] **getDeclaredConstructors()**:获取所有的构造器
    • Constructor<T> getDeclaredConstructors(类<?>… parameterTypes):获取所有指定名称的构造器
      1
      2
      3
      Constructor cos1 = cls1.getConstructor(String.class,Integer.class); // 获取有参的
      Object p2 = cos1.newInstance("张三",100);
      System.out.println(p2);
  3. 获取成员方法
    • Method[] getMethods():获取所有public修饰的成员方法
    • Method getMethods(String name, 类<?>… parameterTypes):获取一个指定名称public修饰的成员方法
    • Method[] **getDeclaredMethods()**:获取所有的成员方法
    • Method getDeclaredMethods(String name, 类<?>… parameterTypes):获取所有指定名称的成员方法
      1
      2
      Method meth2 = cls1.getMethod("eat", String.class); // 指定一个有一个参数的eat方法
      meth2.invoke(person, "西瓜");
  4. 获取类名
    • String **getName()**:
      1
      String name = meth2.getName();
  • Field:成员变量
    1. 设置值:void set(Object obj, Object value)
    2. 获取值:get(Object obj)
    3. 忽略访问权限修饰符的安全检查:setAccessible(true) 暴力反射
  • Constructor:构造方法
    1. 创建对象:T newInstance(Object… initargs)
    2. 如果是用空参数构造方法创建对象,直接可以使用:Class对象的newInstance方法
  • Method:方法对象
    1. 执行方法:Object invoke(Object obj, Object…args)
    2. 获取方法名称:String getName

注解

  • 定义:注解(Annotation),也叫元数据。一种代码级别的说明,是JDK1.5以后引入的一个新特性,与类、接口、枚举在同一层次。可以声明包、类、字段、方法、局部变量、方法参数的前面,用来对元素进行说明,注释
    1. JDK1.5后的新特性
    2. 说明程序
    3. 使用注解方法:**@注解名称**

作用分类

  1. 编写文档:通过代码里标识的注解生成doc文档
  2. 代码分析:通过代码里标识的注解进行分析(使用反射)
  3. 编译检查:通过代码里标识的注解让编译器能够时间基本的编译检查(@Override)

JDK预定义注解

  1. @Override:检测被注解标注的方法是否继承自父类(接口)的
  2. @Deprecated:该注解标注的内容,表示已过时
  3. @SuppressWarnings压制警告
    • 一般传递一个参数all @SuppressWarnings(“all”)

自定义注解

  • 格式
    1
    2
    3
    4
    '元注解'
    public @interface 注解名称{
    '属性列表';
    }
  • 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
    • public interface MyAnno extends java.lang.annotation.Annotation {}
  • 属性:接口中可以定义的成员方法注解中都可以定义,就是接口中的抽象方法
    1. 属性的返回值类型要求
      • 基本数据类型
      • String
      • 枚举
      • 注解
      • 以上类型的数组
    2. 定义了属性,在使用时需要给属性赋值
      • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行赋值
      • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
      • 数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}可以省略
1
2
3
4
5
6
7
8
9
10
11
12
13
'定义注解'
public @interface MyAnno{
int value(); // 整数型
Person per(); // 枚举类型
MyAnno2 anno2(); // 注解型
String[] strs(); // 数组型
}

'属性赋值'
@MyAnno(value = 12, per = Person.P1, anno2 = @MyAnno2, strs={"abc","cde"})
public class Demo{

}

元注解

用于描述注解的注解

  1. @Target:描述注解能够作用的位置
    • ElementType取值:
      1. TYPE:可以作用于
      2. METHOD:可以作用于方法
      3. FIELD:可以作用于成员变量
  2. @Retention:描述注解被保留的阶段
    • @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
  3. @Documented:描述注解是否被抽取到api文档中
  4. @Inherited:描述注解是否被子类继承

使用(解析)注解

获取注解中定义的属性值

  1. 获取注解定义的位置的对象,**(Class,Method,Field)**
  2. 获取指定的注解
    • getAnnotation(Class)
  3. 调用注解中的抽象方法获取配置的属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
'注解类'
@Target(ElementType.TYPE) // 作用于类上
@Retention(RetentionPolicy.RUNTIME) // 能被JVM读取到
public @interface MyTestAnno {
String name();
String age();
}

'==============================================='

'一个对象'
package io.jtxyh.annotation;

public class Person {
public void show() {
System.out.println("show .....");
}
}
'==============================================='

'使用注解执行一个类中的方法'
@MyTestAnno(name = "io.jtxyh.annotation.Person",age="show") // 指定注解
public class MyTestAnnoTest {
public static void main(String[] args) throws Exception {
'解析注解'
'获取该类的字节码文件对象'
Class<MyTestAnnoTest> class1 = MyTestAnnoTest.class;

' 获取上边的注解对象'
MyTestAnno anno = class1.getAnnotation(MyTestAnno.class);

' 调用注解对象的方法'
String className = anno.name();
String classAge = anno.age();

' 加载该类进内存'
Class class2 = Class.forName(className);

' 创建对象'
Object obj = class2.newInstance();

' 获取方法发对象'
Method method = class2.getMethod(classAge);

' 执行方法'
method.invoke(obj);
}
}

相关文章

Spring注解@NotNull等

JVM内存与垃圾回收