1、定义
百度百科:Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
维基百科:反射指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。
Java的反射机制的实现要借助于4个类:Class,Constructor,Field,Method;
2、应用场景
(1)、在Java类里面解析xml或properties里面的内容,载入类的Class实例。
这样就可以动态配置一些东西,不用每一次都要在代码里面去new或者做其他的事情,以后要改的话直接改配置文件,代码维护起来就很方便了。
(2)、在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new 一个实例,而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。
比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的jar都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把jar加载进内存,然后通过反射的方式来调用jar中的方法。
(3)、动态修改一些系统级别的API
比如某系统API类的某个属性设置了固定默认值,子类无法直接访问,而我们在自定义类的时候可能需要去修改这个默认值,就可以通过反射来实现。
…
3、常用API
1、获取反射中的class对象
private static void callApi1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clz = Class.forName("template.ClassA");//类所在包下的路径
Class clz2 = ClassA.class;
ClassA classA = new ClassA();
Class clz3 = classA.getClass();
System.out.println("类对象:" + clz);
System.out.println("类对象2:" + clz2);
System.out.println("类对象3:" + clz3);
}
2、通过反射创建类实例
private static void callApi2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//方式1
Class clz = ClassA.class;
ClassA classA = (ClassA) clz.newInstance();
System.out.println("通过 Class 对象创建类实例:" + classA);
//方式2
Constructor constructor = clz.getConstructor();
ClassA classA2 = (ClassA) constructor.newInstance();
System.out.println("通过 Constructor 对象创建类实例:" + classA2);
//3、自定义构造函数的类实例
Constructor constructor2 = clz.getConstructor(String.class);
ClassA classA3 = (ClassA) constructor2.newInstance("Lily");
System.out.println("通过 Constructor 对象创建类有参构造函数实例:" + classA3);
}
通过 Constructor 对象创建类实例可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法(public)。
3、遍历方法和属性及调用
private static void callApi3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射实例化
Class clz = Class.forName("template.ClassA");
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("包含的方法:" + method);
}
Method[] publicMethods = clz.getMethods();
for (Method method : publicMethods) {
System.out.println("包含的公有方法:" + method);
}
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println("包含的属性:" + field);
}
Field[] publicFields = clz.getFields();
for (Field field : publicFields) {
System.out.println("包含的公有属性:" + field);
}
Method setNameMethod = clz.getMethod("setName", String.class);
Constructor constructor = clz.getConstructor();
Object obj = constructor.newInstance();
setNameMethod.invoke(obj, "Tom.Hanks");
Method getNameMethod = clz.getMethod("getName");
System.out.println("反射实例化调用类的方法:" + getNameMethod.invoke(obj));
}
方法名称 | 描述 |
---|---|
getDeclaredMethods | 获取的是类自身声明的所有方法,包含public、protected和private方法 ;不能继承父类的方法 |
getMethods | 获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。 |
getDeclaredFields | 获取的是类自身声明的所有属性 ,包含public、protected和private属性;不能继承父类的属性 |
getFields | 获取的是类的所有共有属性,这就包括自身的所有public属性,和从基类继承的、从接口实现的所有public属性。 |
4、案例
ClassA类:
package template;
/**
* Created by Administrator on 2020/5/11.
*/
public class ClassA {
private String name;
private final String DEFAULT_NAME;//JVM 在编译阶段得到的 .class 文件已经将常量优化为具体的值,在运行阶段就直接使用具体的值了,所以即使修改了常量的值也已经毫无意义了。
public int TAG;
public ClassA() {
DEFAULT_NAME = "无";//将赋值放在构造函数中,构造函数是我们运行时 new 对象才会调用的,所以就不会直接为常量赋值。
}
public ClassA(String name) {
this.name = name;
this.DEFAULT_NAME = "无";//将赋值放在构造函数中,构造函数是我们运行时 new 对象才会调用的,所以就不会直接为常量赋值。
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String getDefaultName() {
return DEFAULT_NAME;
}
}
TestReflect类:
package template;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Administrator on 2020/5/11.
*/
public class TestReflect {
public static void main(String[] args) {
try {
callApi1();
callApi2();
callApi3();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
//1、获取反射中的class对象
private static void callApi1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clz = Class.forName("template.ClassA");
Class clz2 = ClassA.class;
ClassA classA = new ClassA();
Class clz3 = classA.getClass();
System.out.println("类对象:" + clz);
System.out.println("类对象2:" + clz2);
System.out.println("类对象3:" + clz3);
}
//2、通过反射创建类实例
private static void callApi2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//方式1
Class clz = ClassA.class;
ClassA classA = (ClassA) clz.newInstance();
System.out.println("通过 Class 对象创建类实例:" + classA);
//方式2
Constructor constructor = clz.getConstructor();
ClassA classA2 = (ClassA) constructor.newInstance();
System.out.println("通过 Constructor 对象创建类实例:" + classA2);
//通过 Constructor 对象创建类实例可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法(public)。
Constructor constructor2 = clz.getConstructor(String.class);
ClassA classA3 = (ClassA) constructor2.newInstance("Lily");
System.out.println("通过 Constructor 对象创建类有参构造函数实例:" + classA3);
}
//3、遍历方法和属性及调用
private static void callApi3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射实例化
Class clz = Class.forName("template.ClassA");
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("包含的方法:" + method);
}
System.out.println("------------------------------分隔线------------------------------");
Method[] publicMethods = clz.getMethods();
for (Method method : publicMethods) {
System.out.println("包含的公有方法:" + method);
}
System.out.println("------------------------------分隔线------------------------------");
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println("包含的属性:" + field);
}
System.out.println("------------------------------分隔线------------------------------");
Field[] publicFields = clz.getFields();
for (Field field : publicFields) {
System.out.println("包含的公有属性:" + field);
}
System.out.println("------------------------------分隔线------------------------------");
Method setNameMethod = clz.getMethod("setName", String.class);
Constructor constructor = clz.getConstructor();
Object obj = constructor.newInstance();
setNameMethod.invoke(obj, "Tom.Hanks");
Method getNameMethod = clz.getMethod("getName");
System.out.println("反射实例化调用:" + getNameMethod.invoke(obj));
}
}
输出:
5、总结
防止硬编码,更多的体现则是一种灵活性。
当在为私有属性赋值的时候需要设置setAccessible(true);
getDeclaredMethods:不能继承父类(或接口)的所有方法
getMethods:能继承父类(或接口)的公开方法
getDeclaredFields:不能继承父类(或接口)的所有属性
getFields:能继承父类(或接口)的公开属性
当子类想获取父类中的私有属性时,可以通过:getSuperclass的getDeclaredFields()方法来获取。