码迷,mamicode.com
首页 > 编程语言 > 详细

Java反射

时间:2020-02-19 23:51:42      阅读:87      评论:0      收藏:0      [点我收藏+]

标签:有一个   strong   classname   设置   cep   catch   led   leo   灵活   

反射

反射库( reflection library ) 提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵 Java 代码的程序。这项功能被大量地应用于 JavaBeans中,它是 Java组件的体系结构。

能够分析类能力的程序称为反射(reflective )。反射机制的功能极其强大,在下面可以看到,反射机制可以用来:

  • 在运行时分析类的能力。
  • 在运行时查看对象,例如,编写一个 toString方法供所有类使用。
  • 实现通用的数组操作代码。
  • 利用 Method对象,这个对象很像中的函数指针。

class类

最常用的 Class方法是 getName。这个方法将返回类的名字。如果类在一个包里,包的名字也作为类名的一部分:包名.类名。

还可以调用静态方法 forName 获得类名对应的 Class 对象。

String dassName = "java.util .Random"; 
Class cl = Class.forName(className) ;

这个方法只有在 className是类名或接口名时才能够执行。否则,forName方法将抛出一个 checked exception (已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器(exception handler ) o。

请注意,一个 Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如, int 不是类,但 int.class 是一个 Class类型的对象。

Class 类实际上是一个泛型类。例如, Employee.class 的类型是 Class

虚拟机为每个类型管理一个 Class 对象。因此,可以利用=运算符实现两个类对象比较的操作。例如,if (e.getClass() == Employee,class) . . .

还有一个很有用的方法 newlnstance( ),可以用来动态地创建一个类的实例例如:e.getClass0.newlnstance();创建了一个与 e具有相同类类型的实例。

newlnstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常。

捕获异常

当程序运行过程中发生错误时,就会“抛出异常“抛出异常比终止程序要灵活得多,这是因为可以提供一个“ 捕获” 异常的处理器(handler ) 对异常情况进行处理。如果没有提供处理器,程序就会终止,并在控制台上打印出一条信息,其中给出了异常的类型。

异常有两种类型:未检查异常和已检查异常。

如果try块中抛出异常,则将跳过 try块中的剩余代码,程序直接进人catch 子句(这里,利用Throwable类的 printStackTrace 方法打印出栈的轨迹。Throwable 是 Exception 类的超类)。如果try块中没有抛出任何异常,那么会跳过 catch 子句的处理器代码。

利用反射分析类的能力

反射机制最重要的内容—检查类的结构。

  • 在java.lang.reflect 包中有三个类 Field、Method 和 Constructor分别用于描述类的域、方法和构造器。
    • Field类有一 个getType方法,用来返回描述域所属类型的 Class 对象。
    • Method类还有一个可以报告返回类型的方法。
    • Method 和 Constructor类有能够 报告参数类型的方法
    • 这三个类都有一个叫做 getName 的方法,用来返回项目的名称。
    • 这三个类还有一个叫做 getModifiers 的方法,它将返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况。
      • 另外,还可以利用java.lang.reflect 包中的 Modifier类的静态方法分析 getModifiers 返回的整型数值。
      • 例如,可以使用 Modifier类中的 isPublic、 isPrivate 或 isFinal 判断方法或构造器是否是 public、 private 或 final。
      • 还可以利用 Modifier.toString方法将 修饰符打印出来。
  • Class类(java.lang.Object下java.lang.Class
    • Class类中的 getFields、 getMethods 和 getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员
    • Class 类的 getDeclareFields、 getDeclareMethods 和 getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员

在运行时使用反射分析对象

查看任意对象的数据域名称和类型:

  • 获得对应的 Class 对象。
  • 通过 Class 对象调用 getDeclaredFields。

利用反射机制可以查看在编译时还不清楚的对象域。

查看对象域的关键方法是 Field类中的 get 方法:如果 f 是一个 ==Field类型的对象==(例如,通过 getDeclaredFields 得到的对象),obj 是某个==包含 f域的类==的==对象==,f.get(obj)将返回一个对象,其值为 obj 域的当前值。

Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989) ; 
Class cl = harry.getClass0; 
    // the class object representing Employee
Field f = cl .getDeclaredFieldC'name") : 
    // the name field of the Employee class
Object v = f.get (harry); 
// the value of the name field of the harry object,i .e., the String object "Harry Hacker"

实际上,这段代码存在一个问题:如果name是一个私有域,那么get 方法将会抛出一个 IllegalAccessException。

反射机制的默认行为受限于 Java 的访问控制。除非拥有访问权限,否则 Java 安全机制只允许査看任意对象有哪些域,而不允许读取它们的值.

然而,如果一个 Java 程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,需要调用 Field、Method 或 Constructor 对象的 setAccessible 方法。

setAccessible方法是 AccessibleObject类中的一个方法,它是 Field、 Method 和 Constructor类的公共超类。这个特性是为调试、持久存储和相似机制提供的。

可以获得就可以设置。调用 f.set(obj,value) 可以将 obj 对象的 f 域设置成新值。

编写一个可供任意类使用的通用 toString方法:使用getDeclaredFileds 获得所有的数据域,然后使用setAccessible 将所有的域设置为可访问的。对 于每个域,获得了名字和值。

PS:循环引用将有可能导致无限递归。因此,ObjectAnalyzer将记录已经被访问过的对象。

为了能够査看数组内部,需要采用一种不同的方式。

可以使用 toString方法查看任意对象的内部信息。例如,下面这个调用:

ArrayList<Integer> squares = new ArrayList<>(); 
for (int i = 1; i <= 5; i++) squares.add(i * i); System.out.println(new ObjectAnalyzer().toString(squares));

将会产生下时的打印结果:

java.util.Arraylist[elementData=class java.lang.Object[]{java.Intager[value=1][][],java.1ang.Integer[value=4][][],java.1ang.Integer[value=9][][],java.1ang.Integer[value=16][][],java.1ang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]

还可以使用通用的 toString 方法实现自己类中的 toString 方法, 如下所示:

public String toString() {
    return new ObjectAnalyzer().toString(this); 
}

这是一种公认的提供 toString 方法的手段。

使用反射编写泛型数组代码

java.lang.reflect 包中的 Array类允许动态地创建数组。例如,将这个特性应用到 Array类中的 copyOf方法实现中。

将一个 Employee[]临时地转换成 Object[]数组,然后再把它转换回来是可以的,但一从开始就是 Object[] 的数组却永远不能转换成 Employee[]数组。

Array类中的静态方法 newlnstance, 它能够构造新数组。在调用它时必须提供两个参数,一个是数组的元素类型,一个是数组的长度。
Object newArray = Array.newlnstance(componentType, newLength) ;
可以通过调用 Array.getLength(a) 获得数组的长度。

而要获得新数组元素类型,就需要进行以下工作:

  1. 首先获得 a数组的类对象。
  2. 确认它是一个数组。
  3. 使用 Class类(只能定义表示数组的类对象)的 getComponentType确定数组对应的类型。
public static Object goodCopyOf(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, Math.min(length,newLength));
    return newArray;
}

请注意,这个 CopyOf方法可以用来扩展任意类型的数组,而不仅是对象数组,基本类型数组也可。

intn a = { 1,2, 3, 4, 5 };
a = (int[]) goodCopyOf(a, 10);

整型数组类型 int[] 可以被转换成 Object,但不能转换成对象数组,所以goodCopyOf的参数声明为 Object 类型,而不要声明为对象型数组(Object[])。

java.lang.reflect.Array 1.1

  • static Object get(Object array,int index)
  • static xxx getxxx(Object array,int index)
    (xxx 是 boolean、byte、char、 double、 float、int、 long、 short 之中的一种基本类型。)
    这些方法将返回存储在给定位置上的给定数组的内容。
  • static void set(Object array,int index,Object newValue)
  • static setXxx(Object array,int index,xxx newValue)
    ( xxx 是 boolean、 byte、char、double、float、int、 long、short 之中的一种基本类型。)
    这些方法将一个新值存储到给定位置上的给定数组中。
  • static int getLength(Object array)
    返回数组的长度。
  • static Object newInstance(Class componentType,int length)
  • static Object newInstance(Class componentType, int[] lengths)
    返回一个具有给定类型、给定维数的新数组

调用任意方法

在 Method类中有一个 invoke 方法,它允许调用包装在当前 Method 对象中 的方法。invoke 方法的签名是:
Object invoke(Object obj, Object... args)
第一个参数是隐式参数,其余的对象提供了显式参数。

对于静态方法,第一个参数可以被忽略,即可以将它设置为 null。

例如,假设用 ml 代表 Employee类的 getName方法,下面这条语句显示了如何调用这个方法:
String n = (String) ml.invoke(harry);

如果返回类型是基本类型, invoke方法会返回其包装器类型。

如何得到 Method对象呢?

  • 可以通过调用 getDeclareMethods方法,然后对返回的 Method对象数组进行查找,直到发现想要的方法为止。

  • 也可以通过调用Class类中的 getMethod方法得到想要的方法。它与 getField方法类似。需要传入方法名和参数类型。
    Method getMethod(String name, Class... parameterTypes)
    例如,下面说明了如何获得 Employee类的 getName方法和 raiseSalary方法的方法指针。

    Method ml = Employee.class.getMethod("getName"); 
    Method m2 = Employee.class.getMethod("raiseSalary",double.class);

Java反射

标签:有一个   strong   classname   设置   cep   catch   led   leo   灵活   

原文地址:https://www.cnblogs.com/l999q/p/12333740.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!