java中的反射

本文深入探讨Java反射机制的功能,包括如何在运行时获取类信息、创建对象、访问属性和调用方法,并通过实例展示了如何利用反射进行类的动态加载和方法的动态调用。此外,介绍了如何通过配置文件(如Properties文件)来动态获取类名,并运用反射技术实现代理类和解耦设计。

反射是Java程序开发语言的特征之一。它允许动态地发现和绑定类、方法、字段,以及所有其他的由语言所产生的元素。

Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法。通过反射甚至可以调用到private的方法;

Java反射所需要的类并不多,主要有java.lang.Class类和java.lang.reflect包中的Field、Constructor、Method、Array类。

Class类:Class类的对象表示正在运行的 Java 应用程序中的类和接口。
Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。它封装了反射类的构造方法。  
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。它是用来封装反射类方法的一个类。
Array类:提供了动态创建数组和访问数组的静态方法。该类中的所有方法都是静态方法 

class类

JVM为每种类型管理着一个独一无二的Class对象----每个类(型)都有一个Class对象。
Java程序运行过程中,当需要创建某个类的实例时,JVM首先检查所要加载的类对应的Class对象是否已经存在。如果还不存在,JVM就会根据类名查找对应的字节码文件并加载,接着创建对应的Class对象,最后才创建出这个类的实例。
运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类或接口的类型信息。要想获取类或接口的相应信息,需要先获取这个Class对象。 

获得Class对象

调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。
例如, MyObject x=new MyObject(); Class c1 = x.getClass();
使用Class类的forName()静态方法获得与字符串对应的Class对象。
Class c2=Class.forName("java.lang.String");
注意:参数字符串必须是类或接口的完全限定名。
使用类型名.class获取该类型对应的Class对象。
例如,Class cl1 = Manager.class; Class cl2 = int.class;    Class cl3 = double[].class;

使用反射机制来获取类的详细信息、创建类的对象、访问属性值、调用类的方法等。

 获取类型信息

要在运行时获取某个类型的信息,需先获取这个类型所对应的Class对象,然后调用Class类提供的相应方法来获取。
获取指定类对应的Class对象 Class
clazz = Class.forName("java.util.ArrayList");
获取指定类的包名clazz.getPackage()获取此Class对象所对应的实体所在的包的信息描述类java.lang.Package的一个对象。
String packageName = clazz.getPackage().getName();
获取指定类的修饰符 通过Class对象的getModifiers()方法可以获取此Class对象所对应的实体的用整数表示的类修饰符值。
int mod = clazz.getModifiers(); String modifier = Modifier.toString(mod);
获取指定类的完全限定名 className = clazz.getName();
获取指定类的父类 superClazz = clazz.getSuperclass();
获取指定类实现的接口
通过Class对象的getInterfaces()方法可以获取此Class对象所对应的实体实现的所有接口的Class对象数组。
Class[] interfaces = clazz.getInterfaces();

获取指定类的成员变量

通过Class对象的getFields()方法可获取此Class对象所对应的实体的所有public字段(成员变量)。如果要获取所有字段,可以使用getDeclaredFields()方法。

注意:方法返回的是java.lange.reflect.Field类的对象数组。Field类用来代表字段的详细信息。通过调用Field类的相应方法可以获取字段的修饰符、数据类型、字段名等信息。

获取类的构造方法 通过Class对象的getConstructors()方法可获取该Class对象所对应的实体的所有public构造方法。如果要获取所有的构造方法,可以使用getDeclaredConstructors()方法。
Constructor[] constrcutors = clazz.getDeclaredConstructors();
获取类的成员方法 通过Class对象的getMethods()方法获取到的是该Class对象所对应的实体的所有public成员方法。如果要获取所有成员方法,可以使用getDeclaredMethods()方法。
Method[] methods = clazz.getDeclaredMethods(); 

 创建对象      
      以前,创建对象的方法通常都是通过new操作符调用该类的构造方法来创建的。
例如,Date currentDate = new Date();
      大多数情况下,这种方式已足够满足需求。因为在编译期间,已经知道要创建的对象所对应的类名称。
但是,如果现在编写一个开发工具软件,将可能直到运行时才知道要创建的对象所对应的类名称。
例如,一个GUI开发工具可以让用户拖拽各种图形组件到设计界面上。
public Object create(String className){   
       根据类名来创建出它的对象
       返回这个新创建的对象
}

使用无参构造方法

如果要使用无参的构造方法创建对象,只需调用这个类对应的Class对象的newInstance()方法。 Class c = Class.forName("java.util.ArrayList"); List list = (List) c.newInstance();
需要注意的是:如果指定名称的类没有无参构造方法,在调用newInstance()方法时会抛出一个NoSuchMethodException异常。 

使用带参数的构造方法

要使用带参数的构造方法来创建对象,可以分为如下3个步骤来完成。
第1步  获取指定类对应的Class对象。
第2步  通过Class对象获取满足指定参数类型要求的Constructor对象。
第3步  调用指定Constructor对象的newInstance方法,传入对应的参数值,创建对象。

调用方法 使用反射可以取得指定类的指定方法的对象代表,方法的对象代表就是java.lang.reflect.Method类的实例,通过Method类的invoke方法可以动态调用这个方法。
public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
该方法的第一个参数是一个对象类型,表示要在指定的这个对象上调用这个方法
第二个参数是一个可变参数,用来给这个方法传递参数值;
invoke方法的返回值用来表示动态调用指定方法后的实际返回值。

访问成员变量的值

使用反射可获取类的成员变量的对象代表,成员变量的对象代表是java.lang.reflect.Field类的实例,可以使用它的getXXX方法来获取指定对象上的值,也可以调用它的setXXX方法来动态修改指定对象上的值,
其中的XXX表示成员变量的数据类型。

       在实际开发中,为了达到各个类之间的依赖关系剥离(也就是经常说的解耦),在对类的调用和实例化的时候,通过在配置文件中配置相应的类名,在程序中读取类名,然后通过反射技术在程序中加载和实例化,如常见的数据库驱动程序类,为了达到不依赖特定数据库驱动类,将用到的数据库驱动类名放到配置文件中(常用的有XML文件、Properties文件和文本文件),然后在程序中加载驱动,来实现对数据库的解耦,也就是说只要修改配置文件,就可以方便地更改数据库类型。 

  Properties文件的处理

Java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是“键=值”的格式,也就是说文件的每一行都是先定义一个键名,然后等于号后面是值,在properties文件中,可以用“#”来作注释,
properties文件在Java编程中用到的地方很多,操作很方便。最常见的操作该类文件的方法是通过Properties类来完成。
JDK 中的 Properties 类存在于包java.util 中,该类继承自 Hashtable ,主要方法包括:
getProperty(String key):用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到key所对应的 value。

load(InputStream  inStream) :从输入流中读取属性列表(键和元素对)。通过对指定的文件进行装载来获取该文件中的所有键-值对,以供getProperty(String key) 来搜索。
setProperty(String  key, String  value):调用Hashtable的方法put 。来设置“键-值”对。
store(OutputStream out, String comments):以适合使用load方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。
clear ():清除所有装载的“键-值”对。

### 三级标题:Java反射的定义与工作原理 Java中的反射机制是一种允许程序在运行时动态地检查和操作类、接口、字段、方法等内部信息的技术。通过这种机制,可以在不知道对象具体类型的情况下创建对象、调用方法以及访问其属性,甚至包括私有成员。这一特性赋予了Java程序极大的灵活性和扩展性,是构建复杂系统时不可或缺的一部分[^1]。 当一个类被加载到JVM(Java虚拟机)中后,每个类都会对应生成一个`Class`对象。这个`Class`对象包含了该类的所有信息,如构造函数、方法列表、属性等。正常情况下,我们使用类来创建实例;而在反射机制下,则可以通过获取到的`Class`对象来反向了解并操作对应的类及其对象。这意味着开发者能够在应用程序执行期间对类的行为进行修改或增强,例如读取文件内容存入Bean再存储至数据库的过程中遇到字段名不匹配的问题时,可以利用反射动态处理不同命名规则之间的映射关系[^4]。 实现反射功能的核心在于`java.lang.reflect`包提供的APIs,它支持动态地加载类、获取类的信息、创建实例及调用方法等功能。比如,若想通过反射调用某个方法,首先需要获得目标类的`Class`对象,然后查找所需的方法,并设置访问权限(对于私有方法),最后才能完成实际的调用过程。下面是一个简单的示例展示了如何使用反射来调用一个方法: ```java public class ReflectionExample { public void sayHello() { System.out.println("Hello from reflection!"); } public static void main(String[] args) throws Exception { // 获取ReflectionExample类的Class对象 Class<?> clazz = Class.forName("ReflectionExample"); // 创建实例 Object instance = clazz.getDeclaredConstructor().newInstance(); // 获取sayHello方法 Method method = clazz.getMethod("sayHello"); // 调用方法 method.invoke(instance); } } ``` 上述代码演示了从加载类到最终调用其公共方法的基本流程。值得注意的是,在涉及私有成员时还需额外调用`setAccessible(true)`以绕过Java语言的安全限制。 综上所述,Java反射机制基于每个类唯一的`Class`对象,使得程序具备了在运行时刻分析类的能力,从而实现了高度灵活的应用开发模式。然而,尽管反射提供了强大的功能,但过度依赖可能会导致性能下降以及安全风险增加等问题,因此应当谨慎使用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值