一、反射
1.1 Java 反射概述
反射机制是 Java 语言的特性之一,是Java被视为动态语言的一个关键特性,其核心是指程序运行时动态加载类并获取类的详细信息
1.1.1 什么是反射
- 反射是一种能力,能够自描述和自控制,在运行状态中,动态获取类信息及调用类的属性和方法
- Java 反射的 3 个动态特性
- 运行时创建实例
- 运行期间调用方法
- 运行时更改属性
1.1.2 反射的原理
- 了解反射的原理,先回顾 Java 程序是如何执行的,如下图
-
之前所运行的程序,都是在编译时就已经链接了所需的类
-
而 Java 反射机制则运行程序在运行时在加载,使用那些在编译时完全未知的类
-
反射机制允许程序创建和控制任何类的对象,无需提前硬编码目标类
1.1.3 反射的用途
- 反射机制允许 Java 程序加载一个运行时才能得知其名称的类,获取该类的所有信息
- 并且可以生成其实例,对其属性赋值或调用其方法
- 通过 Java 反射可以实现对类的实例的构造,探知类所具有的方法和属性,调用任意一个实例的方法等等
- Java 反射机制可以探知类的基本结构,这种能力称之为 Java 类的 “自审”
- 反射机制是构建框架技术的基础所在
1.1.4 反射常用的 API
使用 Java 反射技术,常用的类如下
类名 | 说明 |
---|---|
java.lang.Class <T> 类 | 反射的核心类,反射所有的操作都是围绕该类来生成 通过 Class 类可以获取类的属性、方法等内容 |
java.lang.reflect.Construcrot<T> 类 | 表示类的构造方法 |
java.lang.reflect.Field 类 | 表示类的属性,可以获取和设置类中属性的值 |
java.lang.reflect.Method 类 | 表示类的方法,可以用来获取类中方法的信息或执行方法 |
1.2 反射的应用(获取类的信息)
在 Java 程序中使用反射的基本步骤如下
- 导入 java.lang.reflect 包中的相关类
- 获取需要操作的类的 Class 实例
- 调用 Class 实例的方法获取 Filed、Method 等实例
- 使用反射 API 操作实例成员
当一个类或接口被加载后,从系统当中可以等到一个代表该类或接口的 Class 类型的实例,通过该实例就可以访问到 Java 虚拟机找那个的这个类或接口
1.2.1 获取 Class 实例
获取 Class 实例主要有三种方法,可以根据实际情况灵活选择
- 调用类或接口实例的 getClass() 方法
- 所有类和接口的实例都可以调用 getClass() 方法
- 该方法会返回该实例的所属类型对应的 Class 实例
Person person=new Person();
Class c1=person.getClass();
- 调用类或接口的 class 属性
- 在某些类或接口没有完成实例或无法创建实例时
- 可以通过其 class 属性获取所对应的 Class 实例
- 这种方法需要在编译期就知道类或接口的名称
Class c2=Person.class;
- 使用 Class.forName()
- 编码时无法确认具体类型,需要程序在运行时根据情况灵活加载
- 可以使用 Class.forName() 方法,该方法是静态的,需要手动传入字符串参数
- 字符串参数的值是某个类,即包含类包名和完整类名
Class c3=Class.forName("com.mysql.cj.jdbc.Driver");
1.2.2 从Class 实例获取信息
在获取了对应的 Class 实例后,可以使用 Class 实例的方法来获取该类型的信息
- 获取对应类型基本信息的方法
方法 | 说明 |
---|---|
String getName() | 以字符串形式返回该类型的名称,即包名加类名 |
Stirng getSimpleName() | 以字符串形式返回该类型的简称,即类名 |
Package getPackage() | 获取该类型所在的包 |
Class getSuperclass() | 返回该类型的超类的 Class 实例,即该类型的父类的 Class 实例 |
Class [] getInterfaces() | 返回该类型所实现的全部接口的 Class 实例 |
int getModifiers() | 返回该类型的所有修饰符,由 public、protected、private、final、static、abstract 等对应的 int 常量组成,返回的整数应使用 Modifier 工具类来解码,才可以判断修饰符的构成 |
Class [] getDeclaredClasses() | 返回该类型中包含的全部内部类的 Class 实例 |
Class getDeclaringClass() | 返回该类型所在的外部类的 Class 实例,例如 A类内有内部类B,B.getDeclaringClass() 则返回 A类的 Class 实例 |
- 获取对应类型所包含构造方法的方法
方法 | 说明 |
---|---|
Constructor getConstructor(Class … parmas) | 返回该类型指定参数列表的 public 方法 |
Constructor[] getConstructors() | 返回该类型的所有 public 构造方法 |
Constructor getDeclaredConstructor(Class … parmas) | 返回该类型的指定参数列表的构造方法,访问级别不限 |
Constructor[] getDeclaredConstructors() | 返回该类型的所有构造方法,访问级别不限 |
- 获取对应类型所含属性的方法
方法 | 说明 |
---|---|
FieId getFieId(String name) | 返回该类型中指定名称的 public 属性,name 参数用于指定属性名称 |
FileId[] getFieIds() | 返回该类型中的所有 public 属性 |
FileId getDeclaredFieId(String name) | 返回该类型中指定名称的属性,与属性的访问级别无关 |
FileId[] getDeclaredFieIds() | 返回该类型中的全部属性,与属性的访问级别无关 |
- 访问类包含方法的方法
方法 | 说明 |
---|---|
Method getMethod(String name,Class … parmas) | 返回该实例中指定的 public 方法,name参数用于 指定方法名称,params 参数指定参数列表 |
Method[] getMethods() | 返回该实例中所有的 public 方法 |
Method getDeclaredMethod(String name,Class … params) | 返回该实例中指定的方法,与方法的访问级别无关 |
Method[] getDeclaredMethods() | 返回该实例中的全部方法,与方法的访问级别无关 |
从 Class 实例获取信息,当需要传递参数时,也是传递类型的实例,例如
- getConstructor(String.class,List.class)
- 上面就是传递了一个 String 类型以及 List 集合类型 的参数
1.2.3 创建实例
通过反射来创建 Java 类型的实例有以下两种方式
- 使用 Class 实例的 newInstance() 方法创建相关类型的实例
- 使用 Constructor 实例创建相关类型的实例
- 示例
Object o1 = c1.newInstance();
Object o2 = c1.getConstructor().newInstance();
1.2.4 访问类的属性
使用 File 实例可以对属性进行取值或赋值操作
访问属性的方法
方法 | 说明 |
---|---|
xxx getXxx(Object obj) | xxx 表示b中基本数据类型之一,如 int getInt(Object obj)。obj 为类的实例,如果 FieId 实例表示的是一个静态方法,则 obj 可以为空 |
Obejct get(Object obj) | 以 Object 类型返回 obj中相关属性的值 |
void setXxx(Object obj,xxx val) | 以 obj 中相关属性的值设置为 val。xxx 为 8中基本数据类型之一 |
void ser(Object obj,Object val) | 以 obj 中相关属性的值设为 val |
void setAccessible(boolean flag) | 对相关属性设置访问权限,这是为 true 可以禁止 Java 语言的访问检查 也就是可以对私有属性赋值 |
示例
package Test01;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
Class<?> c1 = Person.class;
// 创建实例
Object o = c1.newInstance();
// 获取 private String name; 属性
Field name = c1.getDeclaredField("name");
// 禁用java语言访问检查
name.setAccessible(true);
// 取值
System.out.println(name.get(o));
// 赋值
name.set(o,"巧克力");
System.out.println(name.get(o));
//
}
}
1.2.3 调用类的方法
-
Method 类中包含一个 invoke() 方法,通过 invoke() 方法,Method 实例可以调用 Java 类的实例方法和静态方法
-
此外还有一个方法,可以设置私有方法可访问,关键字为
setAccessible(true)
-
invoke() 方法定义如下
Object invoke(Object obj,Object ... args);
- obj 是执行该方法的对象,args 是执行该方法是传入的参数
- 例如 Person 类当中有一个
public void show(int agrs)
方法,演示
package Test01;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchFieldException {
Class<?> c1 = Person.class;
// 创建实例
Object o = c1.newInstance();
// 获取 public void show(int agrs) 方法
Method show = c1.getDeclaredMethod("show", int.class);
show.invoke(o,123);
}
}
Class<?> c1 = Person.class;
// 创建实例
Object o = c1.newInstance();
// 获取 public void show(int agrs) 方法
Method show = c1.getDeclaredMethod(“show”, int.class);
show.invoke(o,123);
}
}