微信搜寻:码农StayUp
主页地址:https://gozhuyinglong.github.io
源码分享:https://github.com/gozhuyinglong/blog-demos

1. 前言

在OOP的世界里,万物皆对象。也就是说,咱们能够将任何货色形象成一个对象。

比方人,能够形象成一个Person类,通过new Person()来实例化一个对象;再比方鸭子,能够形象成一个Duck类,也能够对其进行实例化……那么这一个个类自身是不是也能够形象成一个类呢?Java提供了一个非凡的类Class,用来形容类的外部信息,是反射的外围类。

下图是本篇讲述内容:

2. Java反射机制概述

Java反射(Reflection)容许应用程序在运行时借助于反射API,来获取所有类或接口的外部信息,并且能间接操作任意对象的外部属性及办法。反射机制的外围类为java.lang.Class

  • 类加载完后,会在堆内存的办法区中产生一个Class类型的对象。
  • Class类没有公开的构造函数,是由类加载器的defineClass办法结构而成。所以Class对象不是“new”进去的,而是通过办法来获取的。
  • 这个Class对象具备类的残缺构造信息,并且一个类只有一个Class对象。

3. 获取Class对象

获取Class对象有以下四种形式:

  1. 通过类对象获取;
  2. 通过类间接调用class获取;
  3. 通过Class.forName获取;
  4. 通过类加载器获取。

上面应用代码展现获取 Person 类的Class对象的四种形式:

@Testpublic void testClassFor() {    // 1.通过类实例获取    Person person = new Person();    Class<? extends Person> clazz1 = person.getClass();    System.out.println("01 - " + clazz1);    // 2.通过类间接调用class获取    Class<Person> clazz2 = Person.class;    System.out.println("02 - " + clazz2);    // 3.通过Class.forName获取    Class<?> clazz3 = null;    try {        clazz3 = Class.forName("io.github.gozhuyinglong.reflection.Person");    } catch (ClassNotFoundException e) {        // 当找不到指定类时,会抛出此异样        e.printStackTrace();    }    System.out.println("03 - " + clazz3);    // 4.通过类加载器获取    ClassLoader classLoader = this.getClass().getClassLoader();    Class<?> clazz4 = null;    try {        clazz4 = classLoader.loadClass("io.github.gozhuyinglong.reflection.Person");    } catch (ClassNotFoundException e) {        // 当找不到指定类时,会抛出此异样        e.printStackTrace();    }    System.out.println("04 - " + clazz4);    // hashCode相等,阐明这四种形式获取的是同一个实例    System.out.println("05 - " + clazz1.hashCode());    System.out.println("06 - " + clazz2.hashCode());    System.out.println("07 - " + clazz3.hashCode());    System.out.println("08 - " + clazz4.hashCode());}

输入后果:

01 - class io.github.gozhuyinglong.reflection.Person02 - class io.github.gozhuyinglong.reflection.Person03 - class io.github.gozhuyinglong.reflection.Person04 - class io.github.gozhuyinglong.reflection.Person05 - 72174889506 - 72174889507 - 72174889508 - 721748895

通过下面的输入后果能够看出,这四个Class对象的hashCode雷同,阐明应用这四种形式获取的是同一个对象。

4. 一些非凡的类和接口的Class对象

在源码正文中提到一些非凡的类和接口:

  • 枚举是一品种。
  • 注解是一种接口。
  • 数组也属于一个反映为Class对象的类。具备雷同元素类型和维数的数组,也具备雷同的Class对象(也就是说,元素类型不同,或数组维数不同,其Class对象也不同)。
  • 原始Java类型(boolean, byte, char, short, int, long, float,double)和关键字 void 也示意为Class对象。

上面通过代码来验证:

@Testpublic void testClassOther() {    // 枚举是一品种    Class<PersonEnum> clazz1 = PersonEnum.class;    System.out.println("01 - " + clazz1);    // 注解是一种接口    Class<PersonAnnotation> clazz2 = PersonAnnotation.class;    System.out.println("02 - " + clazz2);    // 数组也属于一个反馈 Class 实例的类    Person[] personArray3 = new Person[1];    Class<? extends Person[]> clazz3 = personArray3.getClass();    System.out.println("03 - " + clazz3);    // 具备雷同元素类型和维数的数组,也具备雷同的 Class 实例    Person[] personArray4 = new Person[4];    Class<?> clazz4 = personArray4.getClass();    Person[][] personArray5 = new Person[1][];    Class<?> clazz5 = personArray5.getClass();    // 两个一维数组的 hashCode 相等,阐明是同一实例    System.out.println("04 - " + clazz3.hashCode());    System.out.println("05 - " + clazz4.hashCode());    // 一维数组与二维数组的 hashCode 不相等,阐明不是同一实例    System.out.println("06 - " + clazz5.hashCode());    // 原始 Java 类型和关键字 void 也示意为 Class 实例    Class<Integer> clazz6 = int.class;    System.out.println("07 - " + clazz6);    Class<Double> clazz7 = double.class;    System.out.println("08 - " + clazz7);    Class<Void> clazz8 = void.class;    System.out.println("09 - " + clazz8);}

输入后果:

01 - class io.github.gozhuyinglong.reflection.PersonEnum02 - interface io.github.gozhuyinglong.reflection.PersonAnnotation03 - class [Lio.github.gozhuyinglong.reflection.Person;04 - 72174889505 - 72174889506 - 164253485007 - int08 - double09 - void

通过输入后果能够看出,确如源码中形容那样。

5. Java反射API

Java提供了一套反射API,该API由Class类与java.lang.reflect类库组成。该类库蕴含了FieldMethodConstructor等类。这些类型的对象是由JVM在运行时创立的,用以示意未知类里对应的成员。

反射容许以编程的形式拜访已加载类的字段、办法和构造函数信息,并在平安限度内利用反射对其进行操作。

上面将介绍一些罕用的类:

5.1 Class(类)

java.lang.Class类用来形容类的外部信息,Class的实例能够获取类的包、注解、修饰符、名称、超类、接口等。

@Testpublic void testClass() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取该类所在包门路    Package aPackage = clazz.getPackage();    System.out.println("01 - " + aPackage);    // 获取该类上所有注解    Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();    for (Annotation temp : declaredAnnotations) {        System.out.println("02 - " + temp);    }    // 获取类上的修饰符    int modifiers = clazz.getModifiers();    String modifier = Modifier.toString(modifiers);    System.out.println("03 - " + modifier);    // 获取类名称    String name = clazz.getName();    System.out.println("04 - " + name);    // 获取简略类名    String simpleName = clazz.getSimpleName();    System.out.println("05 - " + simpleName);    // 获取直属超类    Type genericSuperclass = clazz.getGenericSuperclass();    System.out.println("06 - " + genericSuperclass);    // 获取直属实现的接口    Type[] genericInterfaces = clazz.getGenericInterfaces();    for (Type temp : genericInterfaces) {        System.out.println("07 - " + temp);    }}

输入后果:

01 - package io.github.gozhuyinglong.reflection02 - @io.github.gozhuyinglong.reflection.PersonAnnotation()03 - public final04 - io.github.gozhuyinglong.reflection.Person05 - Person06 - class io.github.gozhuyinglong.reflection.PersonParent07 - interface io.github.gozhuyinglong.reflection.PersonInterface

5.2 Constructor(构造函数)

java.lang.reflect.Constructor提供了类的构造函数信息。能够获取构造函数上的注解信息、参数类型等。

@Testpublic void testConstructor() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取一个申明为 public 构造函数实例    Constructor<?> constructor1 = clazz.getConstructor(String.class, int.class, PersonEnum.class);    System.out.println("01 - " + constructor1);    // 获取所有申明为 public 构造函数实例    Constructor<?>[] constructorArray1 = clazz.getConstructors();    for (Constructor<?> constructor : constructorArray1) {        System.out.println("02 - " + constructor);    }    // 获取一个申明的构造函数实例    Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);    System.out.println("03 - " + constructor2);    // 获取所有申明的构造函数实例    Constructor<?>[] constructorArray2 = clazz.getDeclaredConstructors();    for (Constructor<?> constructor : constructorArray2) {        System.out.println("04 - " + constructor);    }    // 依据构造函数创立一个实例    Object o1 = constructor1.newInstance("杨过", 25, PersonEnum.MAN);    System.out.println("05 - " + o1);    // 将构造函数的可拜访标记设为 true 后,能够通过公有构造函数创立实例    constructor2.setAccessible(true);    Object o2 = constructor2.newInstance("小龙女");    System.out.println("06 - " + o2);    // 获取该构造函数上的所有注解    Annotation[] annotations = constructor1.getDeclaredAnnotations();    for (Annotation annotation : annotations) {        System.out.println("07 - " + annotation);    }    // 获取该构造函数上的所有参数类型    Type[] genericParameterTypes = constructor1.getGenericParameterTypes();    for (Type genericParameterType : genericParameterTypes) {        System.out.println("08 - " + genericParameterType);    }}

输入后果:

01 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)02 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)02 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int)02 - public io.github.gozhuyinglong.reflection.Person()03 - private io.github.gozhuyinglong.reflection.Person(java.lang.String)04 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)04 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int)04 - private io.github.gozhuyinglong.reflection.Person(java.lang.String)04 - public io.github.gozhuyinglong.reflection.Person()05 - Person{name='杨过', age=25, sex='MAN'}06 - Person{name='小龙女', age=0, sex='null'}07 - @io.github.gozhuyinglong.reflection.PersonAnnotation()08 - class java.lang.String08 - int08 - class io.github.gozhuyinglong.reflection.PersonEnum

5.3 Field(属性)

java.lang.reflect.Field提供了类的属性信息。能够获取属性上的注解、修饰符、属性类型、属性名等。

@Testpublic void testField() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取一个该类或父类中申明为 public 的属性    Field field1 = clazz.getField("hobby");    System.out.println("01 - " + field1);    // 获取该类及父类中所有申明为 public 的属性    Field[] fieldArray1 = clazz.getFields();    for (Field field : fieldArray1) {        System.out.println("02 - " + field);    }    // 获取一个该类中申明的属性    Field field2 = clazz.getDeclaredField("name");    System.out.println("03 - " + field2);    // 获取该类中所有申明的属性    Field[] fieldArray2 = clazz.getDeclaredFields();    for (Field field : fieldArray2) {        System.out.println("04 - " + field);    }    // 获取该属性上的所有注解    Annotation[] declaredAnnotations = field2.getDeclaredAnnotations();    for (Annotation declaredAnnotation : declaredAnnotations) {        System.out.println("05 - " + declaredAnnotation);    }    // 获取修饰符    String modifier = Modifier.toString(field2.getModifiers());    System.out.println("06 - " + modifier);    // 获取属性类型,返回类对象    Class<?> type = field2.getType();    System.out.println("07 - " + type);    // 获取属性类型,返回Type对象    Type genericType = field2.getGenericType();    System.out.println("08 - " + genericType);    // 获取属性名称    String name = field2.getName();    System.out.println("09 - " + name);}

输入后果:

01 - public java.lang.String io.github.gozhuyinglong.reflection.PersonParent.hobby02 - public int io.github.gozhuyinglong.reflection.Person.height02 - public java.lang.String io.github.gozhuyinglong.reflection.PersonParent.hobby03 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name04 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name04 - private int io.github.gozhuyinglong.reflection.Person.age04 - public int io.github.gozhuyinglong.reflection.Person.height05 - @io.github.gozhuyinglong.reflection.PersonAnnotation()06 - private07 - class java.lang.String08 - class java.lang.String09 - name

5.4 Method(办法)

java.lang.reflect.Method提供了类的办法信息。能够获取办法上的注解、修饰符、返回值类型、办法名称、所有参数。

@Testpublic void testMethod() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取一个该类及父类中申明为 public 的办法,须要指定办法的入参类型    Method method = clazz.getMethod("setName", String.class);    System.out.println("01 - " + method);    // 获取该类及父类中所有申明为 public 的办法    Method[] methods = clazz.getMethods();    for (Method temp : methods) {        System.out.println("02 - " + temp);    }    // 获取一个在该类中申明的办法    Method declaredMethod = clazz.getDeclaredMethod("display");    System.out.println("03 - " + declaredMethod);    // 获取所有在该类中申明的办法    Method[] declaredMethods = clazz.getDeclaredMethods();    for (Method temp : declaredMethods) {        System.out.println("04 - " + temp);    }    // 获取该办法上的所有注解    Annotation[] declaredAnnotations = method.getDeclaredAnnotations();    for (Annotation temp : declaredAnnotations) {        System.out.println("05 - " + temp);    }    // 获取修饰符    String modifier = Modifier.toString(method.getModifiers());    System.out.println("06 - " + modifier);    // 获取返回值类型,返回类对象    Class<?> returnType = method.getReturnType();    System.out.println("07 - " + returnType);    // 获取返回值类型,返回Type对象    Type genericReturnType = method.getGenericReturnType();    System.out.println("08 - " + genericReturnType);    // 获取办法名称    String name = method.getName();    System.out.println("09 - " + name);    // 获取所有入参    Parameter[] parameters = method.getParameters();    for (Parameter temp : parameters) {        System.out.println("10 - " + temp);    }}

输入后果:

01 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.toString()02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.getName()02 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)02 - public int io.github.gozhuyinglong.reflection.Person.getAge()02 - public void io.github.gozhuyinglong.reflection.Person.setAge(int)02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.sayHello()02 - public io.github.gozhuyinglong.reflection.PersonEnum io.github.gozhuyinglong.reflection.PersonParent.getSex()02 - public void io.github.gozhuyinglong.reflection.PersonParent.setSex(io.github.gozhuyinglong.reflection.PersonEnum)02 - public final void java.lang.Object.wait() throws java.lang.InterruptedException02 - public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException02 - public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException02 - public boolean java.lang.Object.equals(java.lang.Object)02 - public native int java.lang.Object.hashCode()02 - public final native java.lang.Class java.lang.Object.getClass()02 - public final native void java.lang.Object.notify()02 - public final native void java.lang.Object.notifyAll()03 - private java.lang.String io.github.gozhuyinglong.reflection.Person.display()04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.toString()04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.getName()04 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)04 - private java.lang.String io.github.gozhuyinglong.reflection.Person.display()04 - public int io.github.gozhuyinglong.reflection.Person.getAge()04 - public void io.github.gozhuyinglong.reflection.Person.setAge(int)04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.sayHello()05 - @io.github.gozhuyinglong.reflection.PersonAnnotation()06 - public07 - void08 - void09 - setName10 - java.lang.String arg0

5.5 Modifier(修饰符)

java.lang.reflect.Modifier提供了拜访修饰符信息。通过ClassFieldMethodConstructor等对象都能够获取修饰符,这个拜访修饰符是一个整数,能够通过Modifier.toString办法来查看修饰符形容。并且该类提供了一些静态方法和常量来解码拜访修饰符。

@Testpublic void testModifier() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取类的修饰符值    int modifiers1 = clazz.getModifiers();    System.out.println("01 - " + modifiers1);    // 获取属性的修饰符值    int modifiers2 = clazz.getDeclaredField("name").getModifiers();    System.out.println("02 - " + modifiers2);    // 获取构造函数的修饰符值    int modifiers3 = clazz.getDeclaredConstructor(String.class).getModifiers();    System.out.println("03 - " + modifiers3);    // 获取办法的修饰符值    int modifiers4 = clazz.getDeclaredMethod("display").getModifiers();    System.out.println("04 - " + modifiers4);    // 判断修饰符值是否 final 类型    boolean isFinal = Modifier.isFinal(modifiers1);    System.out.println("05 - " + isFinal);    // 判断修饰符值是否 public 类型    boolean isPublic = Modifier.isPublic(modifiers2);    System.out.println("06 - " + isPublic);    // 依据修饰符值,获取修饰符标记的字符串    String modifier = Modifier.toString(modifiers1);    System.out.println("07 - " + modifier);    System.out.println("08 - " + Modifier.toString(modifiers2));}

输入后果:

01 - 1702 - 203 - 204 - 205 - true06 - false07 - public final08 - private

5.6 Parameter(参数)

java.lang.reflect.Parameter提供了办法的参数信息。能够获取办法上的注解、参数名称、参数类型等。

@Testpublic void testParameter() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 获取构造函数的参数    Constructor<?> constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class);    Parameter[] parameterArray1 = constructor.getParameters();    for (Parameter temp : parameterArray1) {        System.out.println("01 - " + temp);    }    // 获取办法的参数    Method method = clazz.getMethod("setName", String.class);    Parameter[] parameterArray2 = method.getParameters();    for (Parameter temp : parameterArray2) {        System.out.println("02 - " + temp);    }    Parameter parameter = parameterArray1[0];    // 获取参数上的注解    Annotation[] annotationArray = parameter.getAnnotations();    for (Annotation temp : annotationArray) {        System.out.println("02 - " + temp);    }    // 获取参数名称    String name = parameter.getName();    System.out.println("03 - " + name);    // 获取参数类型    Type parameterizedType = parameter.getParameterizedType();    System.out.println("04 - " + parameterizedType);    Class<?> type = parameter.getType();    System.out.println("05 - " + type);}

输入后果:

01 - java.lang.String arg001 - int arg101 - io.github.gozhuyinglong.reflection.PersonEnum arg202 - java.lang.String arg002 - @io.github.gozhuyinglong.reflection.PersonAnnotation()03 - arg004 - class java.lang.String05 - class java.lang.String

5.7 AccessibleObject(可拜访标记)

java.lang.reflect.AccessibleObject类是FieldMethodConstructor类的超类。

该类提供了对类、办法、构造函数的访问控制查看的能力(如:公有办法只容许以后类拜访)。

拜访查看在设置/获取属性、调用办法、创立/初始化类的实例时执行。

能够通过setAccessible办法将可拜访标记设为true(默认为false),会敞开拜访查看。这样即便是公有的属性、办法或构造函数,也能够拜访。

6. 通过反射动静创建对象并执行办法

能够利用反射来创建对象,并可执行办法,上面看代码示例:

  • 通过Class类的newInstance创立一个实例。(该办法调用无参结构器)。
  • 通过构造函数Constructor类创立一个实例。
  • 获取办法,再通过 invoke 办法来调用,第一个参数为实例,前面参数为办法的Parameter
  • 获取字段,因为 age 字段是公有的,所以将其设置为可拜访(不设置会报异样)。并通过 set 办法来赋值。
@Testpublic void testInvoke() throws Exception {    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");    // 通过Class类的newInstance创立一个实例。(该办法调用无参结构器)    Object o1 = clazz.newInstance();    System.out.println("01 - " + o1.toString());    // 通过构造函数Constructor类创立一个实例    Constructor<?> constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class);    Object o2 = constructor.newInstance("杨过", 25, PersonEnum.MAN);    System.out.println("02 - " + o2.toString());    // 先获取办法,再通过 invoke 办法来调用,第一个参数为实例,前面参数为办法的Parameter    Method method = clazz.getMethod("setName", String.class);    method.invoke(o1, "小龙女");    System.out.println("03 - " + o1.toString());    // 获取字段,因为 age 字段是公有的,所以将其设置为可拜访(不设置会报异样)。并通过 set 办法来赋值    Field field = clazz.getDeclaredField("age");    field.setAccessible(true);    field.set(o1, 28);    System.out.println("04 - " + o1.toString());}

执行后果:

01 - Person{name='null', age=0, sex='null'}02 - Person{name='杨过', age=25, sex='MAN'}03 - Person{name='小龙女', age=0, sex='null'}04 - Person{name='小龙女', age=28, sex='null'}

7. 反射的毛病

引自官网指南:https://docs.oracle.com/javas...

反射虽是弱小的,但不可随便应用。如果能够在不应用反射的状况下执行操作,则应防止应用它。因为通过反射拜访代码时,会有以下毛病。

7.1 性能开销

反射包含了一些动静类型,所以JVM无奈对这些代码进行优化。因而,反射操作的效率要比那些非反射操作低得多。咱们应该防止在常常被执行的代码或对性能要求很高的程序中应用反射。

7.2 平安限度

应用反射技术要求程序必须在一个没有平安限度的环境中运行。如果一个程序必须在有平安限度的环境中运行,如Applet,那么这就是个问题了。

7.3 外部裸露

因为反射容许代码执行一些在失常状况下不被容许的操作,比方拜访公有的属性和办法。所以应用反射可能会导致意料之外的副作用:代码有性能上的谬误,升高可移植性。反射代码毁坏了抽象性,因而当平台产生扭转的时候,代码的行为就有可能也随着变动。

8. 残缺代码

残缺代码请拜访我的Github,若对你有帮忙,欢送给个⭐,感激~~????????????

https://github.com/gozhuyingl...

9. 参考资料

  • Java™ Platform, Standard Edition 8 API Specification
  • The Java™ Tutorials