Java Tutorials-03-反射

反射和RTTI

  • RTTI: Run-Time Type Indentification, 运行时类型识别. 并非Java体系中的概念, 来自Thinking in C++
  • Reflection(反射): 允许在程序运行期间探知并分析类对象的结构.

RTTI(Run-Time Type Identification)运行时类型识别。在《Thinking in Java》一书第十四章中有提到,其作用是在运行时识别一个对象的类型和类的信息。主要有两种方式:一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型;另一种是“反射”机制,它允许我们在运行时发现和使用类的信息。

Class 类 & Class 对象

每个类实例都有一个相对应的”Class 对象”, 所以类实例在进行向上转型时不会丢失原有的类型信息, 这个 Class 对象的类型就是”Class 类”, 位于 java.lang.Class;

  • T.class: 获取类型 T 的 Class 对象, 基本类型 int 也可以通过 int.class 获取, 虽然 int 等基本类型不是类, 但是也可以 Class cl = int.class;

  • t.getClass(): 返回的也是 Class 对象, getClass() 是 Object 类的方法;

  • 可以用 == 判断两个obj的 class 是否来自同一个 class 对象: if(a.getClass() == A.class);

JVM 通过 ClassLoader 从 .class 文件中加载并创建 class 对象 Advanced-Java.04.ClassLoader

用反射创建类

// 方式1:
Human human = new Human();
Class c1 = human.class;
Human human = (Human)c1.newInstance(); // Class.newInstance()返回的是Object类型
// 方式2:
Class c1 = Class.forName("org.xxx.Human");
Human human = (Human)c1.newInstance();

使用反射API分析类

Class, Constructor(构造方法), Field(属性), Method(方法), Modifier(作用域)

Class cl = Class.forName("orj.xxx.ClassName");

// Class.newINstance创建类对象, 这调用类的默认构造器
ClassName obj = cl.newInstance();

// 获取类的public static
String modify = Modifier.toString(cl.getModifiers());

// 获取构造器
Constructor[] contructors = cl.getDeclaredConstructors();

// 获取方法
Method[] methods = cl.getDeclaredMethods();
// Class Method.getReturnType(); // 获得方法返回类型

// 获取类的限定
String methodModiifier = Modifier.toString(method.getModifiers());

// 调用任意方法
Class clazz = ConcurrentHashMap.class;

// 获取concurrentHashMap.containsKey()方法
// 第二个参数是可变参数Class<?>... parameterTypes
Method method = clazz.getMethod("containsKey", Object.class);

// 第一参数是类实例, 如果调用static方法, 第一个参数穿null
// 第二个参数是可变参数Object... args
method.invoke(new ConcurrentHashMap<String,String>(), "ThisIsKey");

使用反射API调用方法

Test test = (Test) clazz.newInstance();

// 从class获取参数是(string)的 Method
Method method = clazz.getDeclaredMethod("sayHello", String.class);

// 这里调用的是实例方法,第一个参数是Test的实例
method.invoke(test, 15, "arg");

Class类的方法列表

  • Class<?> forName(String className)
  • Class<?> forName(String name, boolean initialize, ClassLoader loader)
  • T newInstance():
  • boolean isInstance(Object) : Native方法, 注意区别instanceof二元操作符
  • boolean isArray(): 是否是数组, Native方法
  • Class<?> getComponentType(): 返回Class类型, 返回的Class是数组元素的类型, 示例代码: String[].class.getComponentType()
  • Method getMethod(String name, Class<?>... parameterTypes): 返回指定方法名和形参的方法
  • 以下用来获取构造器/方法/属性的列表:
    • Constructor[] getDeclaredConstructors()
    • Method[] getDeclaredMethods()
    • Field[] getDeclaredFields()

数组和反射

java.lang.reflect.Array类提供了数组的反射方法, 注意区分java.util.Arrays

用反射创建数组

// Array.newInstance 创建数组
int[] array1 = (int[])Array.newInstance(int.class, 10);

/* Class.newInstance 创建数组, 这里会抛异常, 因为数组类型T[]没有默认构造函数
* 这也是Array.newInstence和Class.newInstance的区别
*/
Class intArrClass = array1.getClass();
int[] array2 = (int[])intArrClass.newInstance(); // 异常 !!

用反射分析数组

// Class.getComponentType 获取数组元素类型
Class c = array.getClass().getComponentType();

// Array.getLength获取长度
int l = Array.getLength(array);

// 非数组的类型调用getComponentType会发生什么? 返回Null
Class c2 = Object.class.getComponentType();

reflect.Array类的方法列表

  • Object newInstance(Class<?> componentType, int length)
  • reflect.Array并没有探测数组元素类型, 和数组长度的方法:(Class类提供了一个: array.getClass().getComponentType().toString());
  • int Array.getLength(Object arr) : 返回值是int, 数组大小最大只能是int ?

反射的使用场景

使用到 class.