反射
在程序运行期间发现更多的类及其属性的能力,能够分析类能力的程序称为反射,便于编写能够动态操纵 java 代码的程序。特别是在设计或运行中添加新类时,能够快速地应用开发工具动态的查询新添类的能力
反射机制作用:
在运行时分析类的能力 在运行时查看对象 实现通用的数组操作代码 利用 Method 对象,类似函数指针
Class 类
<Class> e;
Class cl = e.getClass();//获取 Class 类型实例,Class 对象表示一个特定类的属性。
e.getClass().getName();//返回类的名字。
//如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。
//异常处理器,必须提供。
try{
Class c2 = Class.forName(className);//静态方法,获取类名对应的 Class 对象
}catch(Exception e){
e.printStackTrace();// 将 Throwable 对象和栈的轨迹输出到标准错误流
}
如果T是任意的java类型(或void关键字),T.class 将代表匹配的类对象。
Class cl3 = Random.class;
一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。
if(e.getClass() == Employee.class); // == 比较两个类对象。
e.getClass().newInstance(); // 动态创建类实例,调用默认的构造器(无参)。
Object m = Class.forName("Employee").newInstance(); //根据存储在字符串中的类名创建一个类
捕获异常
抛出异常比终止程序灵活的多,这是因为可以提供一个“捕获”异常的处理器(handle)对异常情况进行处理
异常有两种类型:
已检查类型(会检查是否提供了处理器) 未检查类型(不会检查是否提供了处理器,需要精心编写代码避免错误发生)
利用反射分析类的能力
reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、方法和构造器。
Field 类有一个 getType 方法,用来返回描述域所属类型的 class 对象。 Method 和 Constructor 类有能够报告参数类型的方法。 Method 类还有一个可以报告返回类型的方法。 三个类中还有一个 getModifiers 它将返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况。 Modifier 类的静态方法分析 getModifiers 返回的整型数值。 Modifier.toString 方法将修饰符打印出来。 Class 类中的 getFields、getMethods 和 getConstructors 方法将分别返回类提供的 public域、方法和构造器数组,其中包括超类的公有成员。 Class 类的 getDeclareFields、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类成员。
在运行时使用反射分析对象
如果 f 是一个 Field 类型的对象(如,通过 getDeclaredFields 得到的对象),obj 是某个包含 f 域的类的对象,f.get(obj) 将返回一个对象,其值为obj域的当前值。
反射机制的默认行为受限于 Java 的访问控制。如果 f 是一个私有域,所以 get 方法将会跑出一个 IllegalAccessException。只有利用 get 方法才能得到可访问域的值。除非拥有访问权限,否则 Java 安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。
setAccessible(true);
能够覆盖访问控制。这个特性是为调试、持久存储和相似机制提供的。 面对数值类型,使用 get 方法时,反射机制将会自动地将这个域值打包到相应的对象包装器中。 f.set(obj, value)
可以将 obj 对象的 f 域设置成新值。 OnjectAnalyzer 将记录已经被访问过的对象以防止无限递归。
使用反射编写泛型数组代码
在编写一个通用的动态数组创建方法时,会遇到原数组无法copy元素至创建的 Object 类型数组里。
这是因为一个子类数组能够临时转换成父类数组,然后再转换回来也可以。而一个一开始就是父类的数组不能转换成子类数组。如果这样做,则在运行时 java 会产生 ClassCastException 异常。
Object newArray = Array.newInstance(componentType, newLength);
通过Array类的静态方法构造新数组。需要提供元素类型和数组长度 Array 元素类型获取:
首先获得 a 数组的类对象。 确认它是一个数组。 使用 Class 类(只能定义表示数组的类对象)的 getComponentType 方法确定数组对应的类型。
// this method can use for the type at will of array.
public static Object CopyOf(Object a, int newLength){
Class cl = a.getClass();
if(!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
调用任意方法
仅建议在有必要的时候使用,建议使用接口或 lambda
// 需要提供参数类型
Method m1 = <Object>.class.getMethod(<MethodName>);
Method m2 = <Object>.class.getDeclaredMethod(<MethodName>);
<Type> M1Go = (TypeName) m1.invoke(<obj>, <Obj_Arg>);
反射与泛型
反射允许你在运行时分析任意的对象。如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除
泛型 Class 类
Class类是泛型的。String.class 实际上是一个 Class 类的对象(唯一)。
使用 Class 参数进行类型匹配
public static < T> Pair< T> makePair ( Class< T> c) throws InstantiationException, IllegalAccessException
{
return new Pair < > ( c. newInstance ( ) , c. newInstance ( ) ) ;
}
虚拟器中的泛型类型信息
Java 泛型的卓越特性之一是在虚拟机中泛型类型的擦除。但擦除的类依旧保留泛型祖先的微弱记忆。
public static Comparable min ( Comparable[ ] a)
反射 API 能够确定:
这个泛型方法有一个叫做 T 的类型参数。 这个类型参数有一个子类型限定,其自身又是一个泛型类型。 这个限定类型有一个通配符参数。 这个通配符参数有一个超类型限定。 这个泛型方法有一个泛型数组参数。
需要重新构造实现者声明的泛型类以及方法中的所有方法。但是,对于特定的对象或方法调用,如何解释类型参数。
为表达泛型类型声明,使用 java.lang.reflect 包中提供的接口 Type。这个接口包含下列子类型:
Class 类,描述具体类型。 TypeVariable 接口,描述类型变量(如 T extends Comparable<? super T>)。 WildcardType 接口,描述通配符(如 ? super T)。 ParameterizedType 接口,描述泛型类或接口类型(如 Comparable<? super T>)。 GeneticArrayType 接口,描述泛型数组(如 T[])。