Java的反射

Posted hackerstd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的反射相关的知识,希望对你有一定的参考价值。

Java的反射

Class类

在此之前,我们必须知道一句话,“java之中的一切都可以看作一个对象”。类是对象的抽象,而Class则是类的抽象,即任何一个类都是Class的实例对象。另外注意区别Class类和class关键字。Class类下面有很多方法,例如getConstructors() (包括其一些相似作用的方法这里以及后面不再列举,例如getDeclaredConstructor()等等),getFields(),getMethods();而它们会分别返回Constructor类型、Filed类型、Method类型的对象或者数组。Class类的构造方法是私有的,当一个类被加载时,JVM(java虚拟机)会自动通过Class的私有构造方法实例化一个Class对象,而我们没有办法通过Class的构造方法去实例化Class。而我们可以通过以下三种方法来获得Class的实例化对象;

  1. 类名.forName("具体路径 包名.类名");
  2. 类名.class;
  3. 对象.getClass();

获得了Class的实例化对象,我们就可以获得类的很多信息,例如权限修饰符,参数列表,类名,成员变量,成员方法等等。

问:什么是Java的反射?

答:大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。

Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。

问:Java反射有什么作用呢?

答:假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。大家都用过Jcreator和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。

Constructor类

每个Constructor对象都代表一个构造方法,利用Constructor对象可以操作相应的构造方法。下面时Constructor类常用的方法以及它的作用;

  1. isVarArgs(),查看该构造方法是否允许带有可变数量的参数,如果允许则返回true,否则返回false;方法原型:public boolean isVarArgs();
  2. getName(),以字符串的形式返回该构造方法的名字。方法原型:public String getName();
  3. getParameterTypes(),按照声明顺序以Class数组的形式获得该构造方法的各个参数类型。方法原型:public Class<?>[] getParameterTypes();
  4. getExceptionTypes(),以Class数组的形式获得该构造方法可能抛出的异常类型。方法原型:public Class<?>[] getExceptionTypes();
  5. newInstance(),通过该构造方法利用指定参数创建一个该类的对象,如果未设置参数则表示采用默认无参数的构造方法。函数原型:public T newInstance(Object... initargs),T表示返回的是一个Objec类的对象;

    注:Class.newInstance和Constructor.newInstance的有区别;Constructor的该方法能够使用有参数的构造方法,而Class下的该方法只能够使用无参默认构造方法。
  6. setAccessible(),如果该构造方法的权限为private,默认不允许通过反射利用newInstance()方法创建对象;如果先执行该方法并将入口参数设为true,则允许创建。方法原型:public void setAccessible(boolean flag);
    7.getModifiers(),获得可以解析出该构造方法所采用的修饰符的整数(利用Modifier类下的toString方法可以转换成字符串)。方法原型:public int getModifiers()。

下面介绍几个Modifier类常用的方法;

isPublic(int mod);

isProtected(int mod);

isPrivate(int mod);

isStatic(int mod);

isFinal(int mod);

toString(int mod);

这几种方法的作用都显而易见,不再赘述。

下面看代码示例:

//代码示例 Main类
import java.lang.reflect.*;

public class Main_01 {

	public static void main(String[] args) {

		Example_01 example = new Example_01("10", "20", "30");
		Class<? extends Example_01> exampleC = example.getClass();

		Constructor[] declaredConstructors = exampleC.getDeclaredConstructors();
		for (int i = 0; i < declaredConstructors.length; i++) {
			Constructor<?> constructor = declaredConstructors[i];
			System.out.println("查看是否允许带有可变数量的参数:" + constructor.isVarArgs());
			System.out.println("该构造方法的入口参数类型依次为:");
			Class[] parameterTypes = constructor.getParameterTypes();
			for (int j = 0; j < parameterTypes.length; j++) {
				System.out.println(" " + parameterTypes[j]);
			}
			System.out.println("该构造方法可能抛出的异常类型为:");
			Class[] exceptionTypes = constructor.getExceptionTypes();
			for (int j = 0; j < exceptionTypes.length; j++) {
				System.out.println(" " + exceptionTypes[j]);
			}
			Example_01 example2 = null;
			while (example2 == null) {
				try {
					if (i == 2)
						example2 = (Example_01) constructor.newInstance();
					else if (i == 1)
						example2 = (Example_01) constructor.newInstance("7", 5);
					else {
						Object[] parameters = new Object[] { new String[] {
								"100", "200", "300" } };
						example2 = (Example_01) constructor
								.newInstance(parameters);
					}
				} catch (Exception e) {
					System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");
					constructor.setAccessible(true);
				}
			}
			if(example2!=null){
			example2.print();
			System.out.println();
			
			}
		}

	}

}

//代码示例 Example类
public class Example_01 {
	String s;
	int i, i2, i3;
	private Example_01() {
	}
	protected Example_01(String s, int i) {
		this.s = s;
		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("s=" + s);
		System.out.println("i=" + i);
		System.out.println("i2=" + i2);
		System.out.println("i3=" + i3);
	}
}

这是运行结果截图:

技术图片

Filed类

每个Field对象都代表一个成员变量,利用Field对象可以操控相应的成员变量。下面是Field类的一些常用的方法以及作用;

  1. getName(),获得该成员变量的名称。方法原型:public String getName();
  2. getType(),获得表示该成员变量类型的Class对象。方法原型:public Class<?> getType();
  3. getModifiers(),获得可以解析出该成员变量所采用修饰符的整数。方法原型:public int getModifiers();
  4. get(Object obj),获得指定对象obj中成员变量的值,返回类型为Object型。方法原型:public Object get(Object obj);
  5. set(Object obj,Object value),将指定对象obj中成员变量的值设定为value;
    还有具体到各种类型的get set方法,不再一一介绍。另外说明:Object对象指的是已经实例化的反射对象。
    下面是代码示例:
//代码示例 Main类
import java.lang.reflect.*;
public class Main_02 {
	public static void main(String[] args) {
		Example_02 example = new Example_02();
		Class exampleC = example.getClass();
		// 获得所有成员变量
		Field[] declaredFields = exampleC.getDeclaredFields();
		for (int i = 0; i < declaredFields.length; i++) {
			Field field = declaredFields[i]; // 遍历成员变量
			// 获得成员变量名称
			System.out.println("名称为:" + field.getName());
			Class fieldType = field.getType(); // 获得成员变量类型
			System.out.println("类型为:" + fieldType);
			boolean isTurn = true;
			while (isTurn) {
				// 如果该成员变量的访问权限为private,则抛出异常,即不允许访问
				try {
					isTurn = false;
					// 获得成员变量值
					System.out.println("修改前的值为:" + field.get(example));
					// 判断成员变量的类型是否为int型
					if (fieldType.equals(int.class)) {
						System.out.println("利用方法setInt()修改成员变量的值");
						field.setInt(example, 168); // 为int型成员变量赋值
						// 判断成员变量的类型是否为float型
					} else if (fieldType.equals(float.class)) {
						System.out.println("利用方法setFloat()修改成员变量的值");
						// 为float型成员变量赋值
						field.setFloat(example, 99.9F);
						// 判断成员变量的类型是否为boolean型
					} else if (fieldType.equals(boolean.class)) {
						System.out.println("利用方法setBoolean()修改成员变量的值");
						// 为boolean型成员变量赋值
						field.setBoolean(example, true);
					} else {
						System.out.println("利用方法set()修改成员变量的值");
						// 可以为各种类型的成员变量赋值
						field.set(example, "MWQ");
					}
					// 获得成员变量值
					System.out.println("修改后的值为:" + field.get(example));
				} catch (Exception e) {
					System.out.println("在设置成员变量值时抛出异常,"
							+ "下面执行setAccessible()方法!");
					field.setAccessible(true); // 设置为允许访问
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}
//代码示例 Example类
public class Example_02 {
	int i;
	public float f;
	protected boolean b;
	private String s;
}

下面是运行结果:

技术图片

Method类

每个Method对象代表一个方法,利用Method对象可以操纵相应的方法。下面是Method类的一些常用的方法以及作用;

  1. getName(),获得该方法的名称。方法原型:public String getName();
  2. getParameterTypes(),按照声明的顺序以Class数组的形式获得该方法的各个参数的类型。方法原型:public Class<?>[] getParameterTypes();
  3. getReturnType(),以Class对象的形式返回该方法的返回类型。方法原型:public Class<?> getReturnType();
  4. getExceptionTypes(),以Class数组的形式获得该方法可能抛出的异常。方法原型:public Class<?> getExceptionTypes();
  5. invoke(Object obj,Object···args),利用指定参数args执行指定对象obj中的该方法,返回值类型为Object型。方法原型:public Object invoke(Object obj, Object... args);
  6. isVarArgs(),同Constructor;
  7. getModifiers(),同上。
    下面是代码示例:
//代码示例 Main类
import java.lang.reflect.*;

public class Main_03 {
	public static void main(String[] args) {
		Example_03 example = new Example_03();
		Class exampleC = example.getClass();
		// 获得所有方法
		Method[] declaredMethods = exampleC.getDeclaredMethods();
		for (int i = 0; i < declaredMethods.length; i++) {
			Method method = declaredMethods[i]; // 遍历方法
			System.out.println("名称为:" + method.getName()); // 获得方法名称
			System.out.println("是否允许带有可变数量的参数:" + method.isVarArgs());
			System.out.println("入口参数类型依次为:");
			// 获得所有参数类型
			Class[] parameterTypes = method.getParameterTypes();
			for (int j = 0; j < parameterTypes.length; j++) {
				System.out.println(" " + parameterTypes[j]);
			}
			// 获得方法返回值类型
			System.out.println("返回值类型为:" + method.getReturnType());
			System.out.println("可能抛出的异常类型有:");
			// 获得方法可能抛出的所有异常类型
			Class[] exceptionTypes = method.getExceptionTypes();
			for (int j = 0; j < exceptionTypes.length; j++) {
				System.out.println(" " + exceptionTypes[j]);
			}
			boolean isTurn = true;
			while (isTurn) {
				// 如果该方法的访问权限为private,则抛出异常,即不允许访问
				try {
					isTurn = false;
					if("staticMethod".equals(method.getName()))
						method.invoke(example); // 执行没有入口参数的方法
					else if("publicMethod".equals(method.getName()))
						System.out.println("返回值为:"
								+ method.invoke(example, 168)); // 执行方法
					else if("protectedMethod".equals(method.getName()))
						System.out.println("返回值为:"
								+ method.invoke(example, "7", 5)); // 执行方法
					else if("privateMethod".equals(method.getName())) {
						Object[] parameters = new Object[] { new String[] {
								"M", "W", "Q" } }; // 定义二维数组
						System.out.println("返回值为:"
								+ method.invoke(example, parameters));
					}
				} catch (Exception e) {
					System.out.println("在执行方法时抛出异常,"
							+ "下面执行setAccessible()方法!");
					method.setAccessible(true); // 设置为允许访问
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}

//代码示例 Example
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 s, int i)
			throws NumberFormatException {
		System.out.println("执行protectedMethod()方法");
		return Integer.valueOf(s) + i;
	}
	
	private String privateMethod(String... strings) {
		System.out.println("执行privateMethod()方法");
		StringBuffer stringBuffer = new StringBuffer();
		for (int i = 0; i < strings.length; i++) {
			stringBuffer.append(strings[i]);
		}
		return stringBuffer.toString();
	}
}

下面是运行结果图:

技术图片

未完待更……2020-4-2

以上是关于Java的反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

OpenGL片段着色器不照亮场景

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?