Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念:编译期和运行期。
编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作。比如:检查语法错误。
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Class类
Class类是一个特殊类,它用于表示JVM运行时类或接口的信息。
Class类提供很多方法用于获取类的各种信息,比如获取类名、判断该类是否是一个接口还是普通类等等。
在Java中枚举类是一种类,而注解是一个接口,数组也是一个类;Java原始类型(boolean, byte, char, short, int, long, float, and double)和关键字void也被表示为Class的对象。
一个类只有被JVM加载后才能使用,当类被虚拟机加载后都会在内存中创建一个该类的Class对象,用于存储该类的各种信息。
Class类是java中的一个类和其他非继承类一样,默认的父类也是Object。
public class Demo01 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 创建Student类的对象
// 编译期确定对象的类型(Student)
Student stu1 = new Student();
// 反射使用场景
// 运行期确定对象的类型()
// Student stu2 = (Student)(Class.forName("?").newInstance());
// System.out.println(stu2);
// 创建(获取)Class对象
// Student类/Person类的信息
Class cls1 = Person.class;
Class cls2 = Person.class;
Class cls3 = Person.class;
// 一个类只会被加载1次,加载后产生1个该类的Class对象
System.out.println(cls1.hashCode()); // 哈希值相同
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
}
}
class Person{}
class Student{}
这种通过Class实例获取class信息的方法称为反射(Reflection)。
以下为Class对象的三种创建方式:
// Class对象的三种创建方式
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:通过类名访问class
Class stringCls1 = String.class;
// 方式2:通过实例访问getClass()
String s = " ";
Class stringCls2 = s.getClass();
// 方式3:通过Class类的静态方法forName(类名)
Class stringCls3 = Class.forName("java.lang.String");
// 相同的Class对象
System.out.println(stringCls1.hashCode());
System.out.println(stringCls2.hashCode());
System.out.println(stringCls3.hashCode());
}
}
Class常用的方法
import java.time.DayOfWeek;
import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
import com.sun.xml.internal.ws.api.message.Packet;
public class Demo03 {
public static void main(String[] args) {
Class clssss1 = Person.class;
Class clssss2 = Integer.class;
Class clssss3 = List.class;
Class clssss4 = DayOfWeek.class;
Class clssss = String.class;
printClassInfo(clssss);
}
public static void printClassInfo(Class cls) {
// 类名
System.out.println("类(接口)的名称:" + cls.getName());
System.out.println("完全限定名:" + cls.getSimpleName());
System.out.println("类(接口)的类型名称:" + cls.getTypeName());
// 父类
Class superCls = cls.getSuperclass();
System.out.println("类的父类:" + superCls.toString());
// 接口实现类
Class[] interfaceCls = cls.getInterfaces();
System.out.println("当前类实现的接口:");
for (Class icls : interfaceCls) {
System.out.println(icls);
}
// packet包
Package pct = cls.getPackage();
if (pct != null) {
System.out.println("类所在的包的名称:" + pct.getName());
}
// 判断类型
System.out.println("是否为接口:" + cls.isInterface());
System.out.println(cls.isArray());
System.out.println(cls.isEnum());
}
}
以下为创建Class对象来获取其他类的信息:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.UUID;
public class Demo04 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
// 读取类的名称
String className = Files.readAllLines(Paths.get("e://test//relf.txt")).get(0);
// 得到该类的Class对象
Class cls = Class.forName(className);
// 创建三个对象
// 调用newInstance()相当于:Order o1 = new Order()
// 执行无参构造方法
Object obj1 = cls.newInstance();
Object obj2 = cls.newInstance();
Object obj3 = cls.newInstance();
System.out.println(obj1);
System.out.println(obj2);
System.out.println(obj3);
}
class Order{
private String orderNo;
private LocalDateTime creationTime;
public Order(){
this.orderNo = UUID.randomUUID().toString();
this.creationTime = LocalDateTime.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
调用构造方法
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象是一个构造方法,调用结果总是返回实例。
通过Constructor对象来调用:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// 反射的方式调用构造方法
public class Demo05 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class cls = Example.class;
// 调用无参构造方法创建Example类型的对象
Example ex1 = (Example)cls.newInstance();
System.out.println(ex1);
// 获取所有的构造方法
System.out.println("所有的构造方法:");
Constructor[] constructArray = cls.getConstructors();
for (Constructor construct : constructArray) {
System.out.println(construct);
}
System.out.println("------------------------------");
// 调用有参构造方法创建Example类型的对象
// 1、获取指定参数类型的构造方法
// 无参构造
// Constructor construct = cls.getConstructor();
// 有一个参数的构造方法
// Constructor construct = cls.getConstructor(int.class);
// 有两个参数的构造方法
Constructor construct = cls.getConstructor(int.class,double.class);
System.out.println(construct);
// 第二步:执行构造方法,创建Example类型的对象
Example ex2 = (Example)construct.newInstance(1024,3.1415926);
System.out.println(ex2);
}
}
class Example{
private Example(String s) {
System.out.printf("Example类的有参构造: s=%s\n",s);
}
protected Example(float f) {
System.out.printf("Example类的有参构造: f=%f\n",f);
}
public Example() {
System.out.println("Example类的无参构造!");
}
public Example(int a) {
System.out.printf("Example类的有参构造: a=%d\n",a);
}
public Example(int a, double b) {
System.out.printf("Example类的有参构造: a=%d,b=%f\n",a,b);
}
}
通过 Constructor对象的 .getConstructors() 方法来获取所有的public共有的构造方法;
通过 Constructor对象的 .getDeclaredConstructors() 方法来获取所有的构造方法;
通过 privateConstruct.setAccessible(true); 来打破私有的封装,从而获取构造方法。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// 反射的方式调用构造方法
public class Demo06 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class cls = Example.class;
System.out.println("所有的构造方法:");
// 获取所有public共有的构造方法
// Constructor[] constructArray = cls.getConstructors();
// for (Constructor construct : constructArray) {
// System.out.println(construct);
// }
// 获取所有定义的构造方法
Constructor[] constructArray = cls.getDeclaredConstructors();
for (Constructor construct : constructArray) {
System.out.println(construct);
}
// 获取一个私有的构造方法
Constructor privateConstruct = cls.getDeclaredConstructor(String.class);
// 调用一个私有的构造方法
privateConstruct.setAccessible(true);
Example ex = (Example)privateConstruct.newInstance("luyujiang");
System.out.println(ex);
}
}
获取继承关系
通过Class对象可以获取继承关系:
Class getSuperclass():获取父类类型;
Class[] getInterfaces():获取当前类实现的所有接口。
import java.io.FileInputStream;
// 反射操作 :获取继承关系
public class Demo07 {
public static void main(String[] args) {
// 父类
Class superClass = FileInputStream.class.getSuperclass();
System.out.println("父类:" + superClass.getName());
System.out.println("父类的父类:" + superClass.getSuperclass().getName());
// 接口
System.out.println("实现接口列表:");
Class[] interfaceClassArray = MyStringComparator.class.getInterfaces();
for (Class inf : interfaceClassArray) {
System.out.println(inf.getName());
}
}
}
class MyStringComparator implements Comparable{
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}
}
通过Class对象的isstanceof()方法可以判断 “引用” 和 “类型” 之间的关系;
通过Class对象的isAssignableFrom()方法可以判断 “类型” 和 “类型” 之间的关系;
// instanceof 运算符
// isAssignableFrom() 方法
public class Demo08 {
public static void main(String[] args) {
// instanceof 运算符:判断 “引用” 和 “类型” 之间的关系
Object obj1 = Integer.valueOf(123);
System.out.println("是否为Integer类型:" + (obj1 instanceof Integer));
System.out.println("是否为Number类型:" + (obj1 instanceof Number));
System.out.println("是否为Double类型:" + (obj1 instanceof Double));
System.out.println("是否为String类型:" + (obj1 instanceof String));
System.out.println("-----------------------");
Object obj2 = String.valueOf(123);
System.out.println("是否为Object类型:" + (obj2 instanceof Object));
System.out.println("是否为Integer类型:" + (obj2 instanceof Integer));
System.out.println("是否为Number类型:" + (obj2 instanceof Number));
System.out.println("是否为Double类型:" + (obj2 instanceof Double));
System.out.println("是否为String类型:" + (obj2 instanceof String));
System.out.println("-----------------------");
// isAssignableFrom() 方法:判断 “类型” 和 “类型” 之间的关系
System.out.println("Integer <= Integer ? " + (Integer.class.isAssignableFrom(Integer.class)));
System.out.println("Integer <= Number ? " + (Integer.class.isAssignableFrom(Number.class)));
System.out.println("Number <= Integer ? " + (Number.class.isAssignableFrom(Integer.class)));
System.out.println("Double <= Integer ? " + (Double.class.isAssignableFrom(Integer.class)));
System.out.println("Comparable <= Integer ? " + (Comparable.class.isAssignableFrom(Integer.class)));
}
}
访问字段
Java的反射API提供的Field类封装了字段的所有信息:
通过Class实例的方法可以获取Field实例:getField(),getFields(),getDeclaredField(),getDeclaredFields();
通过Field实例可以获取字段信息:getName(),getType(),getModifiers();
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
// 反射操作:访问字段(成员变量)
public class Demo09 {
public static void main(String[] args) {
Class cls = Book.class;
// 所有public访问修饰符定义的字段
Field[] fields1 = cls.getFields();
for (Field field : fields1) {
System.out.println("所有public访问修饰符定义的字段");
System.out.println("成员变量访问修饰符(int):" + field.getModifiers());
System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:" + field.getType());
System.out.println("成员变量名称:" + field.getName());
System.out.println("----------------------------------------");
}
// 所有定义的字段
Field[] fields2 = cls.getDeclaredFields();
for (Field field : fields2) {
System.out.println("所有定义的字段");
System.out.println("成员变量访问修饰符(int):" + field.getModifiers());
System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:" + field.getType());
System.out.println("成员变量名称:" + field.getName());
System.out.println("----------------------------------------");
}
}
}
class Book{
public String bookName;
public int stock;
private double sale;
private boolean isShelf;
@Override
public String toString() {
return "Book [bookName=" + bookName + ", stock=" + stock + ", sale=" + sale + ", isShelf=" + isShelf + "]";
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public double getSale() {
return sale;
}
public void setSale(double sale) {
this.sale = sale;
}
public boolean isShelf() {
return isShelf;
}
public void setShelf(boolean isShelf) {
this.isShelf = isShelf;
}
}
案例1:
import java.lang.reflect.Field;
// 使用反射的方式,完成成员变量的访问
public class Demo10 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
// 编译期完成对象类型和成员变量的访问
// Book book = new Book();
// book.bookName = "葫芦娃大战迪迦";
// 运行期
// 使用反射的方式,完成成员变量保存值
Class cls = Book.class; // 获取Class类型对象
Object obj = cls.newInstance(); // 通过反射创建Book类的对象
// 按照字段名称,获取指定字段
Field field = cls.getDeclaredField("bookName");
// 参数1:目标Book对象
// 参数2:存入成员变量的值
field.set(obj, "葫芦娃大战赛罗");
System.out.println(obj);
// sale字段(private)
// 按照字段名称,获取指定字段
Field field2 = cls.getDeclaredField("sale");
field2.setAccessible(true); // 访问私有成员变量
field2.setDouble(obj,0.8);
System.out.println(obj);
}
}
案例2:
import java.lang.reflect.Field;
// 反射操作:访问对象中的成员变量(字段)值
public class Demo11 {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Book book = new Book();
book.setBookName("书剑恩仇录");
book.setSale(0.75);
book.setShelf(true);
book.setStock(451);
printInfo(book);
}
private static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
// 输出参数对象的所有成员变量和值
Class cls = obj.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("成员变量名称:" + field.getName());
// 判断是否可以访问
if(!field.isAccessible()) {
field.setAccessible(true); // 设置私有成员变量允许访问
}
System.out.println("成员变量内容:" + field.get(obj));
System.out.println();
}
}
}
调用方法
Java的反射API提供的Method对象封装了方法的所有信息:
通过Class实例的方法可以获取Method实例:getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods();
通过Method实例可以获取方法信息:getName(),getReturnType(),getParameterTypes(),getModifiers();
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);
通过设置setAccessible(true)来访问非public方法;
通过反射调用方法时,仍然遵循多态原则。
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
// 反射操作:获取方法
// 每一个方法都会被封装成一个Method对象
public class Demo12 {
public static void main(String[] args) {
Class cls = Book.class;
// 获取所有public方法(包含父类)
// Method[] methods = cls.getMethods();
// 获取所有定义的方法(仅包含当前类)
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法的访问修饰符:" + Modifier.toString(method.getModifiers()));
System.out.println("方法的返回值类型:" + method.getReturnType());
System.out.println("方法的名称:" + method.getName());
System.out.println("--------------------");
// 获取所有的参数类型
// System.out.println("方法的参数类型:");
// Class[] paramTypes = method.getParameterTypes();
// 获取所有的参数对象
Parameter[] params = method.getParameters();
for (Parameter p : params) {
System.out.println(p.getName());
System.out.println(p.getType());
System.out.println("-------------------------");
}
System.out.println();
}
}
}
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 反射操作:调用方法
public class Demo13 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
// Base b = new Base();
// b.create(1000);
// 获取Class对象
Class cls = Base.class;
Object obj = cls.newInstance(); // 创建Base对象
// 按照方法名称和“参数类型”获取Method方法
// create()
// Method method = cls.getMethod("create");
// create(int x)
Method method = cls.getMethod("create",int.class);
// Method对象的invoke()作用:
// 以反射的方式执行create()方法
// create()
// int r = (int) method.invoke(obj);
// System.out.println(r);
// create(int x)
int r = (int) method.invoke(obj, 1000);
System.out.println(r);
}
}
class Base{
public Object create() {
int i = (int) (Math.random()*10);
return i;
}
public Object create(int x) {
int i = (int) (Math.random()*x);
return i;
}
}
案例1:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 以反射的方式调用静态方法
public class Demo14 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 计算10以底的对数
// 获取某个指定数字的位数
System.out.println((int)Math.log10(123456) + 1);
// 反射调用
Class cls = Math.class;
Method methodLog10 = cls.getMethod("log10", double.class);
int size = Double.valueOf((double)methodLog10.invoke(null, 123456)).intValue() + 1;
System.out.println(size);
}
}
我们来考察这样一种情况:一个Person类定义了hello()方法,并且它的子类Student也覆写了hello()方法,那么,从Person.class获取的Method,作用于Student实例时,调用的方法到底是哪个?
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo15 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取Person1的Hello方法
Method h = Person1.class.getMethod("hello");
// 对Student1调用Hello()方法
h.invoke(new Student1());
// 执行逻辑相当于
Person1 p = new Student1();
p.hello();
}
}
class Person1{
public void hello() {
System.out.println("Person:Hello");
}
}
class Student1 extends Person1{
public void hello() {
System.out.println("Student:Hello");
}
}