1、概述
Java中的反射编程是通过在JVM运行时获取某个类型的字节码(Class)对象,从而利用其反向获取此类型定义的内部信息实现编程的一种机制。反射编程在设计开发工具框架时使用较多,如Spring、hibernate等,反射编程通常属于非常规操作,存在一定的安全问题。Java应用框架技术中使用比较频繁,反射编程较普通的编程方式执行效率要低。
通过Java的反射机制,我们可以在程序中访问已经装载到JVM中的Java对象描述,实现访问、检测、修改描述Java对象本身信息的功能。在java.lang.reflect包中提供了对该功能的支持。
Java反射中常用组件如下:
- java.lang.Class:字节码类型。
- java.lang.reflect.Constructor:构造器类型。
- java.lang.reflect.Method:方法类型。
- java.lang.reflect.Field:属性类型(字段类型)。
- java.lang.reflect.Modifier:访问修饰符类型访问级别。
2、Class类
Class类的实例表示正在运行的Java应用程序中的类和接口,是字节码的实例。Class没有公共的构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。任何一个Java对象都可以获得一个和其相关的Class对象。利用Class对象可以获得此类信息,所有同一类型的实例共享一个字节码对象。
api中说明如下:
提供的方法如下:
常用方法说明如下:
- forName(String className):返回与给定字符串名称的类或接口相关联的类对象,返回的是Class类型。
- getClassLoader():返回类的类加载器,返回ClassLoader类型。
- getConstructor(类<?>… parameterTypes):返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共类函数。
- getConstructors():返回包含一个数组Constructor对象反射由此表示的类的所有公共构造类对象。
- getDeclaredClasses():返回一个反映所有被这个 类对象表示的类的成员声明的类和类对象的数组。
- getDeclaredConstructor(类<?>… parameterTypes):返回一个Constructor对象,该对象反映Constructor对象表示的类或接口的指定类函数。
- getDeclaredConstructors():返回一个反映Constructor对象表示的类声明的所有 Constructor对象的数组 。
- getDeclaredField(String name):返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象。
- getDeclaredFields():返回的数组Field对象反映此表示的类或接口声明的所有字段类对象。
- getDeclaredMethod(String name, 类<?>… parameterTypes):返回一个 方法对象,它反映此表示的类或接口的指定声明的方法类对象。
- getDeclaredMethods():返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
- getField(String name):返回一个Field对象,它反映此表示的类或接口的指定公共成员字段类对象。
- getFields():返回包含一个数组Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
- getMethod(String name, 类<?>… parameterTypes):返回一个方法对象,它反映此表示的类或接口的指定公共成员方法类对象。
- getMethods():返回包含一个数组方法对象反射由此表示的类或接口的所有公共方法类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
- getModifiers():返回此类或接口的Java语言修饰符,以整数编码。
- getName():返回由类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为String 。
- getPackage():获取此类的包。
- getResource(String name):查找具有给定名称的资源。
- getResourceAsStream(String name):查找具有给定名称的资源。
- newInstance():创建由此类对象表示的类的新实例。
3、反射访问构造方法
Constructor类表示某个类的构造方法的实例,利用Constructor可以创建Constructor所在类的实例并对此类进行初始化等操作,Constructor没有公共构造方法,所以不能实例化,可通过某个类的Class实例获得一个Constructor实例。
api中说明如下:
提供的方法如下:
常用方法说明如下:
- getExceptionTypes():返回一个 类对象的数组, 类表示由该对象表示的底层可执行文件声明的异常类型。
- getModifiers():返回由该对象表示的可执行文件的Java语言modifiers,返回的是int型。
- getName():以字符串形式返回此构造函数的名称。
- getParameterCount():返回由此对象表示的可执行文件的形式参数(无论是显式声明还是隐式声明)的数量。
- getParameterTypes():返回一个 类对象的数组, 类以声明顺序表示由该对象表示的可执行文件的形式参数类型。
- isVarArgs():返回true如果这个可执行文件被宣布为带有可变数量的参数;返回false其他。
- newInstance(Object… initargs):使用此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
- setAccessible(boolean flag):如果构造方法的权限是private,则默认不允许使用newInstance方法,可以先调用整个方法,将入口参数设为true,则允许使用newInstance创建对象。
- toString():返回一个描述这个Constructor的字符串。
下面代码进行测试。
创建实体类:
public class Book {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Student {
private String name;
private Integer age;
private double height;
private double weight;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public Student(String name, Integer age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
// 自定义方法
public void learn(Book book) {
System.out.println("读者的姓名是:" + this.getName() + ",正在阅读的书籍是:" + book.getName());
}
void run() {
System.out.println(this.getName() + "喜欢的运动是爬山!");
}
}
测试:
public class Test {
public static void main(String[] args) {
try {
// 反射获取Class对象
Class<?> stuClass = Class.forName("com.ycz.reflect.pojo.Student");
// 按照参数类型获取构造器
Constructor<?> constructor = stuClass.getConstructor(String.class, Integer.class);
// 由构造器获取实例
Student student = (Student) constructor.newInstance("云过梦无痕", 25);
System.out.println("姓名:" + student.getName() + ",年龄:" + student.getAge());
// 获取构造器的访问级别
int level = constructor.getModifiers();
System.out.println("构造器的访问级别:" + level);
// 获取构造器的入口参数数量
int count = constructor.getParameterCount();
System.out.println("该构造方法的参数个数:" + count);
// 获取参数数组
Class[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.println("第" + (i + 1) + "个参数类型为:" + params[i]);
}
// 入口参数是否允许为可变
boolean b = constructor.isVarArgs();
System.out.println("该构造方法入口参数是否可变?" + b);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行,控制台:
4、反射获取成员变量和成员方法
java.lang.reflect.Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限,反射的字段可能是一个类(静态)字段或实例字段。Field提供了对字段的反射操作动作,使编程中访问属性字段更加灵活。
api说明如下:
提供的方法如下:
java.lang.reflect.Method提供有关类或接口的方法的信息,以及对它的动态访问权限。
api中说明如下:
提供的方法如下:
以下代码进行测试:
public class Test {
public static void main(String[] args) {
try {
// 反射获取Class实例
Class<?> stuClass = Class.forName("com.ycz.reflect.pojo.Student");
// 获取构造方法,不指定默认获取无参
Constructor<?> constructor = stuClass.getConstructor();
// 由构造方法获取实例
Student student = (Student) constructor.newInstance();
System.out.println(stuClass.getName());
System.out.println(stuClass.getModifiers() == Modifier.PUBLIC);
// 获取修饰权限为public的成员变量数组
Field[] fields = stuClass.getFields();
System.out.println("修饰权限为public的成员变量个数:" + fields.length);
// 获取所有成员变量数组
Field[] allFields = stuClass.getDeclaredFields();
for (int i = 0; i < allFields.length; i++) {
System.out.println("第" + (i + 1) + "个成员属性名称:" + allFields[i].getName());
}
// 获取指定参数构造器
Constructor<?> constructor2 = stuClass.getConstructor(String.class, Integer.class, double.class);
System.out.println(constructor2 == null);
// 获取修饰权限为public的方法数组,包括从父类继承的
Method[] methods = stuClass.getMethods();
System.out.println("修饰权限为public的方法个数:" + methods.length);
for (int i = 0; i < methods.length; i++) {
System.out.println("第" + (i + 1) + "个方法名:" + methods[i].getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
将Student中age字段的修饰权限改为public。
public class Test {
public static void main(String[] args) {
try {
// 反射获取Class实例
Class<?> stuClass = Class.forName("com.ycz.reflect.pojo.Student");
// 获取构造方法,不指定默认获取无参
Constructor<?> constructor = stuClass.getConstructor();
// 由构造方法获取实例
Student student = (Student) constructor.newInstance();
// 获取修饰权限为public的指定成员属性
Field ageField = stuClass.getField("age");
ageField.set(student, 25);
// 获取指定成员属性,不限修饰符
Field nameField = stuClass.getDeclaredField("name");
// private的属性要调用setAccessible方法,入口参数设为true
nameField.setAccessible(true);
nameField.set(student, "云过梦无痕");
Field heightField = stuClass.getDeclaredField("height");
heightField.setAccessible(true);
heightField.set(student, 182.5);
Field weightField = stuClass.getDeclaredField("weight");
weightField.setAccessible(true);
weightField.set(student, 67.5);
System.out.println("姓名:" + student.getName());
System.out.println("年龄:" + student.getAge());
System.out.println("身高:" + student.getHeight() + "cm");
System.out.println("体重:" + student.getWeight() + "kg");
Book book = new Book();
book.setName("山海经");
// 获取修饰符为public指定方法,两个参数:方法名和参数类型
Method method = stuClass.getMethod("learn", Book.class);
// 方法调用,两个参数:对象和参数
method.invoke(student, book);
// 获取指定方法,不限修饰符
Method method2 = stuClass.getDeclaredMethod("run");
// 如果修饰权限不为public,要设置才能调用
if (!method2.isAccessible()) {
// 调用setAccessible方法,入口参数设置true
method2.setAccessible(true);
// 调用,方法无参不用传
method2.invoke(student);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
再通过一个例子测试。
实体类:
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
// 自定义方法
public void sayHello() {
System.out.println("你好,我的名字是:" + this.getName() + ",年龄:" + this.getAge());
}
void sayLike(String name) {
System.out.println(this.getName() + "喜欢的人是:" + name);
}
}
测试:
public class Test {
public static void main(String[] args) {
try {
// 反射获取Class实例
Class<?> perClass = Class.forName("com.ycz.reflect.pojo.Person");
if (perClass != null) {
// 获取构造方法
Constructor<?> constructor = perClass.getConstructor(String.class, Integer.class);
if (constructor != null) {
// 由构造方法获取实例
Person person = (Person) constructor.newInstance("云儿", 25);
person.sayHello();
// 获取指定方法
Method method = perClass.getDeclaredMethod("sayLike", String.class);
if (!method.isAccessible()) {
method.setAccessible(true);
method.invoke(person, "八部天龙一页书");
}
}
}
// 反射获取ArrayList的Class对象
Class<?> arrClass = Class.forName("java.util.ArrayList");
if (arrClass != null) {
List<Integer> list = (List) arrClass.newInstance();
for (int i = 1; i <= 50; i++) {
if (i % 10 == 0) {
list.add(i);
}
}
System.out.println("1到50之间能被10整除的数一共有:" + list.size() + "个!");
for (int i : list) {
System.out.print(i + "\t");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}