Java反射与自定义注解

反射机制

定义

Oracle官方定义:

1
2
    Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
    The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

程序中一般对象的类型在编译期就确定下来,而Java的反射可以在运行时动态创建对象并获取其属性等相关信息。

用途:

  • 通过反射机制动态访问Java对象的属性、方法、构造方法等

  • JDBC加载驱动

  • Spring框架中IOC实例化对象

  • 自定义注解

  • 第三方框架

    如:mybatis

实例化对象的方式

  1. 直接获取class,通过获取到的class.newInstance();
  2. 通过Class.forName()方法来获取class
  3. 通过new一个对象的getClass()方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test01 {

  public static void main(String[] args) throws Exception {
    // 直接获取class
    Class userEntityClass01 = UserEntity01.class;
    UserEntity01 userEntity01 = (UserEntity01) userEntityClass01.newInstance();
    System.out.println(userEntity01);

    // Class.forName()
    Class userEntityClass02 = Class.forName("com.mayikt.entity.UserEntity01");
    UserEntity01 userEntity02 = (UserEntity01) userEntityClass01.newInstance();
    System.out.println(userEntity02);

      
    UserEntity01 userEntity03 = new UserEntity01();
    System.out.println(userEntity03);

    // 同一个Class对象在虚拟机中只存在一份
    System.out.println(userEntityClass01 == userEntityClass02);
  }

}

在同一个JVM中一个类的class对象,只存在一份

构造函数调用

  • 默认获取的是无参构造函数
  • 通过getDeclaredConstructor()方法可以根据参数类型获取到指定的构造函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Test02 {

  public static void main(String[] args) throws Exception {
    Class userEntityClass02 = Class.forName("com.mayikt.entity.UserEntity01");
    // 默认执行的是无参构造函数
    UserEntity01 userEntity02 = (UserEntity01) userEntityClass02.newInstance();
    System.out.println(userEntity02);

    // 通过参数类型来指定获取哪个构造函数
    // 参数中基本类型和包装类型不能通用如:Integer.class和int.class
    Constructor<?> declaredConstructor = userEntityClass02.getDeclaredConstructor(String.class, int.class);
    UserEntity01 userEntity03 = (UserEntity01)declaredConstructor.newInstance("test02", 1);
    System.out.println(userEntity03);
  }
}

操作属性

部分类:

  • Class类:代表类的实体,在运行的Java应用程序中表示类和接口
  • Filed类:代表类的成员变量(成员变量也称为类的属性)
  • Method类:类的方法
  • Constructor类:类的构造方法

部分方法

  • getFiled()getMethod()getConstructor()方法可以获得指定名字的域、方法和构造器

  • getFileds()getMethods()getConstructors():可以获得类提供的public域、方法和构造器数组,

    其中包括超类的共有成员

  • getDeclaredFileds()getDeclaredMethods()getDeclaredConstructors():可以获得类中声明的全部域、方法和构造器

 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
public class Test03 {

  public static void main(String[] args) throws Exception {
    Class userEntityClass02 = Class.forName("com.mayikt.entity.UserEntity01");

    UserEntity01 userEntity01 = (UserEntity01) userEntityClass02.newInstance();

    // getFields()获取自己和父类的公共属性public
    Field[] fields = userEntityClass02.getFields();
    for (Field field : fields) {
      System.out.println(field);
    }

    // 获取自己的所有属性
    Field[] fields02 = userEntityClass02.getDeclaredFields();
    for (Field field : fields02) {
      System.out.println(field);
    }

    // 获取某个属性(公有)
    Field username = userEntityClass02.getDeclaredField("pubUsername");
    username.set(userEntity01, "public username");
    System.out.println(userEntity01.pubUsername);

    // 若是想修改私有属性,需要加上setAccessible(true)
    // username.setAccessible(true);
    Field privateUsername = userEntityClass02.getDeclaredField("username");
    privateUsername.set(userEntity01, "private username");
    System.out.println(userEntity01.pubUsername);
  }
}

在给私有属性赋值时,需要调用setAccessibel(true)方法否则会报如下错误:

1
2
3
4
5
6
Exception in thread "main" java.lang.IllegalAccessException: Class com.reflect.Test03 can not access a member of class com.entity.UserEntity01 with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.set(Field.java:761)
	at com.reflect.Test03.main(Test03.java:35)

操作方法

 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
public class Test04 {

  public static void main(String[] args) throws Exception {
    Class userEntityClass02 = Class.forName("com.mayikt.entity.UserEntity01");

    UserEntity01 userEntity = (UserEntity01) userEntityClass02.newInstance();

    // 获取所有的方法
    Method[] methods = userEntityClass02.getMethods();
    for (Method method : methods) {
      System.out.println(method);
    }

    // 调用指定方法
    // 公有方法
    Method pubMethod = userEntityClass02.getMethod("pubMethod");
    pubMethod.invoke(userEntity);

    // 公有有参方法
    Method pubMethodWithArgs = userEntityClass02.getMethod("pubMethod", String.class);
    pubMethodWithArgs.invoke(userEntity, "小刚");

    // 私有方法
    Method privateMethod = userEntityClass02.getDeclaredMethod("privateMethod");
    privateMethod.setAccessible(true);
    privateMethod.invoke(userEntity);

    // 公有有参方法
    Method privateMethodWithArgs = userEntityClass02.getDeclaredMethod("privateMethod", String.class);
    privateMethodWithArgs.setAccessible(true);
    privateMethodWithArgs.invoke(userEntity, "小刚");
  }
}

越过泛型检查

泛型检查:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Test05 {

  public static void main(String[] args) throws Exception {
    List<String> list = new ArrayList<>();
    list.add("username");

    Class<ArrayList> arrayListClass = ArrayList.class;
    Method addMethod = arrayListClass.getMethod("add", Object.class);
    addMethod.invoke(list, 12365);
    System.out.println(list);
    
    // 遍历使用的时候会抛类型转换异常
    list.forEach(System.out::println);
  }
}

image-20220502082144733

但是上面的例子在遍历使用的时候有会出现java.lang.Integer cannot be cast to java.lang.String

注解

常用注解

  • @OVerride
  • @Deprecated
  • @SuppressWarnings("unchecked") 标注在编译器认为有问题的类、方法上面,用来取消编译器的警告提示。警告类型有serialuncheckedunusedall

元注解

元注解是最基础的几个注解,在声明新的注解的时候需要使用的。

  • @Target:指定新的注解标注的位置,比如类、字段、方法等,取值有ElementType.method
  • @Retention:指定注解的信息保留到什么阶段。取值有RetentionPolicy.RUNTIME
  • @Inherited:指定新注解在父类上时可以被子类继承。

相关内容

0%