第 1 章 Java 反射
1.1 反射的概述
1.1.1 什么反射
JAVA反射(Reflection)机制是在运行状态中,对于任意一个类, 都能够知道这个类的所有属性和方法;对于任意一个对象, 都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java的反射机制。
反射是程序的自我分析能力,通过反射可以确定类有哪些方法、有哪些构造方法以及有哪些成员变量。通过反射机制能够动态读取一个类的信息;能够在运行时动态加载类,而不是在编译期。反射可以应用于框架开发,它能够从配置文件中读取配置信息动态加载类、创建对象,以及调用方法和成员变量。
下面我们解剖一下对象的创建过程
如果一个class文件已经加载到内存(第3步),是不是就可以从class对象中获取类的方法和属性呢?这就是Java的反射。
1.1.2 反射的主要用途
反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。也就是我们将类名配置在配置文件中,Java反射通过类名来创建类的对象,并调用对象的方法和使用对象的属性。
反射拥有以下四大功能:
(1)在运行时(动态编泽)获知任意一个对象所属的类。
(2)在运行时构造任意个类的对象。
(3)在运行时获知任意一个类所具有的成员变量和方法。
(4)在运行时调用任意一个对象的方法和属性。
1.1.3反射机制的优点
(1)增加灵活性和扩展性:使用反射机制可以在程序运行时动态加载、修改、创建、调用类和方法等,从而增加了程序的灵活性和可扩展性。
(2)提高代码的通用性:通过反射机制可以动态的获取类信息,从而可以编写通用的代码,使得不同的类能够以相同的方式来处理。
(3)规范代码结构:反射机制可以使代码结构清晰明了,减少了代码中的冗余部分。
(4)实现框架和插件:反射机制在很多框架和插件中都有广泛的应用,比如Spring框架、 JUnit测试框架等。
(5)动态代理:反射机制的另一个重要应用是实现动态代理,可以在不修改原来代码的情况下,通过代理对象对原对象的方法进行增强。
1.2 反射的使用
1.2.1 Class 类
Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法。
1、获得类相关的方法
方法 | 用途 |
---|---|
asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组, 数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
2、获得类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
3、获得类中构造器相关的方法
方法 | 用途 |
---|---|
getConstrutor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型配匹的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
1.2.2 获取类信息
(1)创建一个学生类
/**
* 学生类
*/
public class Student {
private Long id;
private String name;
public Student() {
}
public Student(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public String concat(Long id,String name){
return name + id;
}
}
(2)创建测试类,通过反射来获取类的信息
public class StudentTest {
@Test
public void test1() throws Exception{
/*
* 1、加载类:已知类名获取类
* (1)Class.forname(类的全名):通过类名加载
* (2)类名.class:通过字节码文件加载
* (3)类实例名.getClass():通过类实例加载
*/
Class<?> clazz = Class.forName("org.example.Student");
//Class<?> clazz = Student.class;
/*
* 2、创建类实例
* (1) newlnstance():方法内部实际上调用了无参数构造方法,
* 必须保证无参构造存在,在新版本中已标记过时
* (2)class.getDeclaredConstructor(...构造参数类)
* .newInstance(...参数):可带参数
*/
Object o = clazz.getDeclaredConstructor(Long.class,String.class)
.newInstance(1001L,"张三");
// 调用方法toString
System.out.println(o.toString());
}
}
1.2.3 Method 类
Method类方法
方法名 | 用途 |
---|---|
getName() | 返回方法名 |
getModifiers() | 获取方法的修饰符列表,返回的修饰符是一个数字, 每个数字是修饰符的代号[一般配合Modifier类的toString(int x)方法使用] |
getReturnType() | 以Class类型,返回方法类型[一般配合Class类的getSimpleName()方法使用] |
getParameterTypes() | 返回方法的修饰符列表(一个方法的参数可能会有多个) [结果集-一般配合Class类的getSimpleName()方法使用] |
invoke(Object obj, Object… args) | 调用方法 |
1.2.4 反射动态方法调用
创建测试方法,通过反射动态调用方法
@Test
public void test2() throws Exception{
// 1、加载类
Class<?> clazz = Class.forName("org.example.Student");
// 2、创建类实例
Object o = clazz.getDeclaredConstructor().newInstance();
// 3、获取setId,setName和concat方法:已知方法名获取方法
Method setId = clazz.getDeclaredMethod("setId", Long.class);
Method setName = clazz.getDeclaredMethod("setName", String.class);
Method concat = clazz.getDeclaredMethod("concat", Long.class, String.class);
// 4、执行方法
setId.invoke(o,1001L);
setName.invoke(o,"张三");
String stuinfo = (String) concat.invoke(o, 1002L, "李四");
// 5、输出
System.out.println(o.toString());
System.out.println(stuinfo);
}
1.2.5 Field 类
Field类方法
方法名 | 用途 |
---|---|
getName() | 返回属性名 |
getModifiers() | 获取属性的修饰符列表,返回的修饰符是一个数字, 每个数字是修饰符的代号[一般配合Modifer类的toString(int x)方法使用] |
getType() | 以Class类型,返回属性类型[一般配合Class类的getSimpleName()方法使用] |
set(Object obj, Object value) | 设置属性值 |
get(Object obj) | 读取属性值 |
1.2.6 反射动态属性读写
创建测试方法,通过反射动态读写属性
@Test
public void test3() throws Exception{
// 1、加载类
Class<?> clazz = Student.class;
// 2、创建类实例
Object o = clazz.getDeclaredConstructor().newInstance();
// 3、获取id,name属性:已知属性名获取属性
Field id = clazz.getDeclaredField("id");
Field name = clazz.getDeclaredField("name");
// 4、给属性赋值
id.setAccessible(true);// 解除属性的写保护
id.set(o,1001L);
name.setAccessible(true);
name.set(o,"张三");
// 5、读取属性值
System.out.println(id.get(o) + "\t" + name.get(o));
}
【总结】
Java反射是一种强大的特性, 它允许我们在运行时获取和操作类的信息。通过反射,我们可以动态地创建对象、调用方法、访问和修改字段。
在JavaBean工 具中,使用反射机制可以获取类的属性名、属性值、调用属性的setter和getter方法等信息, 方便进行对象的序列化与反序列化操作。反射机制还可以实现动态代理,实现不改变原来代码的情况下,对原来对象的方法进行增强。