java反射(详细教程)
我们平常创建类的实例并调用类中成员需要建立在一个前提下,就是已经知道类名和类中成员的信息,灵活性大大降低。甚至在一些项目中还需要修改源码来满足使用条件,大大降低了操作的灵活性。
Java 反射(Reflection)是 Java 语言的一个重要特性,它允许程序在运行时而不是编译时获取类中成员,并且可以动态地操作这些类的成员。
在讲反射之前,我们先聊一聊反射机制中必不可少的一环:配置文件。我需要提前将我的类名,成员名填写到配置文件中去。这样在调用时就可以直接读取配置文件中的信息。相较于读取普通文件,配置文件的读取和调用更加简单。
一、读取配置文件:
1.创建配置文件:在指定的路径下(通常我放在src目录下,不为别的,就因为路径名简单)创建一个以“.properties”为结尾的File文件(普通的键值对形式)作为配置文件。
2.填写配置文件内容:以“键=值”的形式填写,不必添加空格和引号,调用时通过键的字符串形式调用(后面会举例说明)。
3.创建配置文件对象:首先new一个配置文件的对象,使用load方法将配置文件中的内容读取到配置文件对象中。
load方法提供了两种重载,既可以通过字节流输入,也可以通过字符流输入。推荐使用字节流输入,配置文件通常为全英文形式,字节流输入效率高。
4.获取配置文件中的内容:Properties类提供了一种getProperty(键)方法来获取配置文件中的信息,需要在方法中写入一个键,就会返回一个String类型键对应的值。这个String类型的值就是我们要从配置文件中拿到的值。
Exam、我提前在src目录下创建了一个名为“p.properties”的配置文件并提前写好键值对来表示需要需要反射文件的路径、属性和方法。路径表示com.reflectNewEdu包下的名为“reflectEdu”的文件,我需要反射这个文件中的成员。
//配置文件的内容
//路径
className = com.reflectNewEdu.reflectEdu
//属性
ObjName = str
//方法
methodName = printContent
methodName1 = printSome
Exam、在reflectEdu文件中需要写一些成员来帮助我们进行实验。
public class reflectEdu {//分别一个public和一个private属性public int num = 123;private String str = "字符串";//方法public void printContent() {System.out.println("输出内容");}private void printSome() {System.out.println("输出一些东西");}
}
Exam、创建一个Test类,在Test类中填写main方法具体实现反射:
二、获取类对象(main函数中):
1.首先按照上面的步骤创建配置文件对象,并使用字节流加载配置文件进配置文件对象中。
Properties properties = new Properties();
properties.load(new FileReader("src/p.properties"));
2.为了绕过对象阶段创建类对象,Class.forName(类全名)提供了一种在硬盘阶段获取类中成员的方法。Class.forName(类全名) 的核心作用,是通过类的全限定名,让JVM加载这个类,并返回一个代表该类“模板”的 Class 对象。此时仅完成了“类的加载”,并没有创建任何属于这个类的具体对象,就像拿到了一张“汽车设计图纸”,但还没造出真正能开的汽车。
该方法需要写入一个String类型的类全名,我们通过getProperty(键)方法从配置文件中拿到这个类全名。
Class.forName()方法返回一个Class类型的对象,这是一个类对象。类对象并不是哪个类的实例,但可以通过这个类对象下的方法获取到类中的成员,甚至是获取类的实例。(还是挺抽象的)
Class class0 = Class.forName(properties.getProperty("className"));
三、获取类对象中的成员:
一、属性(返回Field类型,需要import java.lang.reflect.Field;):
1.获取当前类里所有属性:getDeclaredFields(); (返回数组)
2.通过属性名获取属性:getDeclaredField(属性名);
3.获取所有公共类型属性:getFields();(返回数组)
4.获取公共类型中指定的属性:getField(属性名);
5.设置对象的属性:属性对象.set(对象名,属性值)
当属性被private修饰无法被调用时,可以使用:类对象名.setAccessible(true); 来进行暴力反射。
二、方法(返回Method类型,需要import java.lang.reflect.Method;):
1.获取类里所有方法:getDeclaredMethods();(返回数组)
2.通过方法名获取方法:getDeclaredMethod(方法名);
3.获取所有公共类型方法:getMethods();(返回数组)
4.获取公共类型中指定的方法:getMethod(方法名);
5.运行指定的方法:方法对象.invoke(对象名, 参数);
第一个为对象名,第二个为参数名,如果没有参数可以不用输入,允许暴力反射。
三、构造方法(返回Constructor类型,需要import java.lang.reflect.Constructor;):
1.获取类里所有构造方法:getDeclaredConstructors();(返回数组)
2.获取指定的无参构造方法:getDeclaredConstructor();
3.获取指定的带有参数的构造方法(举例:带有String和int类型的两个参数的构造方法):getDeclaredConstructor(String.class,int.class);
4.获取所有公共类型的构造方法:getConstructors();(返回数组)
5.获取公共类型中指定的一个方法(有参或无参):getConstructor();
6.执行无参构造方法:Object 新变量名 = 构造方法对象/类对象名.newInstance(); 相当于Object obj = new ();
7.执行有参的构造方法:Object 新变量名 = 构造方法对象.newInstance(参数);
需要注意我标蓝的地方,都需要传入一个类对象,那么如何获取类的实例对象呢?
四、获取类的实例对象:
1.通过构造方法获取:我们已经拿到指向我们需要反射的类的对象了,直接通过getDeclaredConstructor()方法获取一个构造方法(我这里是无参的),再执行构造方法中的newInstance()方法不就行了嘛。
代码:
Constructor con = class0.getConstructor();
Object object = con.newInstance();
注:我们提前无法预测给类起的名字是什么,所以需要用它们共同的父类Object对象来接受。
2.通过类对象获取:对类对象直接使用newInstance()方法,这样无需知道类名,就可以创建类的实例对象啦。
Object object = class0.newInstance();
有了类的实例对象,我们就可以使用上面标蓝的方法了,比如调用类中的方法等。
由此,反射的整个过程就实现完了如果有什么地方不明白的话,可以私信讨论( o >o)/。