什么是反射机制
反射机制是Java 5版本提供的高级新特性,这种机制允许Java程序在运行状态中,对任意一个类都能知道该类的所有属性和方法;对任意一个对象,都能调用该对象的属性和方法。这种动态获取信息及动态调用对象的属性和方法的功能称之为Java的反射机制。
在我们编写Java类文件时,真正运行的是编译之后的“.class”文件。而“.class”文件在运行时,被加载到内存后,都是一个Class类的对象。我们可以通过Java 5提供的反射机制获取到该类的构造器、方法及成员变量等。
简单来说,Java的反射机制中提供了Class类、Constructor、Method及Field等,Class类就是类的元神,Constructor就是构造器的元神,Method就是方法的元神,Field就是成员变量的元神。
反射中的Class类
Class类是Java 5提供一个新类型,就叫做类类型。如何可以获取一个Class类型的对象呢?传统方式就是利用new Class()方式,但Java 5提供不同的方式。参看以下代码:
public class Demo {
@Test
public void demo() throws ClassNotFoundException {
// 第一种方式:类名.class
Class c1 = int.class;
Class c2 = int[].class;
// 第二种方式:对象.getClass()
Class c3 = "hello".getClass();
// 第三种方式:Class.forName("类全名")
Class c4 = Class.forName("app.java.reflect.Demo");
}
}
通过以上代码我们可以了解,获取Class实例的方式有三种:
- 类名.class,例如:Class c1 = Object.class;
- 对象.getClass(),例如:Class c2 = “Hello”.getClass();
- Class.forName(“类全名”),例如:Class c1 = Class.forName(“app.java.reflect.Demo”);
获取到Class类实例之后,有什么具体用途呢?第一,我们可以通过Class类实例的getName()和getSimpleName()方法,获取到对应类的全类名或类名。
public class Demo {
@Test
public void demo() throws ClassNotFoundException {
Class c1 = "hello".getClass();
// java.lang.String
System.out.println(c1.getName());
Class c2 = int[].class;
// [I:"["表示数组,"I"表示int类型
System.out.println(c2.getName());
// int[]
System.out.println(c2.getSimpleName());
}
}
第二,我们可以通过Class类实例的getSuperclass()方法,获取到对应类的完整继承链关系。
public class Demo {
@Test
public void demo() throws ClassNotFoundException {
Class c = java.awt.Frame.class;
while (c.getSuperclass() != null) {
System.out.println(c.getSuperclass().getName());
c = c.getSuperclass();
}
}
}
第三,我们可以通过反射机制来创建对象。
public class Demo {
@Test
public void demo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String className = "app.java.reflect.User";
Class c = Class.forName(className);
User user = (User) c.newInstance();
user.setUsername("king");
System.out.println(user.getUsername());
}
}
class User{
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
private String password;
}
反射中的Constructor
传统Java方式获取类的实例,基本都是通过构造器来实现,反射机制中的Class类同样具有构造器。那反射机制中Class类的构造器如何获得呢?我们来看以下代码:
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
// 获取c类型中所有public构造器
Constructor[] constructors1 = c.getConstructors();
// 获取c类型中所有构造器
Constructor[] constructors2 = c.getDeclaredConstructors();
System.out.println(constructors1.length);
System.out.println(constructors2.length);
// 获取没有参数的public构造器
Constructor con1 = c.getConstructor();
// 获取参数类型依次为String.class,String.class的public构造器
Constructor con2 = c.getConstructor(String.class,String.class);
// 获取参数类型为String.class的构造器
Constructor con3 = c.getDeclaredConstructor(String.class);
}
}
通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的构造器。
- getConstructors()方法:获取Class类所有的public构造器,其中包含没有参数和有参数的。
- getDeclaredConstructors()方法:获取Class类所有的构造器,其中包含由public、private和protected修饰符的。
- getConstructor()方法:获取具体的public修饰构造器,参数表示是否具有参数的构造器。
- getDeclaredConstructor()方法:获取具体的构造器,其中包含由public、private和protected修饰符的。
我们得到Class类型的构造器之后,又会有什么样的用途呢?第一,可以通过构造器来创建实例对象。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
Constructor<User> constructor = c.getConstructor(String.class,String.class);
User user = constructor.newInstance("king","123");
System.out.println(user);
}
}
第二,我们可以构造器来打印指定类型的所有构造器,以及参数类型。
public class Demo {
@Test
public void demo() throws Exception{
Class<String> c = String.class;
Constructor[] constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.print(constructor.getDeclaringClass().getSimpleName() + "(");
Class[] classes = constructor.getParameterTypes();
for (int i = 0; i < classes.length; i++) {
System.out.print(classes[i].getSimpleName());
if (i < classes.length - 1) {
System.out.print(", ");
}
}
System.out.println(")");
}
}
}
反射中的Method
与Class类的构造器类似的就是方法,我们首先讨论如何获取方法。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
// 获取本类和父类中的所有public方法
Method[] ms1 = c.getMethods();
System.out.println(ms1.length);
// 获取本类中所有方法
Method[] ms2 = c.getDeclaredMethods();
System.out.println(ms2.length);
// 获取本类或父类中名称为setUsername,参数类型为String的public方法
Method m1 = c.getMethod("setUsername", String.class);
// 获取本类中声明的名称为toString的,没有参数的方法,它可以是任何访问级别,但它不能是父类中的方法
Method m2 = c.getDeclaredMethod("toString");
}
}
通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的方法。
- getMethods()方法:获取Class类及父类的所有public方法。
- getDeclaredMethods()方法:获取Class类的所有方法,其中包含由public、private修饰符的。
- getMethod()方法:获取Class类及父类中具体的public方法。
- getDeclaredMethod()方法:获取Class类中具体的方法,其中包含由public、private修饰符的。但不能是父类中的方法。
下面我们来看一个利用Class类实例方法的练习。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
Method method = c.getMethod("setUsername", String.class);
Object object = c.newInstance();
method.invoke(object, "king");
System.out.println(object);
}
}
反射中的Field
与Class类的构造器和方法类似的就是成员变量,我们首先讨论如何获取成员变量。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
// 获取本类和父类中的所有public成员变量
Field[] fields1 = c.getFields();
System.out.println(fields1.length);
// 获取本类中所有成员变量
Field[] fields2 = c.getDeclaredFields();
System.out.println(fields2.length);
// 获取本类或父类中名称为password,参数类型为String的public成员变量
Field field1 = c.getField("password");
// 获取本类中名称为password的成员变量,但它不能是父类中的成员变量
Field field2 = c.getDeclaredField("password");
}
}
通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的成员变量。
- getFields()方法:获取Class类及父类的所有public成员变量。
- getDeclaredFields()方法:获取Class类的所有成员变量,其中包含由public、private修饰符的。
- getField()方法:获取Class类及父类的具体成员变量。
- getDeclaredField()方法:获取Class类的具体成员变量,其中包含由public、private修饰符的。但不能是父类中的方法。
获取到Class类的成员变量之后,我们可以进行设置和获取操作。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
Field field = c.getField("password");
Object object = c.newInstance();
field.set(object, "123");
System.out.println(field.get(object));
}
}
需要注意的是这里只能设置和获取修饰符为public的成员变量,不能操作修饰符为private的成员变量。
AccessibleObject
AccessibleObject类是Constructor、Method和Field三个类的父类。AccessibleObject类的常用方法有以下几种:
- isAccessible()方法:判断当前成员是否可访问。
- setAccessible()方法:设置当前成员是否可访问。
当Class类中的构造器、方法和成员变量是私有的时候,如果我们想反射操作的话,就必须先调用setAccessible(true)方法。
public class Demo {
@Test
public void demo() throws Exception{
Class<User> c = User.class;
Field field = c.getDeclaredField("username");
Object object = c.newInstance();
field.setAccessible(true);
field.set(object, "king");
System.out.println(field.get(object));
}
}
转载说明:请注明作者及原文链接,谢谢!