Java反射
Java 反射是可以让我们在运行时获取类的函数、属性、父类、接口等 Class 内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,通过调用 get/set 方法获取变量的值,即使方法或属性是私有的的也可以通过反射的形式调用.
当我们编写完一个 Java 项目之后,所有的 Java 文件都会被编译成一个.class 文件,这些 Class 对象承载了这个类型的父类、接口、构造函数、方法、属性等原始信息,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个类被加载以后,Java 虚拟机就
会在内存中自动产生一个 Class 对象。我们通过 new 的形式创建对象实际上就是通过这些 Class 来创建,只是这个过程对于我们是不透明的而已。
1.如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的 Class 对象。
Class<?> myObjectClass = MyObject.class;
2.如果你已经得到了某个对象,但是你想获取这个对象的 Class 对象,那么你可以通过下面的方法得到:
Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();
3.如果你在编译期获取不到目标类型,但是你知道它的完整类路径,那么你可以通过如下的形式来获取 Class 对象:
Class<?> myObjectClass = Class.forName("com.simple.User");
接口说明
// 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student". ( 常用方式 )
public static Class<?> forName (String className)
// 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student";
// 参数 2 为是否要初始化该 Class 对象,参数 3 为指定加载该类的 ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)
通过 Class 对象构造目标类型的对象:
private static void classForName() { try { // 获取 Class 对象 Class<?> clz = Class.forName("org.java.advance.reflect.Student"); // 通过 Class 对象获取 Constructor,Student 的构造函数有一个字符串参数 // 因此这里需要传递参数的类型 ( Student 类见后面的代码 ) Constructor<?> constructor = clz.getConstructor(String.class); // 通过 Constructor 来创建 Student 对象 Object obj = constructor.newInstance("mr.simple"); System.out.println(" obj : " + obj.toString()); } catch (Exception e) { e.printStackTrace(); } }
接口说明
// 获取一个公有的构造函数,参数为可变参数,如果构造函数有参数,那么需要将参数的类型传递给 getConstructor 方法
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// 获取目标类所有的公有构造函数
public Constructor[]<?> getConstructors ()
私有方法调用必须设置accessible为true,不进行java语言访问检查
Constructor<?> constructor = clz.getConstructor(String.class);
// 设置 Constructor 的 Accessible
constructor.setAccessible(true);
// 设置 Methohd 的 Accessible
Method learnMethod = Student.class.getMethod("learn", String.class);
learnMethod.setAccessible(true);
获取方法:
要获取当前类中定义的所有方法可以通过 Class 中的 getDeclaredMethods 函数,它会获取到当前类中的 public、default、protected、private 的所有方法。
而 getDeclaredMethod(String name, Class...<?> parameterTypes)则是获取某个指定的方法
要获取当前类以及父类中的所有 public 方法可以通过 Class 中的 getMethods 函数,而 getMethod 则是获取某个指定的方法
接口说明
// 获取 Class 对象中指定函数名和参数的函数,参数一为函数名,参数 2 为参数类型列表
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)
// 获取该 Class 对象中的所有函数( 不包含从父类继承的函数 )
public Method[] getDeclaredMethods ()
// 获取指定的 Class 对象中的**公有**函数,参数一为函数名,参数 2 为参数类型列表
public Method getMethod (String name, Class...<?> parameterTypes)
// 获取该 Class 对象中的所有**公有**函数 ( 包含从父类和接口类集成下来的函数 )
public Method[] getMethods ()
这里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。
而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。
获取属性:要获取当前类中定义的所有属性可以通过 Class 中的 getDeclaredFields 函数,它会获取到当前类中的 public、default、protected、private 的所有属性。
而 getDeclaredField 则是获取某个指定的属性要获取当前类以及父类中的所有 public 属性可以通过 Class 中的 getFields 函数,而 getField 则是获取某个指定的属性。
接口说明
// 获取 Class 对象中指定属性名的属性,参数一为属性名
public Method getDeclaredField (String name)
// 获取该 Class 对象中的所有属性( 不包含从父类继承的属性 )
public Method[] getDeclaredFields ()
// 获取指定的 Class 对象中的**公有**属性,参数一为属性名
public Method getField (String name)
// 获取该 Class 对象中的所有**公有**属性 ( 包含从父类和接口类集成下来的公有属性 )
public Method[] getFields ()
这里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。
而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。
获取 Class 对象的父类:
Student student = new Student("mr.simple"); Class<?> superClass = student.getClass().getSuperclass(); while (superClass != null) { System.out.println("Student's super class is : " + superClass.getName()); // 再获取父类的上一层父类,直到最后的 Object 类,Object 的父类为 null superClass = superClass.getSuperclass(); }
获取 Class 对象中实现的接口:
private static void showInterfaces() { Student student = new Student("mr.simple"); Class<?>[] interfaceses = student.getClass().getInterfaces(); for (Class<?> class1 : interfaceses) { System.out.println("Student's interface is : " + class1.getName()); } }
获取注解:
通过反射 api 我们也能够获取一个 Class 对象获取类型、属性、函数等相关的对象,通过这些对象的 getAnnotation 接口获取到对应的注解信息。
如
@Test(tag = "Student class Test Annoatation") public class Student extends Person implements Examination { // 年级 @Test(tag = "mGrade Test Annotation ") int mGrade; // ...... }
然后通过相关的注解函数得到注解信息
private static void getAnnotationInfos() { Student student = new Student("mr.simple"); Test classTest = student.getClass().getAnnotation(Test.class); System.out.println("class Annotatation tag = " + classTest.tag()); Field field = null; try { field = student.getClass().getDeclaredField("mGrade"); Test testAnnotation = field.getAnnotation(Test.class); System.out.println("属性的 Test 注解 tag : " + testAnnotation.tag()); } catch (Exception e) { e.printStackTrace(); } }
接口说明
// 获取指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
// 获取 Class 对象中的所有注解
public Annotation[] getAnnotations();