Java 的反射机制

设置字体大小:

好久没写文章了,忙完了三个课程设计又要去培训了,趁着培训还没开始赶紧写篇文章。昨天学习了 Java 中的反射机制,这里做一个记录。

Java 反射机制

Java 的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法或访问它的任意一个属性,这种动态获取的信息以及动态调用访问对象的方法和属性的功能称为 Java 语言的反射机制。

Java 的反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时获得任意一个类声明的属性和方法;在运行时调用任意一个对象的方法;在运行时访问或修改任意一个对象的属性等等。

下面我会分别对这5个功能进行说明,同时最后会给出一个完整的示例程序,建议在看那五部分的说明时对照着示例程序阅读。

根据对象找到类

Java 反射机制的源头是 java.lang.Class<T> 对象,后序的很多操作都是在获得了 Class 类对象的基础上进行的,可以将这个 Class 类的对象理解为存放在运行时我们要反射的那个类或者对象的本体。那怎么获得这个 Class 类对象呢?方法有三种,这里先说第一种:通过对象获得。

在 Object 类中有一个 getClass() 方法,可以返回该对象的 Class 类对象,前提是必须已经产生了这个类的对象。用法如示例程序中的第72行:

Class<?> cls = p1.getClass(); // 通过对象,得到 Class 对象的方法一

这个方法必须要先存在该类的对象才能反射,通常不使用,下面说一下第二种方法:利用 “类.class” 的形式取得 Class 类的对象。用法在示例程序中的第69行:

Class<?> cls = top.sunriseydy.example.reflectDemo.Person.class; // 通过类名,得到 Class 对象的方法二

方法三则是使用 Class 类提供的一个方法(static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。)来完成,使用比较多,用法在示例程序的第68行:

Class<?> cls = Class.forName("top.sunriseydy.example.reflectDemo.Person"); // 通过类名,得到 Class 对象的方法三

在得到了 Class 类的对象后我们又该怎么操作类呢?下面先从实例化这个类的对象说起。

构造对象

在上一个功能介绍中我们获得了一个类相关联的 Class 类的对象“cls”,在这里我们介绍一下如何通过这个 cls 来构造一个对象。

直接实例化对象

先说一说直接使用 Class 类的方法“public T newInstance() throws InstantiationException, IllegalAccessException”来实例化某个类的一个对象。用法在示例程序的第71行:

Person p1 = (Person) cls.newInstance(); // 直接使用反射实例化一个对象

由于 newInstance() 方法返回的是一个 Class<T> 类中的 T 参数,因此需要将其强制转换为要实例化的那个类,在我的示例程序中就是 Person 类。还要注意的是,该方法实际上调用了类中默认的那个空的构造方法,若类中没有空的默认构造方法就会报错。

调用默认构造方法

还有一种就是通过相关联的 Class 类对象来获取该类的默认构造方法,然后调用这个构造方法来实例化一个对象,这其中用到 java.lang.reflect.Constructor<T> 类,可以理解为存放类中构造方法的一种类。使用 Class 类的“getConstructor() ”方法来获得某个类的默认构造方法(不带参数)。注意,这个方法只能获得公有的构造方法,如果是私有的构造方法则需要使用“getDeclaredConstructor()” 方法获得,并调用该方法返回的对象的“setAccessible(true)”方法来将 Java 语言访问检查禁用,这样在运行时就不会检查这个对象的方法或者属性的访问权限了。但是,这样会造成很大的安全隐患!setAccessible() 方法的 API 文档链接:点这里,示例程序的第83-86行演示了调用私有的构造方法。

得到默认的构造方法后就可以使用 Constructor 类的“newInstance(Object... initargs)”方法来实例化一个对象,记得转换一下该方法返回的对象就行。

该部分在示例程序中的第75-77行:

Constructor<?> constructor = cls.getConstructor(); // 通过反射得到 Person 类的默认构造方法
Person p2 = (Person) constructor.newInstance(); // 利用得到的默认构造方法实例化一个对象
调用带有参数的构造方法

上面介绍了通过反射调用默认的构造方法,这里说一些调用有参数的构造方法。还是用到 Class 类的“getConstructor(Class<?>... parameterTypes) ”方法,只不过这里我们加了参数,这里的参数也就是构造方法里的参数的数据类型,只不过,在数据类型后面都要加上“.class”。在后面调用 Constructor 类的“newInstance(Object... initargs)”方法来实例化一个对象时要在方法中传递对应的构造方法的参数,见示例程序的第79-81行:

Constructor<?> constructor1 = cls.getConstructor(String.class, int.class, String.class); // 通过反射得到带有参数的构造方法
Person p3 = (Person) constructor1.newInstance("张三",21,"男"); // 利用得到的带参数的构造方法实例化一个对象

获得类方法和属性

反射的一个重大功能就是能在运行时获得该类的方法和属性(字段)。具体使用到 java.lang.reflect.Field 和 java.lang.reflect.Method 这两个类,可以理解为存放反射类的属性的类和方法的类。要获得他们,需要用到 Class 类中的“public Field getField(String name)”方法(得到指定属性名的属性类对象);“public Method getMethod(String name,
Class<?>... parameterTypes)”方法(得到指定方法名和方法参数的方法类对象);“public Field[] getFields()”方法(得到反射类中公有的属性,返回值是属性类的对象数组);“public Method[] getMethods()”方法(返回包含反射类中公有方法的方法类对象数组);当然还有可以得到私有访问权限的类方法或者类属性的方法,即:“getDeclaredField()”和“getDeclaredMethod()”,以及方法名加“s”的返回数组类型的“getDeclaredFields()”和“getDeclaredMethods()”。我想看到这里你也找到规律了。方法名中什么也不加的是得到特定的,而含有“Declared”的则是可以得到私有访问权限的,后面加有“s”的是得到所有的,返回的是数组类型。

该部分在示例程序中的第88-98行。

调用对象的类方法

在上一部分我们得到了反射类的方法和属性,这里就先说一下如何对指定的对象调用得到的类方法。我们要用到 Method 类中的“public Object invoke(Object obj,
Object... args)”方法,具体使用方法如下(在示例程序的第101-110行):

Method setAge = cls.getMethod("setAge", int.class); // 通过反射得到类方法
setAge.invoke((Object)p4,23); // 针对对象 p4 调用得到的方法

和调用私有的构造方法一样,如果需要调用私有的类方法,则需要调用 Method 类从 java.lang.reflect.AccessibleObject 继承过来的 “setAccessible(true)”方法来禁用 Java语言访问检查。

访问修改对象的属性

接下来介绍如何在运行时访问并修改指定对象的类属性,主要用到 Field 类的“public Object get(Object obj)”方法和“public void set(Object obj, Object value)”方法。相关 API 文档:get set。用法在示例程序中的第112-114行:

Field personSexField = cls.getDeclaredField("sex"); // 得到私有的类属性
personSexField.setAccessible(true); // 设置访问权限
System.out.println("直接访问 p4 的性别:" + personSexField.get((Object) p4));

先使用 Class 类的方法得到指定类属性的 Field 类对象,再调用 Field 类的这两个方法来取得或设置该类对象的属性值。这里要注意的是,这两个方法的参数“Object obj”的类型都是 Object 对象,因此在传递类对象的时候需要转化为 Object 类对象。在 get 方法中,返回值是 Object 类对象,但如果该属性值是基本类型值,则自动将其包装在一个对象中。

示例程序

见下一页


赞 (0)   -->微信赞赏<--

微信扫描下方左侧二维码或搜索“sunriseydy”关注我的公众号,便捷地阅读博客内容,订阅博客更新
也可以扫描下方右侧的小程序码,进入我的微信小程序:“sunriseydy”,在手机上阅读文章

      

版权说明:

知识共享许可协议
作品 sunriseydy 采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
文章内容如未说明均为原创,欢迎转载,但请注明原作者和出处。部分来自互联网的文章,如有侵权,请联系我,24小时内删除,谢谢
Email:i@mail.sunriseydy.top

评论一下呗亲

电子邮件地址不会被公开。 必填项已用*标注

添加表情