java核心技术卷1::反射机制

反射

在程序运行期间发现更多的类及其属性的能力,能够分析类能力的程序称为反射,便于编写能够动态操纵 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 参数进行类型匹配

// 匹配泛型方法中的 Class<T> 参数的类型变量很有使用价值。
public static <T> Pair<T> makePairClass<T> c) throws InstantiationException,IllegalAccessException
{return new Pair<>c.newInstance), c.newInstance));
}
// Employee.class 是类型 Class<Employee> 的一个对象。makePair 方法的类型参数 T 同 Employee 匹配,并且编译器可以推断出这个方法将返回一个 Pair<Employee>。

虚拟器中的泛型类型信息

Java 泛型的卓越特性之一是在虚拟机中泛型类型的擦除。但擦除的类依旧保留泛型祖先的微弱记忆。

// 原始类 pubilc static <T extends Comparable<? super T>> T minT[] a)
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[])。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注