Javaの反射机制

一、反射的概念

1.概念

我们知道,一般情况下,要创建一个类的实例对象,可以使用new关键字:

包名.类名 对象名 = new 包名.类名(<构造函数实参>);

但有时,程序处于运行期,而我们不知道已经实例化的对象属于哪个类,也不知道这个类有哪些方法和属性。
反射机制就是在程序运行期时获取程序中对象所属的类与相关信息,并能调用它的方法和属性的一种动态机制
反射顾名思义,是从实例对象获得类的信息,与实例化对象的步骤恰好相反:

一般 类的实例化
反射 反射

2.功能

Java反射机制主要提供以下功能:

  1. 运行时判断任意一个对象所属的类
  2. 运行时构造任意一个类的对象
  3. 运行时判断任意一个类所具有的成员变量和方法
  4. 运行时调用任意一个对象的方法
  5. 生成动态代理

3.优缺点

优点:
● 在运行时动态获取类的实例,提高灵活性扩展性
● 与动态编译结合,实现强大的功能;
● 方便地创建灵活的代码,可以在运行时再装配,无需进行源代码地链接。
缺点:
● 反射会消耗一定的系统资源
● 反射调用方法可以忽略权限检查破坏类的封装性

二、Java反射机制API

1.Class类

Class类是反射机制的核心类。Class类的一个实例表示Java中一种数据类型的实体,包括类、接口、枚举、注解、数组、基本数据类型和 void。Class类没有公用的构造方法,实例是JVM在类加载时自动创建的。
每一个类或接口,都有一个静态变量class,另外每一个对象都有一个继承自Object类getClass()方法,可以获取Class实例:

Class class1 = A.class;
Class class2 = new A().getClass();
//class1和class2均为class test.A

Class类提供了很多方法用于获得类的相关信息,一些用于判断Class实例的性质,一些用于获取Class实例的方法和属性等信息:

Class class1 = A.class;
class1.isPrimitive();		//判断是否是基本类型
class1.getName();			//获取类名称,包括包名和类名
class1.getSuperclass();		//获取父类Class实例
class1.getConstructors();	//获取所有public构造方法
class1.getMethods();		//获取所有public方法
class1.getFields();			//获取所有public成员变量	

具体见文档:Java13文档-Class类

2.java.lang.reflect包

java.lang.reflect包中提供了反射机制用到的类,如下:

类名 说明
Constructor 提供类的构造方法信息
Field 提供类的成员变量信息
Method 提供类的成员方法信息
Array 提供动态创建和访问数组的方法
Modifier 提供类和成员的访问修饰符信息

下面将介绍这些类的简单使用,具体的方法和使用见文档:Java13文档-reflect包

Class<?> class1 = A.class;
Method[] methods = class1.getMethods();
for(Method m : methods) {
	System.out.print(m.getName() + "\t");
	System.out.print(Modifier.toString(m.getModifiers()) + "\n");
}
/*
Output:
wait		public final native
wait		public final
wait		public final
equals		public
toString	public
hashCode	public native
getClass	public final native
notify		public final native
notifyAll	public final native
*/

三、通过反射访问类

为方便讨论,下面创建一个Computer类,用于测试反射访问类的方法。

class Computer {
	String typeCPU;
	private String ipAdress;
	protected int wordLength;
	public double batteryCapacity;

	private Computer() {
	}

	public Computer(String typeCPU, String ipAdress, int wordLength, double batteryCapacity) {
		this.typeCPU = typeCPU;
		this.ipAdress = ipAdress;
		this.wordLength = wordLength;
		this.batteryCapacity = batteryCapacity;
	}

	public Computer(String... args) {
		typeCPU = args[0];
		ipAdress = args[1];
		wordLength = Integer.valueOf(args[2]);
		batteryCapacity = Double.valueOf(args[3]);
	}

	void print() {
		System.out.printf(
		"I'm a computer.\nMy CPU is %s.\nMy ipAddress is %s.\nMy wordLength is %d.\nMy battery capacity is %f.\n",
		typeCPU, ipAdress, wordLength, batteryCapacity);
	}
	
	protected boolean changeIP(String newIP) {
		if(newIP.length() == 0)	
			return false;
		this.ipAdress = newIP;
		return true;
	}
	
	private void batteryLoss() {
		batteryCapacity -= 10;
	}
	
	public int getWordLength() {
		return wordLength;
	}
}

1.访问构造方法

动态访问构造方法首先需要创建一个Constructor对象,方法有:
● getConstructors():获取所有public构造方法
● getConstructor(Class<?>…parameterTypes):根据参数表获取一个public构造方法
● getDeclaredConstructors():获取所有构造方法
● getDeclaredConstructor(Class<?>…parameterTypes):根据参数表获取一个构造方法

在获取Constructor对象后,可以使用提供的方法访问构造方法的信息。

Constructor<?>[] constructors = Computer.class.getDeclaredConstructors();
int i = 1;
for (Constructor<?> c : constructors) {
	System.out.println("Constructor" + i++ + ":");
	System.out.println("参数是否可变: " + c.isVarArgs()); // isVarArgs() 判断参数是否可变
	System.out.print("参数依次为:");
	Class<?>[] argsClasses = c.getParameterTypes(); // getParameterTypes() 获取参数表
	for (Class<?> clz : argsClasses)
		System.out.print(clz.getName() + "  ");
	System.out.println();
	Computer computer = null;
	int trytime = 1;
	while (computer == null) {
		try {
			// newInstance(initargs) 使用该构造函数实例化
			if(trytime == 1)
				computer = (Computer) c.newInstance("Core I7", "0.0.0.0", 64, 100);
			else if(trytime == 2)
				//可变参数需要嵌套两层传递参数
				computer = (Computer) c.newInstance(new Object[] {new String[] {"Core I5", "192.168.1.1", "32", "98" , "15"}});
			else
				computer = (Computer) c.newInstance();
		}
		catch (IllegalAccessException e) {		//无访问权限,重新设置
			System.out.println("无访问权限,重新设置!");
			c.setAccessible(true); // 设置函数访问权限,对于private默认为false,必须设置为true才能访问
			trytime++;
		}
		catch (IllegalArgumentException e) {	//参数表不对,更换newInstance方法调用
			System.out.println("参数表不正确!");
			trytime++;
		}
		catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
	computer.print();
}
/*
Output:
Constructor1:
参数是否可变: true
参数依次为:[Ljava.lang.String;  
参数表不正确!
I'm a computer.
My CPU is Core I5.
My ipAddress is 192.168.1.1.
My wordLength is 32.
My battery capacity is 98.
Constructor2:
参数是否可变: false
参数依次为:java.lang.String  java.lang.String  int  double  
I'm a computer.
My CPU is Core I7.
My ipAddress is 0.0.0.0.
My wordLength is 64.
My battery capacity is 100.
Constructor3:
参数是否可变: false
参数依次为:
无访问权限,重新设置!
参数表不正确!
I'm a computer.
My CPU is null.
My ipAddress is null.
My wordLength is 0.
My battery capacity is 0.
*/

2.访问方法

动态访问成员方法首先需要创建一个Method对象,方法有:
● getMethods():获取所有public方法
● getMethods(String name,Class<?> …parameterTypes):根据方法名和参数表获取一个public方法
● getDeclaredMethods():获取所有方法
● getDeclaredMethods(String name,Class<?>…parameterTypes):根据方法名和参数表获取一个方法

在获取Method对象后,可以使用提供的方法访问成员方法的信息。
获取参数表等使用的方法与构造方法一致,这里不再演示。

Computer computer = new Computer("Core I7", "192.168.1.101", 64, 98);
Method[] methods = computer.getClass().getDeclaredMethods();
for (Method method : methods) {
	System.out.println(method.getName() + ":");
	System.out.printf("方法的返回值类型是%s\n", method.getReturnType().getName());	//getReturnType() 获取返回类型
	String methodName = method.getName();											
	try {
		switch (methodName) {	//invoke(obj,args) 对obj对象执行该方法,参数表为args
		case "changeIP" -> {
			method.invoke(computer, "192.168.1.102");
			computer.print();
		}
		case "batteryLoss" -> {
			method.setAccessible(true);
			method.invoke(computer);
			computer.print();
		}
		case "getWordLength" -> System.out.println("字长为"+method.invoke(computer));
		}
	} catch (Exception e) {
		System.out.println(e.getMessage());
	}
	System.out.println("----------------------------");
}
/*
Output:
print:
方法的返回值类型是void
----------------------------
getWordLength:
方法的返回值类型是int
字长为64
----------------------------
batteryLoss:
方法的返回值类型是void
I'm a computer.
My CPU is Core I7.
My ipAddress is 192.168.1.101.
My wordLength is 64.
My battery capacity is 88.
----------------------------
changeIP:
方法的返回值类型是boolean
I'm a computer.
My CPU is Core I7.
My ipAddress is 192.168.1.102.
My wordLength is 64.
My battery capacity is 88.
----------------------------
*/

3.访问成员变量

动态访问成员首先需要创建一个Field对象,方法有:
● getFields():获取所有public变量
● getField(String name):根据变量名获取一个public变量
● getDeclaredFields():获取所有变量
● getDeclaredField(String name):根据变量名获取一个变量

在获取Field对象后,可以使用提供的方法访问成员变量的信息。

Computer computer = new Computer("Core I7", "192.168.1.101", 64, 98);
Field[] fields = computer.getClass().getDeclaredFields();
for(Field f : fields) {
	System.out.println(f.getName() + ":");
	if(f.getName().equals("ipAdress"))
		f.setAccessible(true);		//设置private变量的访问权限
	System.out.println("类型:" + f.getType().getName());		//getType() 获取变量类型
	try {
		System.out.println("原先值:" + f.get(computer));		//get(obj)	获取obj中f的值
		switch (f.getName()) {		//set(obj,value) 将obj中f的值修改为value
		case "typeCPU" -> f.set(computer, "Core I5");
		case "ipAdress" -> f.set(computer, "192.168.1.105");
		case "wordLength" -> f.set(computer, 32);
		case "batteryCapacity" -> f.set(computer, 80);
		}
		System.out.println("修改后:" + f.get(computer));
	}
	catch (Exception e) {
		System.out.println(e.getMessage());
	}
}
computer.print();
/*
Output:
typeCPU:
类型:java.lang.String
原先值:Core I7
修改后:Core I5
ipAdress:
类型:java.lang.String
原先值:192.168.1.101
修改后:192.168.1.105
wordLength:
类型:int
原先值:64
修改后:32
batteryCapacity:
类型:double
原先值:98.0
修改后:80.0
I'm a computer.
My CPU is Core I5.
My ipAddress is 192.168.1.105.
My wordLength is 32.
My battery capacity is 80.
*/
原创文章 49 获赞 5 访问量 2997

猜你喜欢

转载自blog.csdn.net/weixin_44712386/article/details/105456139