反射
在程序运行期间发现更多的类及其属性的能力,能够分析类能力的程序称为反射,便于编写能够动态操纵 java 代码的程序。特别是在设计或运行中添加新类时,能够快速地应用开发工具动态的查询新添类的能力
反射机制作用:
-
在运行时分析类的能力
-
在运行时查看对象
-
实现通用的数组操作代码
-
利用 Method 对象,类似函数指针
Class 类
<Class> e;
Class cl = e.getClass);//获取 Class 类型实例,Class 对象表示一个特定类的属性。
e.getClass).getName);//返回类的名字。
//如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。
//异常处理器,必须提供。
try{
Class c2 = Class.forNameclassName);//静态方法,获取类名对应的 Class 对象
}catchException e){e.printStackTrace);// 将 Throwable 对象和栈的轨迹输出到标准错误流
}
如果T是任意的java类型(或void关键字),T.class 将代表匹配的类对象。
Class cl3 = Random.class;
一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。
ife.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.getobj) 将返回一个对象,其值为obj域的当前值。
反射机制的默认行为受限于 Java 的访问控制。如果 f 是一个私有域,所以 get 方法将会跑出一个 IllegalAccessException。只有利用 get 方法才能得到可访问域的值。除非拥有访问权限,否则 Java 安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。
-
setAccessibletrue);
能够覆盖访问控制。这个特性是为调试、持久存储和相似机制提供的。
-
面对数值类型,使用 get 方法时,反射机制将会自动地将这个域值打包到相应的对象包装器中。
-
f.setobj, value)
可以将 obj 对象的 f 域设置成新值。
-
OnjectAnalyzer 将记录已经被访问过的对象以防止无限递归。
使用反射编写泛型数组代码
在编写一个通用的动态数组创建方法时,会遇到原数组无法copy元素至创建的 Object 类型数组里。
这是因为一个子类数组能够临时转换成父类数组,然后再转换回来也可以。而一个一开始就是父类的数组不能转换成子类数组。如果这样做,则在运行时 java 会产生 ClassCastException 异常。
-
Object newArray = Array.newInstancecomponentType, newLength);
通过Array类的静态方法构造新数组。需要提供元素类型和数组长度
-
Array 元素类型获取:
-
首先获得 a 数组的类对象。
-
确认它是一个数组。
-
使用 Class 类(只能定义表示数组的类对象)的 getComponentType 方法确定数组对应的类型。
// this method can use for the type at will of array.
public static Object CopyOfObject a, int newLength){Class cl = a.getClass);if!cl.isArray)) return null;Class componentType = cl.getComponentType);int length = Array.getLengtha);Object newArray = Array.newInstancecomponentType, newLength);System.arraycopya, 0, newArray, 0, Math.minlength, 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> makePairClass<T> c) throws InstantiationException,IllegalAccessException
{return new Pair<>c.newInstance), c.newInstance));
}
虚拟器中的泛型类型信息
Java 泛型的卓越特性之一是在虚拟机中泛型类型的擦除。但擦除的类依旧保留泛型祖先的微弱记忆。
public static Comparable minComparable[] a)
反射 API 能够确定:
-
这个泛型方法有一个叫做 T 的类型参数。
-
这个类型参数有一个子类型限定,其自身又是一个泛型类型。
-
这个限定类型有一个通配符参数。
-
这个通配符参数有一个超类型限定。
-
这个泛型方法有一个泛型数组参数。
需要重新构造实现者声明的泛型类以及方法中的所有方法。但是,对于特定的对象或方法调用,如何解释类型参数。
为表达泛型类型声明,使用 java.lang.reflect 包中提供的接口 Type。这个接口包含下列子类型:
-
Class 类,描述具体类型。
-
TypeVariable 接口,描述类型变量(如 T extends Comparable<? super T>)。
-
WildcardType 接口,描述通配符(如 ? super T)。
-
ParameterizedType 接口,描述泛型类或接口类型(如 Comparable<? super T>)。
-
GeneticArrayType 接口,描述泛型数组(如 T[])。