Java反射:Class类的使用

67 篇文章 16 订阅

通过Java反射机制,可以在程序中访问已经转载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。Java反射机制的功能十分强大,在java.lang.reflect包中提供了对该功能的支持。

所有Java类均继承了Object类,在Object类中定义了一个getClass()方法,该方法返回一个类型为Class的对象。

JTextField textField = new JTextField(); // 创建JTextField对象
Class textFieldC = textField.getClass(); // 获取Class对象

利用Class类的对象textFieldC,可以访问用来返回该对象的textField对象的描述信息。

获取Class对象的三种方式:

(1)通过Object类继承来的getClass()方法。

(2)通过类的“静态”的class属性。

(3)通过Class类的静态方法:Class.forName(String  className)方法。

示例:使用三种方式获取Class对象。

import javax.swing.JTextField;

/**
 * 使用三种方式获取Class对象。
 * 
 * @author pan_junbiao
 *
 */
public class Test
{
	public static void main(String[] args) throws NoSuchMethodException, SecurityException
	{
		// 第一种方式获取Class对象
		JTextField textField = new JTextField(); // 创建JTextField对象
		Class myClass1 = textField.getClass(); // 获取Class对象

		// 第二种方式获取Class对象
		Class myClass2 = JTextField.class;

		// 第三种方式获取Class对象
		try
		{
			// 注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
			Class myClass3 = Class.forName("javax.swing.JTextField");
		} catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
	}
}

通过反射可访问的主要描述信息:

组成部分访问方法返回值类型说明
包路径getPackage() Package对象获取该类的存放路径
类名称getName() String对象获取该类的名称
继承类getSuperclass()Class对象获取该类继承的类
实现接口getInterfaces()Class型数组获取该类实现的所有接口
构造方法getConstructors()Constructor型数组获取所有权限为public的构造方法
getConstructor(Class<?>... parameterTypes)Constructor对象获取权限为public的指定构造方法
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()方法依次获得权限为public的成员变量和方法时,将包含从超类中继承到的成员变量和方法;而通过getDeclaredFields()和getDeclaredMethods()只是获得在本类中定义的所有成员变量和方法。

1、访问构造方法

在通过下列一组方法访问构造方法时,将返回Constructor类型的对象或数组。每个Constructor对象代表一个构造方法,利用Constructor对象可以操纵相应的构造方法。

方法说明
getConstructors()获取所有权限为public的构造方法。
getConstructor(Class<?>... parameterTypes)获取权限为public的指定构造方法。
getDeclaredConstructors()获取所有构造方法,按照声明顺序返回。
getDeclaredConstructor(Class<?>... parameterTypes)获取指定构造方法。

如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如,访问一个入口参数类型依次为String和int型的构造方法,通过下面两种方式均可实现。

Constructor cons1 = objectClass.getDeclaredConstructor(String.class, int.class);
Constructor cons2 = objectClass.getDeclaredConstructor(new Class[] { String.class, int.class });

Constructor类的常用方法:

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

通过java.lang.reflect.Modifier类可以解析出getModifiers()方法的返回值所表示的修饰符信息,在该类中提供了一系列用来解析的静态方法,即可以查看是否被指定的修饰符修饰,还可以以字符串的形式获得所有修饰符。

Modifier类中的常用解析方法:

静态方法说明
isPublic(int mod)查看是否被public修饰符修饰,如果是则返回true,否则返回false
isProtected(int mod)查看是否被Protected修饰符修饰,如果是则返回true,否则返回false
isPrivate(int mod)查看是否被Private修饰符修饰,如果是则返回true,否则返回false
isStatic(int mod)查看是否被static修饰符修饰,如果是则返回true,否则返回false
isFinal(int mod)查看是否被final修饰符修饰,如果是则返回true,否则返回false
toString(int mod)以字符串的形式返回所有修饰符

例如,判断对象constructor所代表的构造方法是否被private修饰,以及以字符串形式获得该构造方法的所有修饰符的典型代码如下:

int modifiers = constructor.getModifiers();
boolean isEmbellishByPrivate = Modifier.isPrivate(modifiers);
String embellishment = Modifier.toString(modifiers);

示例:访问构造方法。

首先创建一个Example_01类,在该类中声明一个String类型成员变量和3个int类型成员变量,并提供3个构造方法。

/**
 * Example_01类
 * 
 * @author pan_junbiao
 *
 */
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);
	}
}

然后编写测试类Main_01,在该类中通过反射访问Example_01类中的所有构造方法,并将该构造方法是否允许带有可变参数的参数、入口参数类型和可能抛出的异常类型信息输出到控制台。

import java.lang.reflect.Constructor;

/**
 * 通过反射访问Example_01类中的所有构造方法 <br/>
 * 并将该构造方法是否允许带有可变参数的参数 <br/>
 * 入口参数类型和可能抛出的异常类型信息输出到控制台
 * 
 * @author pan_junbiao
 *
 */
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("pan_junbiao的博客", 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();
			}
		}
	}
}

执行结果:

 

2、访问成员变量

在通过下列一组方法访问成员变量时,将返回Field类型的对象或数组。每个Field对象代表一个成员变量,利用Field对象可以操纵相应的成员变量。

方法说明
getFields()获取所有权限为public的成员变量。
getField(String name)获取权限为public的指定成员变量。
getDeclaredFields()获取所有成员变量,按照声明顺序返回。
getDeclaredField(String name)获取指定成员变量。

Field类的常用方法:

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

示例:访问成员变量

首先创建一个Example_02类,在该类中声明int、float、boolean和String型的成员变量,并将它们设置为不同的访问权限。

/**
 * Example_02类
 * 
 * @author pan_junbiao
 *
 */
public class Example_02
{
	int i1;
	int i2;
	public float f;
	protected boolean b;
	private String s;
}

然后通过反射访问Example_02类中的所有成员变量,将成员变量的名称和类型信息输出到控制台,并分别将各个成员变量在修改前后的值输出到控制台。

import java.lang.reflect.Field;

/**
 * 通过反射访问Example_02类中的所有成员变量 <br/>
 * 将成员变量的名称和类型信息输出到控制台 <br/>
 * 并分别将各个成员变量在修改前后的值输出到控制台
 * 
 * @author pan_junbiao
 *
 */
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, "欢迎访问 pan_junbiao的博客");
					}
					// 获得成员变量值
					System.out.println("修改后的值为:" + field.get(example));
				} catch (Exception e)
				{
					System.out.println("在设置成员变量值时抛出异常," + "下面执行setAccessible()方法!");
					field.setAccessible(true); // 设置为允许访问
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}

执行结果:

 

3、访问方法

在通过下列一组方法访问方法时,将返回Method类型的对象或数组。每个Method对象代表一个方法,利用Method对象可以操纵相应的方法。

方法说明
getMethods()获取所有权限为public的方法。
getMethod(String name, Class<?>... parameterTypes)获取权限为public的指定方法。
getDeclaredMethods()获取所以方法,按照声明顺序返回。
getDeclaredMethod(String name, Class<?>... parameterTypes)获取指定方法。

如果是访问指定的方法,需要根据该方法的名称和入口参数的类型来访问。例如,访问一个名称为print、入口参数类型依次为String和int型的方法,通过下面两种方法均可实现:

Method m1 = objectClass.getDeclaredMethod("print", String.class, int.class);
Method m2 = objectClass.getDeclaredMethod("print", new Class[] { String.class, int.class });

Method类的常用方法:

方法说明
getName()获取该方法的名称
getParameterTypes()按照声明顺序以Class数组的形式获取该方法的各个参数的类型
getReturnType()以Class对象的形式获取该方法的返回值的类型
getExceptionTypes()以Class数组的形式获取该方法可能抛出的异常类型
invoke(Object obj, Object... args)利用指定参数args执行指定对象obj中的该方法,返回值为Object型
isVarArgs()查看该构造方法是否允许带有可变数量的参数,如果允许则返回true,否则返回false
getModifiers()获取可以解析出该方法所采用修饰符的整型

示例:访问方法。

首先创建一个Example_03类,并编写4个典型的方法。

/**
 * Example_03类
 * 
 * @author pan_junbiao
 *
 */
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();
	}
}

然后通过反射访问Example_03类中的所有方法,将各个方法的名称、入口参数类型、返回值类型等信息输出到控制台,并执行部分方法。

import java.lang.reflect.Method;

/**
 * 通过反射访问Example_03类中的所有方法 <br/>
 * 将各个方法的名称、入口参数类型、返回值类型等信息输出到控制台 <br/>
 * 并执行部分方法
 * 
 * @author pan_junbiao
 *
 */
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[] { "您好!", "欢迎访问 ", "pan_junbiao的博客" } }; // 定义二维数组
						System.out.println("返回值为:" + method.invoke(example, parameters));
					}
				} catch (Exception e)
				{
					System.out.println("在执行方法时抛出异常," + "下面执行setAccessible()方法!");
					method.setAccessible(true); // 设置为允许访问
					isTurn = true;
				}
			}
			System.out.println();
		}
	}
}

执行结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan_junbiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值