微信搜索:码农StayUp
主页地址:https://gozhuyinglong.github.io
源码分享:https://github.com/gozhuyinglong/blog-demoshtml
在OOP的世界里,万物皆对象。也就是说,咱们能够将任何东西抽象成一个对象。java
好比人,能够抽象成一个Person类,经过new Person()来实例化一个对象;再好比鸭子,能够抽象成一个Duck类,也能够对其进行实例化……那么这一个个类自己是否是也能够抽象成一个类呢?Java提供了一个特殊的类Class
,用来描述类的内部信息,是反射的核心类。git
下图是本篇讲述内容:github
Java反射(Reflection)容许应用程序在运行时借助于反射API,来获取全部类或接口的内部信息,而且能直接操做任意对象的内部属性及方法。反射机制的核心类为java.lang.Class
。编程
Class
类型的对象。Class
类没有公开的构造函数,是由类加载器的defineClass
方法构造而成。因此Class
对象不是“new”出来的,而是经过方法来获取的。Class
对象具备类的完整结构信息,而且一个类只有一个Class
对象。获取Class
对象有如下四种方式:api
下面使用代码展现获取 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()); }
输出结果:安全
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
相同,说明使用这四种方式获取的是同一个对象。微信
在源码注释中提到一些特殊的类和接口:oracle
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'}
引自官方指南:https://docs.oracle.com/javase/tutorial/reflect/index.html
反射虽是强大的,但不可随意使用。若是能够在不使用反射的状况下执行操做,则应避免使用它。由于经过反射访问代码时,会有如下缺点。
反射包括了一些动态类型,因此JVM没法对这些代码进行优化。所以,反射操做的效率要比那些非反射操做低得多。咱们应该避免在常常被执行的代码或对性能要求很高的程序中使用反射。
使用反射技术要求程序必须在一个没有安全限制的环境中运行。若是一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
因为反射容许代码执行一些在正常状况下不被容许的操做,好比访问私有的属性和方法。因此使用反射可能会致使意料以外的反作用:代码有功能上的错误,下降可移植性。反射代码破坏了抽象性,所以当平台发生改变的时候,代码的行为就有可能也随着变化。
完整代码请访问个人Github,若对你有帮助,欢迎给个⭐,感谢~~🌹🌹🌹