JAVA——从基础学起(十六)反射和注解(上)

16 (上)反射

java提供的反射机制可以实现在程序运行时对用户输入的信息进行验证,还可以逆向控制程序的执行过程。

16.1.1 Class类和Java反射

通过Java反射机制,可以在程序中访问已经装载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。java.lang.reflect包中提供了对反射功能的支持。
由于所有的类都继承了Object类,故都实现了getClass()方法。getClass()方法返回了一个类型为Class的对象,如Class classTest = test.getClass();。使用Class类的对象可以对test对象中的各种描述信息进行访问。下面是Class类中的常用方法:

组成部分 访问方法 返回值类型 说明
包路径 getPackage() Package对象 返回该类的存放路径
类名称 getName() String对象 获得该类的名称
继承类 getSuperclass() Class对象 获得该类继承的类
实现接口 getInterfaces() Class型数组 获得该类实现的所有接口
构造方法 getConstructors() Constructor型数组 获得所有权限为public的构造方法
getConstrutor(Class…parameterTypes) Constructor对象 获得指定构造方法
getDeclaredConstructors() Constructor型数组 获得所有构造方法,按声明顺序返回
getDeclaredConstructor(Class…parameterTypes) Constructor对象 获得指定的构造方法
方法 getMethods() Method数组 获得所有权限为public的方法
getMethod(String name , Class…parameterTypes) Method对象 获得权限为public的指定方法
getDeclaredMethods() Method数组 获得所有的方法,按声明顺序返回
getDeclaredMethod(String name , Class … parameterTypes) Method对象 获得指定方法
成员变量 getFields() Field数组 获取所有权限为public的成员变量
getField(String name) Field对象 获取指定public成员变量
getDeclaredFields() Field数组 获取所有成员变量,按声明顺序返回
getDeclaredField(String name) Field对象 获取指定成员变量
内部类 getClasses() Class数组 获取所有权限为public的内部类
getDeclaredClasses() Class数组 获取所有内部类
内部类的声明类 getDeclaringClass() Class对象 如果该类为内部类,则返回他的成员类,否则返回null

注意:在使用getFields()方法和getMethods()方法时,将会包含从父类中继承得到的成员变量和成员方法;当使用getDeclaredFields()和getDecaleredMethods()方法时只是获得该类中定义的所有成员变量与方法。

16.1.1 访问构造方法

由上可知,可以通过一些Class类中的方法访问特定对象的构造方法。这些方法将会返回Constructor对象或Constructor数组,每一个对象都对应着一个特定的构造方法。
getConstructors()
getConstrutor(Class…parameterTypes)
getDeclaredConstructors()
getDeclaredConstructor(Class...parameterTypes)
如访问一个入口参数类型依次是String类和int类的构造方法时,需要使用以下形式:
classObject.getDeclaredConstructor(String.class , int.class)

Constructor类中定义的方法有:

方法 说明
isVarArgs() 是否允许带有可变数量的参数,允许则返回true
getParameterTypes() 返回Class型数组,依次获得该构造函数的参数类型
getExceptionTypes() 返回Class数组,获取该构造方法可能抛出异常的类型
newInstance(Object…initargs) 通过该构造方法使用指定参数创建一个该类的对象,如果未设置参数则表示使用默认构造方法
setAccessible(boolean flag) 如果该构造方法的权限为private,默认为不允许通过反射利用newInstance方法创建对象,如果先执行该方法,并将入口参数设置为true,则可以进行对象创建
getModifiers() 获得可以解析出该构造方法所采用修饰符的整数

通过java.lang.reflect.Modifier类可以解析出getModifier()方法返回值所表示的修饰符信息,该类中定义的方法如下:

静态方法 说明
isPublic(int mod) 查看是否由public修饰
isPrivate(int mod) 查看是否由private修饰
isProtected(int mod) 查看是否由protected修饰
isStatic(int mod) 查看是否由Static修饰
isFinal(int mod) 查看是否由final修饰
toString(int mod) 以字符串形式返回所有修饰符

下面使用实例对类的构造方法进行访问:

package com.mw01;
//创建基本类作为访问构造函数的实例类
public class Example_01 {
	
	String string ;
	int i,i2,i3;
	
	private Example_01() {}

	protected Example_01(String string, int i) {
		super();
		this.string = string;
		this.i = i;
	}
	
	public Example_01(String ...strings) throws NumberFormatException{
		if (0 < strings.length) {
			i = Integer.valueOf(strings[0]);
		}
		if (1 < strings.length) {
			i2 = Integer.valueOf(strings[1]);
		}
		if (2 < strings.length) {
			i3 = Integer.valueOf(strings[2]);
		}
	}
	
	public void print(){
		System.out.println("string = " + string);
		System.out.println("i = " + i);
		System.out.println("i2 = " + i2);
		System.out.println("i3 = " + i3);
	}
}
package com.mw01;
import java.lang.reflect.Constructor;
public class Main_01 {
	public static void main(String[] args) {
		Example_01 example = new Example_01("example" , 1);
		//创建实例类的对象作为研究对象
		Class<? extends Example_01> exampleC = example.getClass();
		Constructor[] declaredConstructors = exampleC.getDeclaredConstructors();
		
		for (int i = 0; i < declaredConstructors.length; i++) {
			System.out.println("查看是否带有可变数量的参数:" + declaredConstructors[i].isVarArgs());
			System.out.println("该构造函数的入口参数类型分别为:");
			Class[] parameterTypes = declaredConstructors[i].getParameterTypes();
			for (int j = 0; j < parameterTypes.length; j++) {
				System.out.println(parameterTypes[j]);
			}
			System.out.println("该构造方法可能抛出的异常类型为:");
			Class[] exceptionsC = declaredConstructors[i].getExceptionTypes();
			for (int j = 0; j < exceptionsC.length; j++) {
				System.out.println(exceptionsC[j]);
			}
			
			Example_01 example2 = null;      //创建对象引用用于后续newInstance方法使用
			while (example2 == null) {
				try {
					if (i == 2) {
						example2 = (Example_01) declaredConstructors[i].newInstance();
					}
					if (i == 1) {
						example2 = (Example_01) declaredConstructors[i].newInstance("7" , 5);
					}
					else {
						Object[] parameters = new Object[] {new String[] {"12","23","45"}};
						example2 = (Example_01) declaredConstructors[i].newInstance(parameters);
					}
				} catch (Exception e) {
					System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法。");
					declaredConstructors[i].setAccessible(true);
				}
			}
			if (example2 != null) {
				example2.print();
				System.out.println();
			}
		}
	}
}

运行结果为:

查看是否带有可变数量的参数:true
该构造函数的入口参数类型分别为:
class [Ljava.lang.String;
该构造方法可能抛出的异常类型为:
class java.lang.NumberFormatException
string = null
i = 12
i2 = 23
i3 = 45

查看是否带有可变数量的参数:false
该构造函数的入口参数类型分别为:
class java.lang.String
int
该构造方法可能抛出的异常类型为:
string = 7
i = 5
i2 = 0
i3 = 0

查看是否带有可变数量的参数:false
该构造函数的入口参数类型分别为:
该构造方法可能抛出的异常类型为:
在创建对象时抛出异常,下面执行setAccessible()方法。
在创建对象时抛出异常,下面执行setAccessible()方法。
string = null
i = 0
i2 = 0
i3 = 0

16.1.2 访问成员变量

在使用方法访问类的成员变量时,将返回Field类型的对象或对象数组,每个Field对象代表着一个成员变量,使用Field对象可以对成员变量进行操作。
getFields()
getField(String name)
getDeclaredFields()
getDeclaredField(String name)
访问指定的成员变量时,可以通过该成员变量的名称来完成,如:objectClass.getDeclaredField("name");
Field类中提供了一些常用的方法:

方法 说明
getName() 获得该成员变量的名称
getType() 获得表示该成员变量类型的Class对象
get(Object object) 获得指定对象object中成员变量的值,返回值为Object
set(Object object , Object value) 将指定对象object中的成员变量值设置为value
getInt(Object object) 获取object对象中类型为int的变量的值
setInt(Object object , int i) 将object对象中的int类型变量设置为i
getFloat(Object object) 获取object对象中类型为float的变量的值
setFloat(Object object , float f) 将object对象中的float类型变量设置为f
getBoolean(Object object) 获取object对象中类型为boolean的变量的值
setFloat(Object object , boolean b) 将object对象中的boolean类型变量设置为b
setAccessible(boolean flag) 此方法可以设置是否忽略权限限制直接访问private等私有成员变量
getModifiers() 获得可以解析出该成员变量所采用修饰符的整数

下面使用实例来进行介绍:

package com.mw02;

public class Example_02 {
	int i ;
	public float f;
	protected boolean b ;
	private String s;
}
package com.mw02;

import java.lang.reflect.*;

public class Main {
	public static void main(String[] args) {
		Example_02 example = new Example_02();
		
		Class exampleC = example.getClass();
		Field[] fields = exampleC.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			System.out.println("变量名称为:" + fields[i].getName());
			
			Class fieldType = fields[i].getType();
			System.out.println("变量类型为:" + fieldType);
			
			boolean isTurn = true;
			while (isTurn) {
				try {
					isTurn = false;
					System.out.println("修改前的变量值为:" + fields[i].get(example));
					if (fields[i].getType().equals(int.class)) {
						System.out.println("使用setInt方法修改变量的值。");
						fields[i].setInt(example, 168);
					}else if (fields[i].getType().equals(float.class)) {
						System.out.println("使用setFloat方法修改变量的值。");
						fields[i].setFloat(example, 99.99F);
					}else if (fields[i].getType().equals(boolean.class)) {
						System.out.println("使用setBoolean方法修改变量的值。");
						fields[i].setBoolean(example, true);
					}else if (fields[i].getType().equals(String.class)) {
						System.out.println("使用set方法改变变量的值。");
						fields[i].set(example, "MWQ");
					}
					System.out.println("修改后的变量值为:" + fields[i].get(example));
				} catch (Exception e) {
					System.out.println("在设置成员变量值时出现异常,下面执行setAccessible()方法!");
					fields[i].setAccessible(true);
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}

运行结果为:

变量名称为:i
变量类型为:int
修改前的变量值为:0
使用setInt方法修改变量的值。
修改后的变量值为:168

变量名称为:f
变量类型为:float
修改前的变量值为:0.0
使用setFloat方法修改变量的值。
修改后的变量值为:99.99

变量名称为:b
变量类型为:boolean
修改前的变量值为:false
使用setBoolean方法修改变量的值。
修改后的变量值为:true

变量名称为:s
变量类型为:class java.lang.String
在设置成员变量值时出现异常,下面执行setAccessible()方法!
修改前的变量值为:null
使用set方法改变变量的值。
修改后的变量值为:MWQ

16.1.3 访问方法

通过一系列方法访问类中的成员方法时,将会返回Method对象或者数组。每个Method对象代表一个方法,利用Method对象可以操纵相应的方法。
getMethods()
getMethod(String name , Class ...parameterTypes)
getDeclaredMethods()
getDeclaredMethod(String name , Class...parameterTypes)
如果需要访问特定的方法,需要根据该方法的名称和入口参数的类型来访问。例如访问一个名称为print,参数类型依次为String和int的 方法,可以通过以下方式实现:
objectClass.getDeclaredMethod("print" , String.class , int.class);或者objectClass.getDeclaredMethod("print" , new Class[]{String.class , int.class});
Method类提供的常用方法有:

方法 说明
getName() 获得该成员方法的名称
getParameterTypes() 按照声明顺序返回该成员方法参数类型的Class数组
getReturnType() 返回表示该成员方法返回值类型Class对象
getExceptionTypes() 以Class数组的方式返回该成员函数抛出的异常类型
invoke(Object object , Object…args) 使用指定参数执行指定对象中的该成员参数,返回值为Object类型
isVarArgs() 查看该构造方法是否允许带有可变数量的参数,允许则返回true
getModifiers() 返回可以解析出该方法修饰符的整数

下面使用实例来进行介绍:

package com.mw03;
//创建基本类并构建不同修饰符修饰的成员方法
public class Example_03 {
	static void staticMethod(){
		System.out.println("执行staticMethod()方法。");
	}
	
	public int publicMethod(int i) {
		System.out.println("执行publicMethod()方法。");
		return i*100;
	}

	protected int protectedMethod(String string , int i)throws NumberFormatException {
		System.out.println("执行protectedMethod()方法。");
		return Integer.valueOf(string) + i;
	}
	private String privateMethod(String...strings) {
		System.out.println("执行privateMethod()方法。");
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < strings.length; i++) {
			sb.append(strings[i]);
		}
		return sb.toString();
	}
}
package com.mw03;
import java.lang.reflect.*;

public class Main {
	public static void main(String[] args) {
		Example_03 example = new Example_03();
		
		Class exampleClass = example.getClass();
		Method[] methods = exampleClass.getDeclaredMethods();
		
		for (int i = 0; i < methods.length; i++) {
			System.out.println("名称为:" + methods[i].getName());
			System.out.println("是否允许带有可变数量的参数:" + methods[i].isVarArgs());
			System.out.println("入口参数类型依次为:");
			Class[] parameterTypes = methods[i].getParameterTypes();
			for (int j = 0; j < parameterTypes.length; j++) {
				System.out.println(" " + parameterTypes[j]);
			}
			System.out.println("返回值类型为:" + methods[i].getReturnType());
			System.out.println("可能抛出的异常类型有:");
			Class[] exceptionTypes = methods[i].getExceptionTypes();
			for (int j = 0; j < exceptionTypes.length; j++) {
				System.out.println(exceptionTypes[j]);
			}
			
			boolean isTurn = true ;
			while (isTurn) {
				try {
					isTurn = false;
					if ("staticMethod".equals(methods[i].getName())) {
						methods[i].invoke(example);
					}else if ("protectedMethod".equals(methods[i].getName())) {
						System.out.println("返回值为:" + methods[i].invoke(example, "88",5));
					}else if ("publicMethod".equals(methods[i].getName())) {
						System.out.println("返回值为:" +methods[i].invoke(example, 168));
					}else if ("privateMethod".equals(methods[i].getName())) {
						Object[] parameters = new Object[]{new String[]{"125","whud","45"}};
						System.out.println("返回值为:" + methods[i].invoke(example, parameters));
					}
				} catch (Exception e) {
					System.out.println("在执行方法时抛出异常,下面执行setAccessible方法:");
					methods[i].setAccessible(true);
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}

运行结果为:

名称为:publicMethod
是否允许带有可变数量的参数:false
入口参数类型依次为:
 int
返回值类型为:int
可能抛出的异常类型有:
执行publicMethod()方法。
返回值为:16800

名称为:staticMethod
是否允许带有可变数量的参数:false
入口参数类型依次为:
返回值类型为:void
可能抛出的异常类型有:
执行staticMethod()方法。

名称为:protectedMethod
是否允许带有可变数量的参数:false
入口参数类型依次为:
 class java.lang.String
 int
返回值类型为:int
可能抛出的异常类型有:
class java.lang.NumberFormatException
执行protectedMethod()方法。
返回值为:93

名称为:privateMethod
是否允许带有可变数量的参数:true
入口参数类型依次为:
 class [Ljava.lang.String;
返回值类型为:class java.lang.String
可能抛出的异常类型有:
在执行方法时抛出异常,下面执行setAccessible方法:
执行privateMethod()方法。
返回值为:125whud45


通过反射机制我们可以使用实例得到Class对象,并通过Class类中的一系列方法访问该实例中的构造方法,成员变量,成员方法等。为我们验证,控制实例提供了很大的方便。

你可能感兴趣的:(Java,java)