Java学习——反射
摘要:本文主要讲述了什么是反射,使用反射有什么好处,以及如何使用反射。
部分内容来自以下博客:
https://www.cnblogs.com/tech-bird/p/3525336.html
https://www.cnblogs.com/jiaoyiping/p/6130355.html
什么是反射
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
Class类
Class类是什么
Class是用来描述类的类,封装了当前对象所对应的类的信息。
一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等。
对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。
Class对象只能由系统建立对象,一个类(而不是一个对象)在JVM中只会有一个Class实例。
通过Class可以完整地得到一个类中的所有被加载的结构。
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
Java中的哪些类型可以有Class类的实例
类,接口,枚举,注解,数组,基本数据类型,void。
常用方法
static Class<?> forName(String className):返回指定类名的Class对象。
T newInstance():返回该Class对象对应的类的一个实例。
Field[] getFields():获取所有的公共属性。
Field[] getDeclaredFields():获取所有的属性。
Field getField(String name):获取指定名称的公共属性。
Field getDeclaredField(String name):获取指定名称的属性。
Method[] getMethods():获取所有的公共方法。
Method[] getDeclaredMethods():获取所有的方法。
Method getMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的公共方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的方法。
Constructor<?>[] getConstructors():获取所有的公共构造方法。
Constructor<?>[] getDeclaredConstructors():获取所有的构造方法。
Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定参数列表的公共构造方法。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数列表的构造方法。
<A extends Annotation> A getAnnotation(Class<A> annotationClass):获取指定类型的公共注解。
<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):获取指定类型的注解。
Annotation[] getAnnotations():获取所有的公共注解。
Annotation[] getDeclaredAnnotations():获取所有的注解。
Class<?>[] getInterfaces():获取实现的所有接口。
Type[] getGenericInterfaces():获取带泛型的接口。
Class<? super T> getSuperclass():获取继承的父类。
Type getGenericSuperclass():获取带泛型的父类。
String getName():返回全类名。
Package getPackage():返回包名。
int getModifiers():返回修饰符。
ClassLoader getClassLoader():返回类加载器。
InputStream getResourceAsStream(String name):返回读取的文件流。
如何获取Class类的实例
Class没有公共构造方法。
Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。
Java程序经过编译以后,生成关于类的字节码文件。在解释运行时,需要将类的字节码文件加载到内存中(通过JVM的类的加载器实现的加载)。此时加载到内存中的类,我们就称作运行时类,此类本身就充当了java.lang.Class的一个实例。
反过来讲:一个Class的实例就对应着一个加载到内存中的运行时类。
获取Class类的实例有四种方式:
1)直接调用运行时类的.class属性
1 Class clazz = String.class;
2)通过调用运行时类的对象的getClass()方法
1 Class clazz = "www.atguigu.com".getClass();
3)调用Class的静态方法forName()获取
1 Class clazz = Class.forName("refl.Person");
4)使用类的加载器
1 ClassLoader cl = this.getClass().getClassLoader(); 2 Class clazz = cl.loadClass("refl.Person");
使用forName()和loadClass()的区别
相同:
使用Class的静态方法forName()和使用ClassLoader的成员方法loadClass()都能获取指定类的Class实例。
也都会进行类的加载,即将.class文件读取到JVM中。
不同:
在调用Class的静态方法forName()时,除了会进行类的加载(即将.class文件加载到JVM中)以外,还会进行类的初始化(即初始化静态成员变量和执行静态代码块)。但调用forName()方法并不会执行构造方法创建对象,只要在调用了成员方法newInstance()以后,才会调用空参的构造方法创建对象。
当调用ClassLoader的成员方法loadClass()时,只进行类的加载(即将.class文件加载到JVM中),不会进行类的初始化(即初始化静态成员变量和执行静态代码块),也不会执行构造方法。
ClassLoader类
类的加载过程
当程序主动使用了某个类时,该类还未被加载到内存中,那么系统会通过下面三个步骤对该类进行初始化。:
1)类的加载:将类的class文件读入内存,并为之创建一个Class对象。由类加载器完成。
2)类的连接:将类的二进制数据合并到JRE中。
3)类的初始化:JVM负责对类进行初始化。
何时会进行类的初始化
1)类的主动引用一定会发生类的初始化:
当虚拟机启动,先初始化main方法所在的类。
使用new关键字创建了一个类的对象。
调用类的静态成员(除了final常量)和静态方法。
使用java.lang.reflect包的方法对类进行反射调用。
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
2)类的被动引用不会发生类的初始化
当访问一个静态域时,只有真正声明这个域的类才会被初始化。
当通过子类引用父类的静态变量,不会导致子类初始化。
通过数组定义类引用,不会触发此类的初始化。
引用常量不会触发此类的初始化,常量在链接阶段就存入调用类的常量池中了。
类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类加载器的分类
引导类加载器:用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。无法直接获取。
扩展类加载器:负责jre/lib/ext目录下的JAR包或–D java.ext.dirs指定目录下的JAR包装入工作库。
系统类加载器:负责java –classpath或–D java.class.path所指的目录下的类与JAR包装入工作,最常用的加载器。
常用方法
Class<?> loadClass(String name):将Class文件加载到内存时,并不会执行类的初始化。
static ClassLoader getSystemClassLoader():获取系统类加载器。
ClassLoader getParent():获取当前类加载器的上级类加载器。
static InputStream getSystemResourceAsStream(String name):和getResourceAsStream用法类似。
InputStream getResourceAsStream(String name):默认则是从ClassPath根下获取,path不能以'/'开头,最终是由ClassLoader获取资源。
如何获取类加载器
获取系统类加载器(可以获取): ClassLoader.getSystemClassLoader();
获取扩展类加载器(可以获取): ClassLoader.getSystemClassLoader().getParent();
获取引导类加载器(不可以获取): ClassLoader.getSystemClassLoader().getParent().getParent();
测试类加载器
测试代码如下:
1 public static void main(String[] args) { 2 // 获取系统类加载器 3 ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); 4 System.out.println(appClassLoader); 5 // 获取扩展类加载器 6 ClassLoader extClassLoader = appClassLoader.getParent(); 7 System.out.println(extClassLoader); 8 // 获取引导类加载器 9 ClassLoader bootClassLoader = extClassLoader.getParent(); 10 System.out.println(bootClassLoader); 11 // 自定义类使用的是系统类加载器 12 System.out.println(ClassLoaderTest.class.getClassLoader()); 13 // JDK提供的类使用的是引导类加载器 14 System.out.println(String.class.getClassLoader()); 15 }
运行结果如下:
1 sun.misc.Launcher$AppClassLoader@18b4aac2 2 sun.misc.Launcher$ExtClassLoader@6d9c638 3 null 4 sun.misc.Launcher$AppClassLoader@18b4aac2 5 null
测试加载资源
测试代码如下:
1 // InputStream is1 = ClassLoader.getSystemClassLoader().getResourceAsStream("test.txt");// null 2 // InputStream is2 = ClassLoader.getSystemClassLoader().getResourceAsStream("/com/test/test.txt");// null 3 InputStream is3 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/test/test.txt"); 4 InputStream is4 = ClassLoader.getSystemResourceAsStream("com/test/test.txt");
使用反射
获取类的信息
1 // 根据类名获取类的Class实例 2 Class<?> cla = Class.forName("test.Student"); 3 // 获取全类名 4 System.out.println("cla.getName() >>> " + cla.getName());// cla.getName() >>> test.Student 5 // 获取包名 6 System.out.println("cla.getPackage() >>> " + cla.getPackage());// cla.getPackage() >>> package test 7 // 获取访问修饰符 8 System.out.println("cla.getModifiers() >>> " + cla.getModifiers());// cla.getModifiers() >>> 0 9 // 获取翻译后的访问修饰符 10 System.out.println("cla.getModifiers() >>> " + Modifier.toString(cla.getModifiers()));// cla.getModifiers() >>> 11 // 获取加载器 12 System.out.println("cla.getClassLoader() >>> " + cla.getClassLoader());// cla.getClassLoader() >>> sun.misc.Launcher$AppClassLoader@18b4aac2 13 // 获取父类 14 Class<?> superclass = cla.getSuperclass(); 15 System.out.println("cla.getSuperclass() >>> " + superclass);// cla.getSuperclass() >>> class test.People 16 // 获取带泛型的父类 17 Type genericSuperclass = cla.getGenericSuperclass(); 18 System.out.println("cla.getGenericSuperclass() >>> " + genericSuperclass); 19 // 获取带泛型的父类的泛型类型 20 ParameterizedType paramType = (ParameterizedType) genericSuperclass; 21 Type[] actualTypeArguments = paramType.getActualTypeArguments(); 22 for (Type type : actualTypeArguments) { 23 System.out.println("genericSuperclass.getActualTypeArguments() >>> " + type.getTypeName()); 24 } 25 // 获取接口 26 Class<?>[] interfaces = cla.getInterfaces(); 27 for (Class<?> interfaceClass : interfaces) { 28 System.out.println("cla.getInterfaces() >>> " + interfaceClass); 29 // cla.getInterfaces() >>> interface test.School 30 // cla.getInterfaces() >>> interface test.Home 31 } 32 // 获取带泛型的接口 33 Type[] genericInterfaces = cla.getGenericInterfaces(); 34 for (Type genericInterface : genericInterfaces) { 35 System.out.println("cla.getGenericInterfaces() >>> " + genericInterface); 36 // cla.getGenericInterfaces() >>> test.School<J> 37 // cla.getGenericInterfaces() >>> test.Home<K> 38 } 39 // 使用反射创建类的实例 40 Student student = (Student) cla.newInstance();// >>> Student() 41 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:0][grade:0.0][sex:null]
获取类的属性
1 // 获取所有的公共属性,包括父类的公共属性 2 Field[] fields = cla.getFields(); 3 for (Field field : fields) { 4 System.out.println("cla.getFields() >>> " + field); 5 // cla.getFields() >>> public int test.Student.age 6 // cla.getFields() >>> public java.lang.String test.People.name 7 } 8 // 获取已声明的所有属性,包括非公共属性,但不包括父类的属性 9 Field[] declaredFields = cla.getDeclaredFields(); 10 for (Field field : declaredFields) { 11 System.out.println("cla.getDeclaredFields() >>> " + field); 12 // cla.getDeclaredFields() >>> public int test.Student.age 13 // cla.getDeclaredFields() >>> double test.Student.grade 14 // cla.getDeclaredFields() >>> private java.lang.String test.Student.sex 15 } 16 // 获取指定名称的属性 17 // Field sex = cla.getField("sex"); 18 Field sex = cla.getDeclaredField("sex"); 19 System.out.println("cla.getDeclaredField() >>> " + sex);// cla.getDeclaredField() >>> private java.lang.String test.Student.sex 20 // 获取访问修饰符 21 System.out.println("sex.getModifiers() >>> " + Modifier.toString(sex.getModifiers()));// sex.getModifiers() >>> private 22 // 获取类型 23 System.out.println("sex.getType() >>> " + sex.getType().getName());// sex.getType() >>> java.lang.String 24 // 获取名称 25 System.out.println("sex.getName() >>> " + sex.getName());// sex.getName() >>> sex 26 // 获取和修改属性值,非公共属性需要执行setAccessible()方法 27 sex.setAccessible(true); 28 System.out.println("sex.get() >>> " + sex.get(student));// sex.get() >>> null 29 sex.set(student, "男"); 30 System.out.println("student.getSex() >>> " + student.getSex());// student.getSex() >>> 男
获取类的方法
1 // 获取所有的公共方法,包括父类的公共方法和接口的公共方法 2 Method[] methods = cla.getMethods(); 3 for (Method method : methods) { 4 System.out.println("cla.getMethods() >>> " + method); 5 // cla.getMethods() >>> public int test.Student.getAge() 6 // cla.getMethods() >>> public void test.Student.setAge(int) 7 // cla.getMethods() >>> public double test.Student.getGrade() 8 // cla.getMethods() >>> public void test.Student.setGrade(double) 9 // cla.getMethods() >>> public java.lang.String test.Student.getSex() 10 // cla.getMethods() >>> public void test.Student.setSex(java.lang.String) 11 // cla.getMethods() >>> public java.lang.String test.Student.toString() 12 // cla.getMethods() >>> public void test.Student.showSchool(java.lang.Object) 13 // cla.getMethods() >>> public void test.Student.showHome(java.lang.Object) 14 // cla.getMethods() >>> public java.lang.String test.People.getName() 15 // cla.getMethods() >>> public void test.People.setName(java.lang.String) 16 // cla.getMethods() >>> public boolean java.lang.Object.equals(java.lang.Object) 17 // cla.getMethods() >>> public native int java.lang.Object.hashCode() 18 // cla.getMethods() >>> public final native java.lang.Class java.lang.Object.getClass() 19 // cla.getMethods() >>> public final native void java.lang.Object.notify() 20 // cla.getMethods() >>> public final native void java.lang.Object.notifyAll() 21 // cla.getMethods() >>> public final void java.lang.Object.wait() throws java.lang.InterruptedException 22 // cla.getMethods() >>> public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 23 // cla.getMethods() >>> public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 24 // cla.getMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException 25 } 26 // 获取已声明的所有方法,包括非公共方法和重写的方法,但不包括父类的方法 27 Method[] declaredMethods = cla.getDeclaredMethods(); 28 for (Method method : declaredMethods) { 29 System.out.println("cla.getDeclaredMethods() >>> " + method); 30 // cla.getDeclaredMethods() >>> public int test.Student.getAge() 31 // cla.getDeclaredMethods() >>> public void test.Student.setAge(int) 32 // cla.getDeclaredMethods() >>> public double test.Student.getGrade() 33 // cla.getDeclaredMethods() >>> public void test.Student.setGrade(double) 34 // cla.getDeclaredMethods() >>> public java.lang.String test.Student.getSex() 35 // cla.getDeclaredMethods() >>> public void test.Student.setSex(java.lang.String) 36 // cla.getDeclaredMethods() >>> public java.lang.String test.Student.toString() 37 // cla.getDeclaredMethods() >>> public void test.Student.showSchool(java.lang.Object) 38 // cla.getDeclaredMethods() >>> public void test.Student.showHome(java.lang.Object) 39 // cla.getDeclaredMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException 40 // cla.getDeclaredMethods() >>> private void test.Student.doTestStudent() 41 } 42 // 获取指定名称和参数类型的方法 43 // Method testStudent = cla.getMethod("testStudent", String.class); 44 Method testStudent = cla.getDeclaredMethod("testStudent", String.class); 45 System.out.println("cla.getDeclaredMethod() >>> " + testStudent);// cla.getDeclaredMethod() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException 46 // 获取注解 47 Annotation[] annotations = testStudent.getAnnotations(); 48 for (Annotation annotation : annotations) { 49 System.out.println("testStudent.getAnnotations() >>> " + annotation); 50 // testStudent.getAnnotations() >>> @java.lang.Deprecated() 51 } 52 // 获取访问修饰符 53 System.out.println("testStudent.getModifiers() >>> " + Modifier.toString(testStudent.getModifiers()));// testStudent.getModifiers() >>> public 54 // 获取返回值类型 55 System.out.println("testStudent.getReturnType() >>> " + testStudent.getReturnType().getName());// testStudent.getReturnType() >>> java.lang.String 56 // 获取名称 57 System.out.println("testStudent.getName() >>> " + testStudent.getName());// testStudent.getName() >>> testStudent 58 // 获取参数 59 Parameter[] parameters = testStudent.getParameters(); 60 for (Parameter parameter : parameters) { 61 System.out.println("testStudent.getParameters() >>> " + parameter.getName()); 62 // testStudent.getParameters() >>> name 63 } 64 // 获取参数列表 65 Class[] parameterTypes = testStudent.getParameterTypes(); 66 for (Class parameterType : parameterTypes) { 67 System.out.println("testStudent.getParameterTypes() >>> " + parameterType.getName()); 68 // testStudent.getParameterTypes() >>> java.lang.String 69 } 70 // 获取异常 71 Class[] exceptionTypes = testStudent.getExceptionTypes(); 72 for (Class exceptionType : exceptionTypes) { 73 System.out.println("testStudent.getExceptionTypes() >>> " + exceptionType.getName()); 74 // testStudent.getExceptionTypes() >>> java.lang.NullPointerException 75 // testStudent.getExceptionTypes() >>> java.lang.ArithmeticException 76 } 77 // 调用方法,非公共属性需要执行setAccessible()方法 78 Object testStudentResult = testStudent.invoke(student, "abc");// >>> testStudent() 79 System.out.println("testStudent.invoke() >>> " + testStudentResult);// testStudent.invoke() >>> abc 80 Method doTestStudent = cla.getDeclaredMethod("doTestStudent"); 81 // Object doTestStudentResult = doTestStudent.invoke(student);// java.lang.IllegalAccessException 82 doTestStudent.setAccessible(true); 83 Object doTestStudentResult = doTestStudent.invoke(student);// >>> doTestStudent() 84 System.out.println("doTestStudent.invoke() >>> " + doTestStudentResult);// doTestStudent.invoke() >>> null
获取类的构造方法
1 // 获取所有的公共构造方法 2 Constructor<?>[] constructors = cla.getConstructors(); 3 for (Constructor<?> constructor : constructors) { 4 System.out.println("cla.getConstructors() >>> " + constructor); 5 // cla.getConstructors() >>> public test.Student() 6 // cla.getConstructors() >>> public test.Student(java.lang.String) 7 } 8 // 获取所有的构造方法,包括非公共构造方法 9 Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors(); 10 for (Constructor<?> constructor : declaredConstructors) { 11 System.out.println("cla.getDeclaredConstructors() >>> " + constructor); 12 // cla.getDeclaredConstructors() >>> public test.Student() 13 // cla.getDeclaredConstructors() >>> public test.Student(java.lang.String) 14 // cla.getDeclaredConstructors() >>> private test.Student(int,double,java.lang.String) 15 } 16 // 获取参数类型的构造方法 17 // Constructor<?> peopleConstructor = cla.getConstructor(String.class); 18 Constructor<?> studentConstructor = cla.getDeclaredConstructor(int.class, double.class, String.class); 19 System.out.println("cla.getDeclaredConstructor() >>> " + studentConstructor);// cla.getDeclaredConstructor() >>> private test.Student(int,double,java.lang.String) 20 // 调用方法,非公共属性需要执行setAccessible()方法 21 // student = (Student) studentConstructor.newInstance(10, 3.5, "女");// java.lang.IllegalAccessException 22 studentConstructor.setAccessible(true); 23 student = (Student) studentConstructor.newInstance(10, 3.5, "女"); 24 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:10][grade:3.5][sex:女]