前言
为什么要学习反射?
反射可以动态创建对象以及获取对应的对象属性 方法,从而让代码更灵活不必写死,好多框架必不可少的使用了反射机制,同时反射机制也符合OCP(开闭原则)即在不修改源码的情况下,进行操作 看上边估计你可能印象不怎么大下边我举个栗子你就能感受到灵活的好处了。
反射可以做什么?
已知反射机制的特点后我们可以用反射机制来做个“状态机”,即根据时间来进行状态流转
- 可以用Redis zset延时队列 {key:value存当前类的包名类名和要执行的方法名} 后续到时间通过反射在调用执行~
- 定时任务每5秒按照时间分值取一次最近五分钟要操作的数据集
- 取出之后按照多线程加反射机制进行执行~因为考虑到反射机制创建对象比较慢,所以执行的时候可以开启多线程执行最近五分钟的任务执行完毕从队列删除此任务以免重复执行。
以上只是简单的介绍了下反射机制可以做什么,需要了解详细的可以和博主私聊讨论哦~
快速入门
一个需求引出反射
1、根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
classfullpath=com.hspedy.Cat
method=hi
package com.shb.reflection.question;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 反射问题引入
*/
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
//传统方式 new 对象 -》调用其方法
/* Cat cat = new Cat();
cat.hi();*/
//文件流方式 Properties
//1、取出配置文件值
Properties properties = new Properties();
properties.load(new FileInputStream("/Users/ext.songhaibo/IdeaProjects/Reflection/src/main/resources/re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println("classfullpath = " + classfullpath);
System.out.println("methodName = " + methodName);
//2、创建对象 传统的方法已经不行了
// new classfullpath();
//反射模式符合开闭原则 OCP,即在不修改源码的情况下,进行操作
//3、传统模式下解决不了就用反射机制
//(1)加载class类 返回Class对象
Class<?> cls = Class.forName(classfullpath);
//(2)通过 cls 得到加载类 com.shb.Cat的对象实例
Object o = cls.newInstance();
System.out.println(o.getClass());
//(3)通过 cls 得到加载的类的com.shb.Cat类的methodName"hi"的对象
//即:在反射中,可以把方法视为对象(万物皆对象)
Method method = cls.getMethod(methodName);
//通过method来调用方法,即通过方法对象来调用方法
method.invoke(o);//传统方式 对象.方法 反射机制 方法.invoke(对象)
}
}
反射原理图
- Java Reflection
1.反射机制允程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
2.加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射
- Java反射机制可以完成
-
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
- 反射相关的主要类:
java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
这些类在java.lang.reflect :提供用于获取关于类和对象的反射信息的类和接口
- 反射的优点和缺点
优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就是失去底层支撑。
缺点:使用反射基本是解释执行,对执行速度有影响。
例子:Reflection02.java
public class Reflection02 {
public static void main(String[] args) throws Exception {
m1();
m2();
}
//普通方法调用hi
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("普通方法调用hi 耗时 " + (end - start));
}
//反射机制调用hi
public static void m2() throws Exception {
//(1)加载class类 返回Class对象
Class<?> cls = Class.forName("com.shb.Cat");
Object o = cls.newInstance();
long start = System.currentTimeMillis();
Method method = cls.getMethod("hi");
for (int i = 0; i < 900000000; i++) {
method.invoke(o);//传统方式 对象.方法 反射机制 方法.invoke(对象)
}
long end = System.currentTimeMillis();
System.out.println("反射机制调用hi 耗时 " + (end - start));
}
- 反射调用优化-关闭访问检查
-
- Method和Field、Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁用访问安全检查的开关
- 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
Class类
- 基本介绍:
- Class也是类,因此也继承Object类
- Class类对象不是new出来的,而是系统创建的
- 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据
- Class常用api示例
/**
* Class常用api示例
*/
public class ClassApi {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classAllPath = "com.shb.Car";
//1、获取Car类对象
Class<?> cls = Class.forName(classAllPath);
//2、输出cls
System.out.println(cls); //是哪个类型 cls
System.out.println(cls.getClass()); //运行类型
//3、获取包名
System.out.println(cls.getPackage().getName());
//4、获取全类名
System.out.println(cls.getName());
//5、生成对象实例
Object o = cls.newInstance();
Car car = (Car) o;
System.out.println(car);
//6、通过反射获取属性 不能获取私有属性
Field brand = cls.getField("brand");
System.out.println(brand.get(car));
//7、通过反射给属性设置值
brand.set(car, "奥迪");
System.out.println(brand.get(car));
//8、得到所有的属性 只能得到公共的
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.toString());
}
//9、得到所有的属性包括私有的 getDeclaredFields
for (Field declaredField : cls.getDeclaredFields()) {
System.out.println(declaredField.toString());
}
}
- 获取Class对象的六种方式
1. 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取。
○ 应用场景:多用于配置文件,读取类全路径,加载类。
○ 阶段:代码阶段/编译阶段 Class.forName()
2. 前提:若已知具体的类,通过类的class获取,该方式 最为安全可靠,程序性能最高实例:Class cls=Cat.class;
○ 应用场景:多用于参数传递,比如通过反射得到对应构造器对象。
○ 阶段:加载阶段 类.class
3. 前提:已知某个类的实例,调用改实例的getClass()方法获取Class对象,实例:Class class= 对象.getClass();
○ 应用场景:通过创建好的对象,获取Class对象。
○ 阶段:运行阶段 对象.getClass()
4. 其他方式 类加载器 得到Class对象
○ ClassLoader cl=对象.getClass().getClassLoader();
○ Class clazz4=cl.loadClass("类的全类名");
5. 基本数据类型得到Class对象
○ Class cls= 基本数据类型.class
○ 阶段:加载阶段 类.class
6. 基本数据类型对应的包装类,可以通过.type得到Class类对象
○ Class cls=包装类.TYPE
○ 阶段:加载阶段 类.class
- 哪些类型有Class对象,如下类型有Class对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface接口
- 数组
- enum枚举
- annotation注解
- 基本数据类型
- void
类加载
- 基本说明
ClassLoad.java com.shb.classload
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
- 举例说明
- 类加载时机
- 当创建对象时(new)
- 当子类被加载时
- 调用类中的静态成员时
- 通过反射
Class.froName("com.shb.Cat");
- 类加载过程图
- 类加载各阶段完成任务
- 加载阶段
- 连接阶段-验证
- 连接阶段-准备
- 连接阶段-解析
- Initialization(初始化)
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程。
- <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕[debug源码]
通过反射获取类的结构信息
com.shb.reflection ReflectionUtils
- 第一组:java.lang.Class类
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中所有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类中所有方法
- getConstructors:获取所有public修饰的构造器,包含本类的
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回 包信息
- getSuperClass:以Class形式返回父类信息
- getInterfaces:以Class[]形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
- 第二组:java.lang.reflect.Field类
- getModifiers:以int形式返回修饰符(有对应的枚举)
- getType:以Class形式返回类型
- getName:返回属性名
- 第三组:java.lang.reflect.Method类
- getModifiers:以int形式返回修饰符
- getReturnType:以Class形式获取 返回类型
- getName:返回方法名
- getParameterTypes:以Class[]返回参数类型数组
- 第四组:java.lang.reflect.Constructor类
- getModifiers:以int形式返回修饰符
- getName:返回构造器名(全类名)
- getParameterTypes:以Class[]返回参数类型数组
package com.shb.reflection;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 通过反射获取类的结构信息 API
*/
public class ReflectionUtils {
public static void main(String[] args) {
}
@Test
// 第一组:java.lang.Class类
public void api_01() throws ClassNotFoundException {
//得到Class对象
Class<?> personClass = Class.forName("com.shb.reflection.Person");
// 1. getName:获取全类名
System.out.println(personClass.getName());
// 2. getSimpleName:获取简单类名
System.out.println(personClass.getSimpleName());
// 3. getFields:获取所有public修饰的属性,包含本类以及父类的
for (Field field : personClass.getFields()) {
System.out.println(field.getName());
}
// 4. getDeclaredFields:获取本类中所有属性
for (Field declaredField : personClass.getDeclaredFields()) {
System.out.println(declaredField.getName());
}
// 5. getMethods:获取所有public修饰的方法,包含本类以及父类的
for (Method method : personClass.getMethods()) {
System.out.println(method.getName());
}
// 6. getDeclaredMethods:获取本类中所有方法
for (Method declaredMethod : personClass.getDeclaredMethods()) {
System.out.println(declaredMethod.getName());
}
// 7. getConstructors:获取所有public修饰的构造器,包含本类的
for (Constructor<?> constructor : personClass.getConstructors()) {
System.out.println(constructor.getName());
}
// 8. getDeclaredConstructors:获取本类中所有构造器
for (Constructor<?> declaredConstructor : personClass.getDeclaredConstructors()) {
System.out.println(declaredConstructor.getName());
}
// 9. getPackage:以Package形式返回 包信息
System.out.println(personClass.getPackage());
// 10. getSuperClass:以Class形式返回父类信息
System.out.println(personClass.getSuperclass());
// 11. getInterfaces:以Class[]形式返回接口信息
for (Class<?> anInterface : personClass.getInterfaces()) {
System.out.println(anInterface.getName());
}
// 12. getAnnotations:以Annotation[]形式返回注解信息
for (Annotation annotation : personClass.getAnnotations()) {
System.out.println(annotation);
}
}
@Test
// 第二组:java.lang.reflect.Field类
public void api_02() throws ClassNotFoundException {
//得到Class对象
Class<?> personClass = Class.forName("com.shb.reflection.Person");
for (Field declaredField : personClass.getDeclaredFields()) {
// 1. getModifiers:以int形式返回修饰符(有对应的枚举)
System.out.println(declaredField.getModifiers());
// 2. getType:以Class形式返回类型
System.out.println(declaredField.getType());
// 3. getName:返回属性名
System.out.println(declaredField.getName());
}
}
@Test
// 第三组:java.lang.reflect.Method类
public void api_03() throws ClassNotFoundException {
//得到Class对象
Class<?> personClass = Class.forName("com.shb.reflection.Person");
// 6. getDeclaredMethods:获取本类中所有方法
for (Method declaredMethod : personClass.getDeclaredMethods()) {
// 1. getModifiers:以int形式返回修饰符
System.out.println(declaredMethod.getModifiers());
// 2. getReturnType:以Class形式获取 返回类型
System.out.println(declaredMethod.getReturnType());
// 3. getName:返回方法名
System.out.println(declaredMethod.getName());
// 4. getParameterTypes:以Class[]返回参数类型数组
for (Class<?> parameterType : declaredMethod.getParameterTypes()) {
System.out.println(parameterType.getName());
}
}
}
@Test
// 第四组:java.lang.reflect.Constructor类
public void api_04() throws ClassNotFoundException {
//得到Class对象
Class<?> personClass = Class.forName("com.shb.reflection.Person");
// 8. getDeclaredConstructors:获取本类中所有构造器
for (Constructor<?> declaredConstructor : personClass.getDeclaredConstructors()) {
// 1. getModifiers:以int形式返回修饰符
System.out.println(declaredConstructor.getModifiers());
// 2. getName:返回构造器名(全类名)
System.out.println(declaredConstructor.getName());
// 3. getParameterTypes:以Class[]返回参数类型数组
for (Class<?> parameterType : declaredConstructor.getParameterTypes()) {
System.out.println(parameterType.getName());
}
}
}
}
@Deprecated
class Person implements iA, iB {
public Person() {
}
public Person(String name, int age, String job, double sar) {
this.name = name;
this.age = age;
this.job = job;
this.sar = sar;
}
//属性
public String name;
protected int age;
String job;
private double sar;
//方法
public void m1(String A, Integer B) {
}
private void m2() {
}
protected void m3() {
}
void m4() {
}
}
interface iA {
}
interface iB {
}
反射爆破创建实例
- 通过反射创建对象
- 方式一:调用类中的public修饰的无参构造器
- 方式二:调用类中的指定构造器
- CLass类相关方法
-
- newInstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class...clazz):根据参数列表,获取对应的public 构造器对象
- getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的构造器对象
- Constructor类相关方法
-
- setAccessible:爆破
- newInstance(Object..obj):调用构造器
package com.shb.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* ● 通过反射创建对象
* 1. 方式一:调用类中的public修饰的无参构造器
* 2. 方式二:调用类中的指定构造器
* 3. CLass类相关方法
* a. newInstance:调用类中的无参构造器,获取对应类的对象
* b. getConstructor(Class...clazz):根据参数列表,获取对应的public 构造器对象
* c. getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的构造器对象
* 4. Constructor类相关方法
* a. setAccessible:爆破
* b. newInstance(Object..obj):调用构造器
*/
public class RelectCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1、获取到User对象
Class<?> cls = Class.forName("com.shb.reflection.User");
//2、通过无参构造器创建对象
Object o = cls.newInstance();
System.out.println(o);
//3、通过有参构造器创建对象
/**
* public User(String name) {
* this.name = name;
* }
*/
//先得到构造器,在创建实例
Constructor<?> constructor = cls.getConstructor(String.class);
Object newInstance = constructor.newInstance("宋海波");
System.out.println(newInstance);
//4、通过非public的有参构造器创建对象
Constructor<?> privateConstructor = cls.getDeclaredConstructor(int.class, String.class);
//爆破 使用反射可以访问私有的构造器
privateConstructor.setAccessible(true);
Object newInstance1 = privateConstructor.newInstance(100, "李四");
System.out.println(newInstance1);
}
}
class User {
private int age = 1;
private String name = "小明";
public User() {
}
public User(String name) {
this.name = name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
- 通过反射访问类中的成员
- 根据属性名获取Field对象 Field f=clazz对象.getDeclaredField(属性名)
- 爆破:f.setAccessible(true);//f 是Field
- 访问 f.set(o,值); syso(f.get(o));
- 如果是静态属性,则set和get中的参数o,可以写成null
package com.shb.reflection;
import java.lang.reflect.Field;
/**
* 通过反射访问类中的成员
*/
public class ReflectAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//1、得到Student类对象
Class<?> cls = Class.forName("com.shb.reflection.Student");
//2、创建对象 o运行类型就是Student
Object o = cls.newInstance();
System.out.println(o);
//3、使用反射操作成员变量 name age
Field age = cls.getField("age");
age.set(o, 88);
System.out.println(o);
System.out.println(age.get(o));
//4、使用反射操作name属性 私有的 静态的
Field name = cls.getDeclaredField("name");
//因为name是私有的无法设置值,所以对name进行爆破
name.setAccessible(true);
name.set(o, "宋海波");
//应为name是静态的,所以对象写null也是可以的
name.set(null, "宋海波");
System.out.println(o);
//静态的属性name name.get(o) name.get(null) 都可以的
System.out.println(name.get(o));
}
}
class Student {
private static String name;
public int age;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
通过反射访问类中的成员
- 访问方法
- 根据方法名和参数列表获取Method方法对象:Method m=clazz.getDeclaredMethod(方法名,XX.class);
- 获取对象:Object o=clazz.newInstance();
- 爆破:m.setAccessible(true);
- 访问:Object returnValue=m.invoke(o,实参列表);
- 注意:如果是静态方法,则invoke的参数o,可以写成null;
package com.shb.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射访问类中的成员
* ● 访问方法
*/
public class ReflectAccessMethod {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, ClassNotFoundException, InvocationTargetException {
//1、得到Boss类对应的对象
Class<?> cls = Class.forName("com.shb.reflection.Boss");
//2、创建对象
Object o = cls.newInstance();
//3、调用public hi方法
Method hi = cls.getMethod("hi", String.class);
hi.invoke(o, "晓丽");
//4、调用private static方法say()
Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
//应为say方法是私有的所以需要爆破
say.setAccessible(true);
Object invoke = say.invoke(o, 100, "张三", '男');
System.out.println(invoke);
//因为say方法是static可以传null调用
say.invoke(null, 100, "张三", '男');
}
}
class Boss {
private static String name;
public int age;
public Boss() {
}
private static String say(int n, String s, char c) {
System.out.println(n + " " + s + " " + c);
return n + " " + s + " " + c + " 我是return";
}
public void hi(String s) {
System.out.println("hi~ " + s);
}
@Override
public String toString() {
return "Boss{" +
"age=" + age +
'}';
}
}