前言

Java的反射可以让我们在编译器(Compile Time)之外的运行期检查类,接口,变量以及方法的信息。并且实例化调用。

通用方法

反射都是通过获取类的Class进而进行各种调用。通过的可以获取类的Class,构造器,变量,方法,注解等等。

  1. public static void getClassDemo() {
  2. Class c = ClassDemo.class;
  3. System.out.println("allName:" + c.getName());
  4. System.out.println("simpleName:" + c.getSimpleName());
  5. System.out.println("modifiers:" + Modifier.toString(c.getModifiers()));
  6. System.out.println("package:" + c.getPackage());
  7. System.out.println("parentClass:" + c.getSuperclass().getSimpleName());
  8. Class[] interfaces = c.getInterfaces();
  9. for (Class in : interfaces) {
  10. System.out.println("interface:" + in.getName());
  11. }
  12. Constructor[] constructors = c.getConstructors();
  13. for (Constructor constructor : constructors) {
  14. System.out.println("constructor:" + constructor.getName());
  15. }
  16. Field[] fields = c.getFields();
  17. for (Field field : fields) {
  18. System.out.println("filed:" + field.getName());
  19. }
  20. Annotation[] annotations = c.getAnnotations();
  21. for (Annotation annotation : annotations) {
  22. System.out.println("annotation:" + annotation.toString());
  23. }
  24. }

通过以上方法就可以获取到基本的类型,需要注意的是,直接获取方法属性等等,那都是获取公开的方法,如果需要获取私有的方法,需要获取对应的方法。

  1. c.getConstructors();
  2. c.getDeclaredConstructors();
  3. c.getFields();
  4. c.getDeclaredFields();
  5. c.getMethods();
  6. c.getDeclaredMethods();
  7. .......

如上所示可以获取对应的私有的方法和属性。

泛型

对于泛型的反射调用,参考文档可得。

  1. 运用泛型反射的经验法则
  2. 下面是两个典型的使用泛型的场景:
  3. 1、声明一个需要被参数化(parameterizable)的类/接口。
  4. 2、使用一个参数化类。
  5. 当你声明一个类或者接口的时候你可以指明这个类或接口可以被参数化,java.util.List接口就是典型的例子。你可以运用泛型机制创建一个标明存储的是String类型list,这样比你创建一个Objectlist要更好。
  6. 当你想在运行期参数化类型本身,比如你想检查java.util.List类的参数化类型,你是没有办法能知道他具体的参数化类型是什么。这样一来这个类型就可以是一个应用中所有的类型。但是,当你检查一个使用了被参数化的类型的变量或者方法,你可以获得这个被参数化类型的具体参数。总之:
  7. 你不能在运行期获知一个被参数化的类型的具体参数类型是什么,但是你可以在用到这个被参数化类型的方法以及变量中找到他们,换句话说就是获知他们具体的参数化类型。
  8. 在下面的段落中会向你演示这类情况。

可参考文章:

如果方法是在一个被参数化类型之中(译者注:如T fun())那么你无法获取他的具体类型,但是如果方法返回一个泛型类(译者注:如List fun())那么你就可以获得这个泛型类的具体参数化类型。

数组

创建数组时采取类java.lang.reflect.Array,此方式可以创建指定类型的数组

  1. int[] intArray = (int[]) Array.newInstance(int.class, 3);
  2. Array.set(intArray, 0, 123);
  3. System.out.println("intArray[0] = " + Array.get(intArray, 0));

注:一般方法用的少,特殊地方处理。

动态代理

java的动态代理可以采取Proxy类处理,对接口进行动态代理可以采取java自带的Proxy,对类进行动态代理可采取cglib。

  1. 你可以通过使用Proxy.newProxyInstance()方法创建动态代理。newProxyInstance()方法有三个参数:
  2. 1、类加载器(ClassLoader)用来加载动态代理类。
  3. 2、一个要实现的接口的数组。
  4. 3、一个InvocationHandler把所有方法的调用都转到代理上。

注:Spring的AOP中有大量使用。

动态类加载与重载

java可实现运行期加载和重载类,实现比较难处理。
一个类只能被同一个ClassLoader实例加载一次
对于动态类的加载,可如下处理:

  1. ClassLoader classLoader = MainClass.class.getClassLoader();
  2. Class aClass = classLoader.loadClass("com.jenkov.MyClass");

重载比较费事:

动态类重载有一点复杂。Java内置的类加载器在加载一个类之前会检查它是否已经被加载。因此重载一个类是无法使用Java内置的类加载器的,如果想要重载一个类你需要手动继承ClassLoader。

对于动态的替换,可参考阿里开源的框架JarsLink:https://github.com/alibaba/jarslink

参考: