写点什么

Java 中的反射与注解

作者:菜皮日记
  • 2023-09-09
    北京
  • 本文字数:3010 字

    阅读完需:约 10 分钟

一、反射 Reflection 和 元类 Class

Class 元类是对普通类的抽象,是类的类。Class 包含了一个类的所有属性,包括类名、包名、父类、实现的接口、所有方法、属性等。拿到一个类的 Class 元类,就拿到了这个类所有信息,就可以通过这些信息动态做一些处理。

通过一个类的 Class 实例获取类信息的方法就称为反射(Reflection)

获取类的 Class 元类对象

方法一:直接通过静态变量class获取:

Class cls = String.class;
复制代码

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

String s = "Hello";Class cls = s.getClass();
复制代码

方法三:如果知道一个类的完整类名,可以通过静态方法Class.forName()获取:

Class cls = Class.forName("java.lang.String");
复制代码

操作 Field 和 Method

拿到了类的 Class 元类实例就是调用如下方法获取类的属性和方法等。

Field getField(name):根据字段名获取某个public的field(包括父类)Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)Field[] getFields():获取所有public的field(包括父类)Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
Method getMethod(name, Class...):获取某个public的Method(包括父类)Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)Method[] getMethods():获取所有public的Method(包括父类)Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
复制代码

操作类的属性,实现动态改变类

得到 Field 对象或 Method 对象后,get set 值时都需要指定一个该类的实例,也就是 get set 针对哪个对象操作。

Demo d = new Demo();d.name = "testname";clazz.getDeclaredField("name").get(d);// 参数 d 就是指定在 d 实例上做 get name 操作clazz.getMethod("hello").invoke(d); // 参数 d 表示在 d 实例上调用 hello 对象
复制代码

通过 Class 生成类实例

方式一:Class 对象的 newInstance 方法

Person p = Person.class.newInstance();
复制代码

调用 Class.newInstance()的局限是,它只能调用该类的 public 无参数构造方法。如果构造方法带有参数,或者不是 public,就无法直接通过 Class.newInstance()来调用。

方式二:反射获取 Constructor 对象

// 获取构造方法,getConstructor方法参数传入类型要与构造方法需要的参数类型相符合。Constructor<Demo> constructor = clazz.getConstructor(String.class, Integer.class);
// 通过 constructor 生成实例Demo aaa = constructor.newInstance("aaa", 123);
// clazz在aaa对象上获取 name 属性clazz.getDeclaredField("name").get(aaa);
复制代码

二、注解的使用

注解分为定义注解,使用注解,解析注解。

注解有点像 Interface,但区别是注解中定义的方法,在使用时是通过属性赋值来使用的。

public @interface MyAnnotation {    // getValue 定义类似 Interface 的方法    // 区别是在注解使用时,不是调用 getValue,而是给 getValue 赋值,未赋值则默认使用 default 值。    String getValue() default "no desc";}
public class Demo {    // 给 getValue 赋值一个字符串    @MyAnnotation(getValue = "annotation on field")    public String name;}
复制代码

元注解

元注解就是给注解用的注解,常用的是 @Target 和 @Retention

Target 声明本注解可用在什么位置,如 ElementType.TYPE, ElementType.FIELD, ElementType.METHOD

Retention 表明本注解保留到什么时候,分为 RetentionPolicy.RUNTIME RetentionPolicy.SOURCE RetentionPolicy.CLASS

如果需要再运行时动态处理的注解,没有声明为 RUNTIME,则再运行时中就反射不到注解了。

解析注解

解析注解就是通过反射 Reflection 获取到一个类的全部信息,包括类上面的注解,再根据注解和其中的属性值进一步做响应的处理。

注解完整三步

定义注解:

package com.sqkb.userasset;
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
/** * @Author songzai * @Date 2021/11/12 15:10 */@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {    // value属性在使用时可以省略 key 名,即不需要写 value = xxx    int value() default 123;
    String getValue() default "no desc";
    String aaaa() default "default aaa";}
复制代码

Demo 实体类,使用注解

package com.sqkb.userasset;
/** * @Author songzai * @Date 2021/11/12 15:10 */// 不写参数名,默认复制给 value()@MyAnnotation(444)public class Demo {    public Demo(String name, Integer age) {        this.name = name;        this.age = age;    }
    @MyAnnotation(getValue = "annotation on field")    public String name;
    public Integer age;
    @MyAnnotation(getValue = "annotation on method")    public void hello() {        System.out.println("hello method");    }
    public static void staticMethod() {        System.out.println("static method");    }
    @MyAnnotation()    public void defaultMethod() {    }}
复制代码

解析注解:

package com.sqkb.userasset;
import java.lang.reflect.Field;import java.lang.reflect.Method;
/** * @Author songzai * @Date 2021/11/12 15:17 */public class AnnotationTest {    public static void main(String[] args) throws Exception {        Class<Demo> clazz = Demo.class;        MyAnnotation annotationOnClass = clazz.getAnnotation(MyAnnotation.class);        System.out.println(annotationOnClass.getValue());        System.out.println(annotationOnClass.aaaa());
        Field name = clazz.getField("name");        MyAnnotation annotation = name.getAnnotation(MyAnnotation.class);        System.out.println(annotation.getValue());        System.out.println(annotation.aaaa());
        Method hello = clazz.getMethod("hello");        MyAnnotation helloAnnotation = hello.getAnnotation(MyAnnotation.class);        System.out.println(helloAnnotation.getValue());        System.out.println(helloAnnotation.aaaa());
        Method defaultMethod = clazz.getMethod("defaultMethod");        MyAnnotation defaultMethodAnnotation = defaultMethod.getAnnotation(MyAnnotation.class);        System.out.println(defaultMethodAnnotation.getValue());    }
}
复制代码


用户头像

菜皮日记

关注

全干程序员 2018-08-08 加入

个人站点:菜皮日记 lipijin

评论

发布
暂无评论
Java 中的反射与注解_Java_菜皮日记_InfoQ写作社区