微信搜索:码农StayUphtml
主页地址:gozhuyinglong.github.iojava
源码分享:github.com/gozhuyinglo…git
在OOP的世界里,万物皆对象。也就是说,咱们能够将任何东西抽象成一个对象。github
好比人,能够抽象成一个Person类,经过new Person()来实例化一个对象;再好比鸭子,能够抽象成一个Duck类,也能够对其进行实例化……那么这一个个类自己是否是也能够抽象成一个类呢?Java提供了一个特殊的类Class
,用来描述类的内部信息,是反射的核心类。编程
下图是本篇讲述内容:api
Java反射(Reflection)容许应用程序在运行时借助于反射API,来获取全部类或接口的内部信息,而且能直接操做任意对象的内部属性及方法。反射机制的核心类为java.lang.Class
。数组
Class
类型的对象。Class
类没有公开的构造函数,是由类加载器的defineClass
方法构造而成。因此Class
对象不是“new”出来的,而是经过方法来获取的。Class
对象具备类的完整结构信息,而且一个类只有一个Class
对象。获取Class
对象有如下四种方式:安全
下面使用代码展现获取 Person 类的Class
对象的四种方式:微信
@Test
public 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());
}
复制代码
输出结果:markdown
01 - class io.github.gozhuyinglong.reflection.Person
02 - class io.github.gozhuyinglong.reflection.Person
03 - class io.github.gozhuyinglong.reflection.Person
04 - class io.github.gozhuyinglong.reflection.Person
05 - 721748895
06 - 721748895
07 - 721748895
08 - 721748895
复制代码
经过上面的输出结果能够看出,这四个Class
对象的hashCode
相同,说明使用这四种方式获取的是同一个对象。
在源码注释中提到一些特殊的类和接口:
Class
对象的类。具备相同元素类型和维数的数组,也具备相同的Class
对象(也就是说,元素类型不一样,或数组维数不一样,其Class
对象也不一样)。boolean
, byte
, char
, short
, int
, long
, float
,double
)和关键字 void
也表示为Class
对象。下面经过代码来验证:
@Test
public 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.PersonEnum
02 - interface io.github.gozhuyinglong.reflection.PersonAnnotation
03 - class [Lio.github.gozhuyinglong.reflection.Person;
04 - 721748895
05 - 721748895
06 - 1642534850
07 - int
08 - double
09 - void
复制代码
经过输出结果能够看出,确如源码中描述那样。
Java提供了一套反射API,该API由Class
类与java.lang.reflect
类库组成。该类库包含了Field
、Method
、Constructor
等类。这些类型的对象是由JVM在运行时建立的,用以表示未知类里对应的成员。
反射容许以编程的方式访问已加载类的字段、方法和构造函数信息,并在安全限制内利用反射对其进行操做。
下面将介绍一些经常使用的类:
java.lang.Class
类用来描述类的内部信息,Class
的实例能够获取类的包、注解、修饰符、名称、超类、接口等。
@Test
public 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.reflection
02 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
03 - public final
04 - io.github.gozhuyinglong.reflection.Person
05 - Person
06 - class io.github.gozhuyinglong.reflection.PersonParent
07 - interface io.github.gozhuyinglong.reflection.PersonInterface
复制代码
java.lang.reflect.Constructor
提供了类的构造函数信息。能够获取构造函数上的注解信息、参数类型等。
@Test
public 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.String
08 - int
08 - class io.github.gozhuyinglong.reflection.PersonEnum
复制代码
java.lang.reflect.Field
提供了类的属性信息。能够获取属性上的注解、修饰符、属性类型、属性名等。
@Test
public 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.hobby
02 - public int io.github.gozhuyinglong.reflection.Person.height
02 - public java.lang.String io.github.gozhuyinglong.reflection.PersonParent.hobby
03 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name
04 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name
04 - private int io.github.gozhuyinglong.reflection.Person.age
04 - public int io.github.gozhuyinglong.reflection.Person.height
05 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
06 - private
07 - class java.lang.String
08 - class java.lang.String
09 - name
复制代码
java.lang.reflect.Method
提供了类的方法信息。能够获取方法上的注解、修饰符、返回值类型、方法名称、全部参数。
@Test
public 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.InterruptedException
02 - public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
02 - public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
02 - 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 - public
07 - void
08 - void
09 - setName
10 - java.lang.String arg0
复制代码
java.lang.reflect.Modifier
提供了访问修饰符信息。经过Class
、Field
、Method
、Constructor
等对象均可以获取修饰符,这个访问修饰符是一个整数,能够经过Modifier.toString
方法来查看修饰符描述。而且该类提供了一些静态方法和常量来解码访问修饰符。
@Test
public 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 - 17
02 - 2
03 - 2
04 - 2
05 - true
06 - false
07 - public final
08 - private
复制代码
java.lang.reflect.Parameter
提供了方法的参数信息。能够获取方法上的注解、参数名称、参数类型等。
@Test
public 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 arg0
01 - int arg1
01 - io.github.gozhuyinglong.reflection.PersonEnum arg2
02 - java.lang.String arg0
02 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
03 - arg0
04 - class java.lang.String
05 - class java.lang.String
复制代码
java.lang.reflect.AccessibleObject
类是Field
、Method
和Constructor
类的超类。
该类提供了对类、方法、构造函数的访问控制检查的能力(如:私有方法只容许当前类访问)。
该访问检查在设置/获取属性、调用方法、建立/初始化类的实例时执行。
能够经过setAccessible
方法将可访问标志设为true
(默认为false
),会关闭访问检查。这样即便是私有的属性、方法或构造函数,也能够访问。
能够利用反射来建立对象,并可执行方法,下面看代码示例:
Class
类的newInstance
建立一个实例。(该方法调用无参构造器)。Constructor
类建立一个实例。invoke
方法来调用,第一个参数为实例,后面参数为方法的Parameter
。set
方法来赋值。@Test
public 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'}
复制代码
引自官方指南:docs.oracle.com/javase/tuto…
反射虽是强大的,但不可随意使用。若是能够在不使用反射的状况下执行操做,则应避免使用它。由于经过反射访问代码时,会有如下缺点。
反射包括了一些动态类型,因此JVM没法对这些代码进行优化。所以,反射操做的效率要比那些非反射操做低得多。咱们应该避免在常常被执行的代码或对性能要求很高的程序中使用反射。
使用反射技术要求程序必须在一个没有安全限制的环境中运行。若是一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
因为反射容许代码执行一些在正常状况下不被容许的操做,好比访问私有的属性和方法。因此使用反射可能会致使意料以外的反作用:代码有功能上的错误,下降可移植性。反射代码破坏了抽象性,所以当平台发生改变的时候,代码的行为就有可能也随着变化。
完整代码请访问个人Github,若对你有帮助,欢迎给个⭐,感谢~~🌹🌹🌹