详解 反射机制
反射机制,可能有的同学在学习本篇博文的内容之前,就久仰过大名。
因为,只要学习框架的知识,就必然会看到反射机制的应用。
那么,为什么反射机制这么受欢迎呢?
因为它功能十分强大。
至于为什么本人在次对反射机制赞不绝口,请看博文内容:
首先,本人先要来讲解下有关反射机制的一个很重要的知识点 —— 类的加载:
类的加载
请观看本人博文 —— 《详解 类的加载》
反射机制
概述:
JAVA反射机制是在运行状态中
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制
要想解剖一个类,必须先要获取到该类的字节码文件对象
而解剖使用的就是Class类中的方法,
所以先要获取到每一个字节码文件对应的Class类型的对象
获取 class文件对象:
获取 class文件对象的三种方式:
- Object类的getClass()方法
- 任何一个对象的 静态属性class
- Class类中静态方法forName():
public static Class forName(String className):
className: 这个表示的是一个类对应的全类名(就是需要加上包名)
那么,本人来展示下这三种方式:
首先,本人给出一个含有两个成员、三种构造方法、三个方法的信息存储类:
package edu.youzg.about_reflact.core;
public class FanInfo {
public String name;
int age;
public FanInfo() {
System.out.println("执行类空参构造方法");
}
public FanInfo(String name) {
this.name = name;
System.out.println("执行类单参构造方法 === " + name);
}
private FanInfo(String name, int age) {
this.name = name;
this.age = age;
System.out.println("执行类双参私有参构造方法 === " + name + " === " + age);
}
public void test(){
System.out.println("这是一个空参的test方法");
}
public void test1 (String name){
System.out.println("这是一个空参的test方法" + name);
}
public void test2(String name,int age){
System.out.println("两个参数的方法"+name+"==="+age);
}
private int test(String name, int age, double num) {
System.out.println("私有的方法" + name + "===" + age + "===" + num);
return 666;
}
}
那么,现在,本人来展示下如何获取 class文件对象:
package edu.youzg.about_reflact.core;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//如何获取一个类的字节码文件对象。
//方式1 getClasss()
Object obj = new Object();
Class aClass = obj.getClass();
Class aClass1 = obj.getClass();
Object obj2 = new Object();
Class aClass2 = obj2.getClass();
System.out.println(aClass==aClass1);
System.out.println(aClass1==aClass2);
System.out.println("=================================");
//方式2,每一个类,都有一个静态的 class 属性可以获取该类的字节码文件对象
Class objectClass = Object.class;
System.out.println(aClass2==objectClass);
Class stringClass = String.class;
System.out.println("=================================");
//方式3:使用Class中的方法 传入一个类的全限定名来获取
//全限定名:包名+类名 能够确保一个类的唯一性。org.westos.demo.MyTest org.westos.demo2.MyTest
//Class 类 描述字节码文件类型的。
//static Class<?> forName (String className)
//返回与带有给定字符串名的类或接口相关联的 Class 对象。
Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
System.out.println(fanClass == fanClass2);
}
}
现在,本人来展示下运行结果:
获取 构造方法:
获取所有构造方法:
- public Constructor<?>[] getConstructors():
获取所有的构造方法,不包含私有的- public Constructor<?>[] getDeclaredConstructors():
获取所有的构造方法 ,包括私有的
获取单个构造方法:
- public Constructor< T > getConstructor(Class<?>... parameterTypes):
获取单个的构造方法, 不包含私有的- public Constructor< T > getDeclaredConstructor(Class<?>... parameterTypes):
获取单个的构造方法,包含私有的
那么,本人来展示下如何 获取构造方法:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
//1.获取该类的字节码文件对象
Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
//2.获取该类的空参构造方法对象
Constructor constructor = fanClass.getConstructor();
//使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
Object obj = constructor.newInstance();
System.out.println(obj);
System.out.println("========================================");
//我们之前也可以借助有参构造来创建对象。
//我们采用反射的方式,来借助有参构造,创建该类对象。
//获取一个参数的构造方法对象。
Constructor constructor1 = fanClass.getConstructor(String.class);
//使用构造方法对象中的方法来创建该类对象。
FanInfo obj2 = (FanInfo) constructor1.newInstance("米斯达");
System.out.println(obj2);
System.out.println("========================================");
//我们以前不能使用私有构造直接创建对象
//下来我们采用反射的方式的方式,借助私有构造方法来创建出该类对象
//获取出私有构造方法对象
Constructor declaredConstructor = fanClass.getDeclaredConstructor(String.class, int.class);
//通过私有的构造方法对象,调用他的方法来创建该类对象。
//取消权限的语法检测
declaredConstructor.setAccessible(true);
Object obj7 = declaredConstructor.newInstance("波鲁纳雷夫", 30);
System.out.println(obj7);
}
}
现在,本人来展示下运行结果:
获取 类的对象:
获取 类的对象:
- public Object newInstance():
根据调用这个方法的Constructor类对象,获得一个相应的对象
那么,本人来展示下如何 获取类的对象:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
//我们采用反射的方式,来创建一个类的对象。
//1.获取该类的字节码文件对象
Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
//2.获取空参的构造方法对象
Constructor declaredConstructor = fanClass.getDeclaredConstructor();
//3.调用 Constructor 中的方法来创建FanInfo的对象
FanInfo fan = (FanInfo) declaredConstructor.newInstance();
System.out.println(fan);
System.out.println("===================================");
//我们现在来通过双参构造方法来创建FanInfo的对象
Constructor constructor = fanClass.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
//设置权限
fan = (FanInfo) constructor.newInstance("米斯达", 30);
System.out.println(fan);
System.out.println("===================================");
//如果我们借助 空参 来创建对象,还有一个简便方法。
Class fanClass2 = Class.forName("edu.youzg.about_reflact.core.FanInfo");
//不用去获取空参构造对象,在 Class 里面有一个方法,就能创建对象。
FanInfo fan2 = (FanInfo) fanClass2.newInstance();
System.out.println(fan2);
}
}
那么,本人现在来展示下运行结果:
获取成员变量:
获取所有成员变量:
- public Field[] getFields():
获取所有的成员变量包含从父类继承过来的- public Field[] getDeclaredFields():
获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
获取单个成员变量:
- public Field getField(String name)
- public Field getDeclaredField(String name)
那么,本人来展示下如何 获取成员变量:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
//对于成员变量 用 Field 这个类型来描述
//1.获取该类的字节码文件对象
Class fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
//2.获取该类的成员变量对象
//2.1 获取该类中所有的字段对象数组
//getFields() 获取所有的公共字段
Field[] fields = fanClass.getFields();
for (Field field : fields) {
System.out.println(field.toString());
}
System.out.println("==================================");
//获取所有字段对象,包括私有
Field[] declaredFields = fanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("===========================================================");
//获取公共的单个字段对象
Field name = fanClass.getField("name");//传入字段名称
System.out.println(name);
//获取非公共的单个字段对象。
Field age = fanClass.getDeclaredField("age");
System.out.println(age);
}
}
那么,本人现在来展示下运行结果:
获取成员方法:
获取所有成员方法:
- public Method[] getMethods():
获取所有的公共的成员方法不包含私有的
包含从父类继承过来的过来的公共方法- public Method[] getDeclaredMethods():
获取自己的所有成员方法 包含私有的
获取单个成员方法:
//参数1: 方法名称 参数2:方法行参的class 对象
- public Method getMethod(String name,Class<?>... parameterTypes):
获取单个的方法 不包含私有的- public Method getDeclaredMethod(String name,Class<?>... parameterTypes):
获取单个方法包括私有的
那么,本人现在来展示下 获取成员方法:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
//我们采用反射的方式来调用方法执行。
//1.获取该类的字节码文件对象
Class<?> fanClass = Class.forName("edu.youzg.about_reflact.core.FanInfo");
//2.获取空参的方法对象
Method testMethod = fanClass.getMethod("test");
//3.调用Method类中的 invoke() 方法,让test 调用执行
Object obj = fanClass.newInstance();
testMethod.invoke(obj); //参数1,类的对象,参数2,给方法的形参传递的实际的值
System.out.println("================================");
//调用两个参数的方法执行
Method test1 = fanClass.getMethod("test1", String.class);
//参数1,类的对象,参数2,给方法的形参传递的实际的值
test1.invoke(obj, "米斯达");
System.out.println("==================================");
Method test2 = fanClass.getMethod("test2", String.class, int.class);
test2.invoke(obj, "米斯达", 30);
System.out.println("==================================");
//以前的方式,你在外界new对象调用 私有方法是调用不到的。
//我们通过反射来调用私有方法执行。
Method test3 = fanClass.getDeclaredMethod("test3", String.class, int.class, double.class);
//调用私有方法,取消私有的权限校验
test3.setAccessible(true);
Integer result = (Integer) test3.invoke(obj, "波鲁纳雷夫", 30, 66.6);
System.out.println(result);
}
}
那么,本人现在来展示下运行结果:
现在,本人要讲解一个非常常用的反射机制的应用场景 —— 通过反射运行配置文件内容:
反射运行配置文件内容:
在本人《详解 Properties类》博文中,曾讲过:Properties类可以将配置文件中的键值对读取出来。
那么,在这里,本人将通过反射机制,将从配置文件中读取出来的值,赋给类中相应成员:
本人直接来上代码:
首先,本人来给出一个多功能手机类:
package edu.youzg.about_reflact.core;
public class Phone {
public Phone() {
}
public void game() {
System.out.println("正在运行游戏");
}
public void music() {
System.out.println("正在播放音乐");
}
public void call() {
System.out.println("正在进行通话");
}
}
现在,本人来展示下配置文件的信息:
className=edu.youzg.about_reflact.core.Phone
methodName=music
那么,现在,本人来展示下通过反射机制扫描配置文件,来调用目标方法:
package edu.youzg.about_reflact.core;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
//加载配置文件
//基于目标类开发
Properties properties = new Properties();
properties.load(new FileReader("./config.properties"));
//1.获取该类的字节码文件对象
Class clazz = Class.forName(properties.getProperty("className"));
//2.通过反射来创建目标类对象
Object obj = clazz.getDeclaredConstructor().newInstance();
//3.调用目标类中的方法执行
Method methodName = clazz.getMethod(properties.getProperty("methodName"));
methodName.invoke(obj);
}
}
那么,本人来展示下运行结果:
反射忽视泛型检查:
在本人博文《详解 泛型 与 自动拆装箱》中讲到过:
泛型仅在编译期有效,而反射机制是针对运行期的。
所以,我们若是通过反射机制去调用泛型的话,就会读取不到相应的类型,只会读取到Object类型,这就是我们所谓的 泛型擦除机制
那么,现在,本人就来展示下通过反射机制去调用一个集合时,可以存入不恰当的数据:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(666);
list.add(374);
list.add(251);
// list.add("abc");
//泛型,只在编译期有效,运行期就擦除了。
//ArrayList.class
Class aClass = list.getClass();
//获取add()方法对象
Method add = aClass.getMethod("add", Object.class);
add.invoke(list,"abc");
System.out.println(list);
}
}
那么,本人来展示下运行结果:
可以看到,Integer类型的ArrayList,存入了字符串数据!
接下来,本人来通过反射机制给出一个工具类,以便我们能够直接对一个对象的某成员赋值:
反射机制设置某个对象的某个属性为指定的值:
首先,是这个工具类:
package edu.youzg.about_reflact.core;
import java.lang.reflect.Field;
public class AssignUtil {
/* 此方法可将obj对象中名为propertyName的属性的值设置为value*/
public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, IllegalAccessException {
Class aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(propertyName);
declaredField.setAccessible(true); //取消private权限的检测
declaredField.set(obj,value);
}
}
现在,本人来给出一个测试类:
package edu.youzg.about_reflact.core;
public class Test {
public static void main(String[] args) throws Exception {
//通过反射写一个通用的设置某个对象的某个属性为指定的值
FanInfo FanInfo = new FanInfo();
AssignUtil.setProperty(FanInfo,"name","波鲁纳雷夫");
AssignUtil.setProperty(FanInfo,"age",30);
System.out.println(FanInfo.name);
System.out.println(FanInfo.age);
}
}
那么,现在,本人来展示下运行结果:
那么,现在,本人再来讲解一个有关反射机制的很重要的知识点 —— 动态代理:
动态代理
请观看本人博文—— 《详解 动态代理》