Hi,大家好,我是抢老婆酸奶的小肥仔。

反射,在咱们日常开发中无时无刻,被大量运用在框架代码和东西代码中,反射能够通俗点讲便是一个类的自我剖析,经过反射能够获取到这个类一切信息,包括:特点,特点值,办法等等。

咱们今天来初探下java中的反射,希望对大家有所帮助,开撸!

1、反射概念

Java反射机制:即在运转状况中,任意一个类,都能够知道这个类一切特点和办法;任意一个目标,都能够调用他的办法和特点。

1.1 反射流程

在java中,咱们编写的类在编译时,会编译成.class文件,然后经过类加载器加载到JVM中,JVM会每个类创立一个Class目标,而Class目标中会保存每个的信息,例如:办法、特点等。JVM底层供给了一些native办法,让Java类获取到Class目标clz,进而获取到类的特点。

【Java】戏说反射-也许对你有帮助

在上图中,能够获取类的目标有三种方式:

  1. 经过Object类中的getClass(),例如:实体先进行结构,然后调用getClass()。
  2. 经过目标的.class特点,即直接运用类名.class既可
  3. 经过Class.forName(),这是经过类名来获取目标,会有反常需要处理。

1.2 优缺陷

长处:能够很方便的获取当时类的信息,能够创立灵活代码,更易于完成面向目标

缺陷:1、反射需耗费一定的系统资源

2、破坏了封装性,可能会导致一些安全问题。

2、反射常用办法

在java.lang.reflect包下面,咱们会看到定义的许多类。在日常开发中,大多数咱们用不到,常用的也便是那么几个,如:Field(类的域/特点)、Method(类的办法)、Constructor(类的结构器)等,下面咱们来看看这些类中定义的哪些办法。

准备工作:

创立两个实体类

/**
 * @author: jiangjs
 * @description:
 * @date: 2023/6/30 9:41
 **/
@Data
public class Animal {
    private String name;
    private Integer age;
    private String color;
    @Data
    public static class OtherAnimal{
        private String other;
    }
}
/**
 * @author: jiangjs
 * @description:
 * @date: 2023/6/30 9:51
 **/
@EqualsAndHashCode(callSuper = true)
@Data
public class Dog extends Animal{
    /**
     * 品种
     */
    private String breed;
    @Data
    public static class OtherDog{
        private String other;
    }
}

其间Dog承继了Animal,是Animal的子类。

2.1 Class类办法

办法名 描绘
forName(String className); 获取当时类的目标
getClassLoader(); 获取当时类的加载器
getClasses(); 获取当时类及其父类中的一切公共类和接口的目标
getDeclaredClasses(); 获取当时类中一切类和接口类的目标
newInstance(); 创立类的实例
getName(); 获取当时类的完好途径称号
getSimpleName(); 获取类的称号
getPackage(); 获取当时类的包
getSuperclass(); 获取当时类的父类的目标
getInterfaces(); 获取当时类的完成类或接口目标

咱们来看下代码完成:

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<Animal> clazz = Animal.class;
        Class<Dog> dogClazz = Dog.class;
        //获取类加载器
        ClassLoader classLoader = clazz.getClassLoader();
        log.info("类加载器:" + classLoader);
        //获取当时类及其父类中的一切公共类和接口的目标
        Class<?>[] classes = dogClazz.getClasses();
        for (Class<?> aClass : classes) {
            log.info("获取的类及父类中的类目标:" + aClass);
        }
        //获取当时类中一切类和接口类的目标
        Class<?>[] declaredClasses = dogClazz.getDeclaredClasses();
        for (Class<?> aClass : declaredClasses) {
            log.info("获取的类目标:" + aClass);
        }
        //创立类的实例
        Dog dog = dogClazz.newInstance();
        dog.setName("橙橙");
        log.info("创立类的实例:" + dog.getName());
        //获取当时类的完好途径称号
        String name = dogClazz.getName();
        log.info("获取当时类的完好途径称号:" + name);
        //获取类的称号
        String simpleName = dogClazz.getSimpleName();
        log.info("获取类的称号:" + simpleName);
        //获取当时类的包
        Package aPackage = dogClazz.getPackage();
        log.info("获取当时类的包:" + aPackage);
        //获取当时类的父类的目标
        Class<? super Dog> superclass = dogClazz.getSuperclass();
        log.info("获取当时类的父类的目标:" + superclass);
        //获取当时类的完成类或接口
        Class<?>[] interfaces = dogClazz.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            log.info("获取当时类的完成类或接口:" + anInterface);
        }
    }

输出:

【Java】戏说反射-也许对你有帮助

2.2 Field特点办法

许多时分咱们都是需要获取特点及特点值来做处理,特别是运用AOP时。

办法名 描绘
getField(String name); 依据称号获取特点目标,特点值有必要是公有的
getFields(); 获取该类一切公有的特点目标
getDeclaredField(String name); 依据称号获取当时类的特点目标
getDeclaredFields(); 取得该类一切特点目标

代码完成:

//特点
        //依据称号获取特点目标
        Field breedField = dogClazz.getField("breed");
        log.info("依据称号获取特点目标:" + breedField);
        //获取该类一切公有的特点目标
        Field[] fields = dogClazz.getFields();
        for (Field field : fields) {
            log.info("获取该类公有的特点目标:" + field);
        }
        //获取当时类及父类指定的特点目标
        Field nameField = clazz.getDeclaredField("name");
        log.info("获取当时类及父类指定的特点目标:" + nameField);
        //取得该类一切特点目标
        Field[] clzFields = clazz.getDeclaredFields();
        for (Field field : clzFields) {
            log.info("取得该类一切特点目标:" + field);
        }

输出:

【Java】戏说反射-也许对你有帮助

在特点目标中,也供给了一些办法:

  1. equals(Object obj) :特点与obj是否持平,持平则回来true,不然回来false。
  2. get(Object obj) :获取obj中对应的特点值。
  3. set(Object obj,Object value) :给obj中的特点赋值。

上述供给的特点办法中只能获取到当时类的特点,许多时分咱们还需要知道当时类的父类有哪些特点。因而咱们能够结合类中的getSuperclass()来获取。代码如下:

private static List<Field> getAllFields(Class<?> clazz) {
    List<Field> allFields = new ArrayList<>();
    while (Objects.nonNull(clazz)){
        Field[] declaredFields = clazz.getDeclaredFields();
        allFields.addAll(Arrays.asList(declaredFields));
        clazz = clazz.getSuperclass();
    }
    return allFields;
}

测试: 获取Dog实体及父类的特点。

List<Field> allFields = getAllFields(dogClazz);
for (Field field : allFields) {
    log.info("获取dog当时类及父类特点:" + field);
}

【Java】戏说反射-也许对你有帮助

2.3 Method办法

办法名 描绘
getMethod(String name, Class…<?> parameterTypes) 依据称号获取当时类及父类中的公有办法。name:办法称号,parameterTypes:参数类型,如String.class等,没有参数能够不填
getMethods(); 获取当时类及其父类的一切公有的办法
getDeclaredMethod(String name, Class…<?> parameterTypes) 依据称号获取当时类的办法
getDeclaredMethods(); 获取当时类中一切的办法
getConstructor(Class…<?> parameterTypes) 获取当时类的公有结构办法
getConstructors() 获取当时类一切公有的结构办法
getDeclaredConstructor(Class…<?> parameterTypes) 获取当时类及父类的一切结构办法
getDeclaredConstructors() 取得当时类一切结构办法

代码完成:

 //method办法
Method setName = dogClazz.getMethod("setName",String.class);
log.info("获取setName办法:" + setName);
Method method = dogClazz.getDeclaredMethod("getBreed");
log.info("获取getBreed办法:" + method);
Method[] declaredMethods = dogClazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
     log.info("获取Dog一切办法:" + declaredMethod);
}
Constructor<Dog> constructor = dogClazz.getConstructor(String.class);
log.info("获取带参数结构办法:" + constructor);
Constructor<?>[] constructors = dogClazz.getConstructors();
for (Constructor<?> con : constructors) {
     log.info("获取Dog下一切结构办法:" + con);
}
Method[] methods = dogClazz.getMethods();
for (Method dogMethod : methods) {
    log.info("获取Dog及父类一切办法:" + dogMethod);
}

输出:

【Java】戏说反射-也许对你有帮助

在Method办法中,能够运用invoke(Object obj, Object... args) 来指定obj目标中参数相同的办法。

2.4 Annotation注解办法

办法名 描绘
getAnnotation(Class annotationClass) 获取当时类及其父类与参数类型匹配的公有注解目标
getAnnotations(); 获取当时类一切公有注解目标
getDeclaredAnnotation(Class annotationClass) 获取当时类及其父类与参数类型匹配的一切注解目标
getDeclaredAnnotations() 获取当时类中一切注解目标
isAnnotation() 验证当时类是否为注解类型,是则回来true,不然回来false
isAnnotationPresent(Class<? extends Annotation> annotationClass) 特点中指定类型是否为注解类型,是则回来true,不然回来false

代码完成:

 //Annotation注解
NotBlank annotation = dogClazz.getDeclaredField("breed").getAnnotation(NotBlank.class);
log.info("获取Dog中Data注解:" + annotation);
String message = annotation.message();
log.info("获取注解信息:" + message);
boolean isAnnotation = dogClazz.isAnnotation();
boolean notBlank = NotBlank.class.isAnnotation();
log.info("验证是否为注解类型的类:dog," + isAnnotation+ ";notBlank:" + notBlank);
boolean present = dogClazz.getDeclaredField("breed").isAnnotationPresent(NotBlank.class);
log.info("指定NotBlank是否为注解类型:" + present);

输出:

【Java】戏说反射-也许对你有帮助

2.5 其他办法

除了上述咱们常用的一些办法外,还有些不常用的,咱们也收拾下,以备不时之需吧。

办法名 描绘
isAnonymousClass() 当时类是否为匿名类。是则回来true,不然回来false
isArray() 是否为数组类型。是则回来true,不然回来false
isEnum() 是否为枚举类型。是则回来true,不然回来false
isInstance(Object obj) obj是否该类的实例。是则回来true,不然回来false
isInterface() 是否为接口。是则回来true,不然回来false
isMemberClass() 是否为内部类
isLocalClass() 是否为局部类

代码完成:

//其他
boolean anonymousClass = dogClazz.isAnonymousClass();
log.info("当时类是否为匿名类:" + anonymousClass);
boolean array = dogClazz.isArray();
log.info("是否为数据类型:" + array);
boolean isEnum = dogClazz.isEnum();
log.info("是否为枚举类型:" + isEnum);
boolean instance = dogClazz.isInstance(dog);
log.info("obj是否该类的实例:" + instance);
boolean anInterface = dogClazz.isInterface();
log.info("是否为接口:" + anInterface);
Class<?>[] clazzs = dogClazz.getDeclaredClasses();
for (Class<?> aClass : clazzs) {
    log.info("类名:"+aClass.getSimpleName() + ";是否为内部类:" + aClass.isMemberClass());
    log.info("类名:"+aClass.getSimpleName() + ";是否为局部类:" + aClass.isLocalClass());
}

输出:

【Java】戏说反射-也许对你有帮助

上述便是我理解的反射及常用的办法。其实只需要多敲敲代码,许多东西都会变得熟练。

一言以蔽之:反射便是经过jdk供给的办法从JVM中获取保存的类的各种特点的操作。

谢谢大家听我啰嗦。