反射(Reflection)
反射机制
- 反射机制允许程序再执行期间 借助于 Reflection API 取得任何类的内部消息(比如成员变量、构造器、成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
- 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个Class 对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射
Java反射机制原理图
反射机制可以完成
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关的主要类
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Cdnstructor:代表类的构造方法
Class 类
- Class 类也是类,因此也继承Object类
Class 类对象不是 new 出来的,而是系统创建的
- 对于某个类的Class 类对象,而内存中只有一份,因此类只加载一次
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类的完整结构,通过一系列 API
- Class 对象是存放在堆的
- 类的字节码二进制数据,是存放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等等)
常用方法
package com.bbedu.reflection.class_;
import com.bbedu.Car;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* Class类的常用方法
*/
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
String classAllPath = "com.bbedu.Car";
// <?> 表示不确定的Java类型
Class<?> cls = Class.forName(classAllPath);
// 输出cls
System.out.println(cls); // 显示cls对象是哪个类的Class对象 class com.bbedu.Car
System.out.println(cls.getClass()); // class java.lang.Class
// 得到包名
System.out.println(cls.getPackage().getName()); // com.bbedu
// 得到全类名
System.out.println(cls.getName()); // com.bbedu.Car
// 通过cls创建对象实例
Car car = (Car) cls.getDeclaredConstructor().newInstance();
System.out.println(car);
// 通过反射获取属性
Field brand = cls.getField("brand");
System.out.println(brand.get(car));
// 给属性赋值
brand.set(car, "benz");
System.out.println(brand.get(car));
// 获取所有字段
System.out.println("=======所有字段属性========");
Field[] fields = cls.getFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i].getName());
}
}
}
获取 Class 对象的六种方式
前提:已知一个类的全类名,且该类在类路径下,可以通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException
应用场景:多用于配置文件,读取类全路径,加载类
package com.bbedu.reflection.class_;
import com.bbedu.Car;
/**
* 获取 Class 对象的各种方式(6)
*/
public class GetClass_ {
public static void main(String[] args) throws ClassNotFoundException {
// 1.Class.forName
String classAllPath = "com.bbedu.Car";
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
// 2.类名.class
Class<Car> cls2 = Car.class;
System.out.println(cls2);
// 3.对象.getClass()
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
// 4.通过类加载器[4种]
ClassLoader classLoader = car.getClass().getClassLoader();
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
// 5.基本数据类型
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);
// 6.基本数据类型对应的包装类
Class<Integer> type = Integer.TYPE;
System.out.println(type);
Class<Character> type1 = Character.TYPE;
System.out.println(type1);
System.out.println(integerClass.hashCode());
System.out.println(type.hashCode());
}
}
哪些类型有 Class 对象
package com.bbedu.reflection.class_;
import java.io.Serializable;
/**
* 那些类型有 Class 对象
*/
public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;
Class<Serializable> cls2 = Serializable.class;
Class<Integer[]> cls3 = Integer[].class;
Class<Integer[][]> cls4 = Integer[][].class;
Class<Deprecated> cls5 = Deprecated.class;
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = long.class;
Class<Void> cls8 = void.class;
Class<Class> cls9 = Class.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
类加载
反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时夹杂i需要的累,如果运行时不用该类,则不报错,降低了依赖性
类加载过程图
加载阶段
JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络) 转化位二进制数据加载到内存中,并生成一个代表该类的 java.lang.Class 对象
链接阶段
-验证
-准备
-解析
虚拟机将常量池内的符号引用替换为直接引用的过程
初始化
获取类的结构信息
package com.bbedu.reflection;
import org.junit.jupiter.api.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 通过反射获取类的结构信息
*/
public class ReflectionUtils {
public static void main(String[] args) {
}
// 第一组
@Test
public void api_01() throws ClassNotFoundException {
Class<?> personCls = Class.forName("com.bbedu.reflection.Person");
// 全类名 com.bbedu.reflection.Person
System.out.println(personCls.getName());
// 简单类名 Person
System.out.println(personCls.getSimpleName());
// 获取所有public属性,包含本类和父类
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println("本类及父类的属性:" + field.getName());
}
// 所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类所有属性:" + declaredField.getName());
}
// 本类及父类的public方法
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println("本类及父类的方法:" + method.getName());
}
// 本类所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类所有方法:" + declaredMethod.getName());
}
// 获取本类的public构造器
/**
* Returns:
* the array of Constructor objects representing the public constructors of this class
*/
Constructor<?>[] constructors = personCls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("本类的构造器:" + constructor.getName());
}
// 获取所有构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("本类所有构造器:" + declaredConstructor.getName());
}
// 获取包信息
System.out.println(personCls.getPackage());
// 返回父类信息
Class<?> superclass = personCls.getSuperclass();
System.out.println(superclass);
System.out.println(superclass.getSuperclass());
// 得到接口信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("接口信息" + anInterface);
}
// 注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息" + annotation);
}
}
}
class A {
public String hobby;
public A() {
}
public void hi() {
}
}
interface IA {
}
interface IB {
}
@Deprecated()
class Person extends A implements IA, IB {
// 属性
public String name;
protected int age;
String job;
private double sal;
public Person() {
}
public Person(String name) {
}
private Person(String name, int age) {
}
// 方法
public void m1() {
}
protected void m2() {
}
void m3() {
}
private void m4() {
}
}
反射爆破
创建实例
package com.bbedu.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectionCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 获取Class对象
Class<?> cls = Class.forName("com.bbedu.reflection.User");
// 1.通过public的无参构造器创建实例
Object o = cls.newInstance();
System.out.println(o);
// 2.通过public的有参构造器创建实例
// 2.1 得到构造器
Constructor<?> constructor = cls.getConstructor(String.class);
// 2.2 创建实例
Object o1 = constructor.newInstance("hello");
System.out.println(o1);
// 私有构造器
/*Returns: the Constructor object of the public constructor that matches the specified parameterTypes*/
// Constructor<?> constructor1 = cls.getConstructor(int.class, String.class);
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
// 暴破(暴力破解),使用反射可以访问private构造器
// 反射面前,都是纸老虎
declaredConstructor.setAccessible(true);
Object emmm = declaredConstructor.newInstance(20, "emmm");
System.out.println(emmm);
}
}
class User {
private int age = 10;
private String name = "bbchen";
public User() {
}
public User(String name) {
this.name = name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
操作属性
package com.bbedu.reflection;
import java.lang.reflect.Field;
/**
* 反射操作属性
*/
public class ReflectionAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> cls = Class.forName("com.bbedu.reflection.Student");
Object o = cls.newInstance();
System.out.println(o.getClass());
Field age = cls.getField("age");
System.out.println(o);
age.set(o, 99);
System.out.println(o);
System.out.println(age.get(o));
Field name = cls.getDeclaredField("name");
// 暴破
name.setAccessible(true);
// name.set(o, "Siri");
// static 属性,因此可以写成null
name.set(null, "Siri");
System.out.println(name.get(o));
System.out.println(name.get(o));
}
}
class Student {
public int age;
private static String name;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name=" + name +
'}';
}
}
操作方法
package com.bbedu.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 反射调用方法
*/
public class ReflectionAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> cls = Class.forName("com.bbedu.reflection.Boss");
Object o = cls.newInstance();
// Method hi = cls.getMethod("hi", String.class);
Method hi = cls.getDeclaredMethod("hi", String.class);
hi.invoke(o, "bbchen");
Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
// 暴破
say.setAccessible(true);
System.out.println(say.invoke(o, 123, "hello", 'm'));
// 静态方法还可以这样
System.out.println(say.invoke(null, 123, "hello", 'm'));
Object invoke = say.invoke(o, 123, "hello", 'm');
System.out.println(invoke.getClass());
System.out.println(invoke);
}
}
class Boss {
public int age;
private static String name;
public Boss() {
}
private static String say(int n, String s, char c) {
return n + " " + s + " " + c;
}
public void hi(String s) {
System.out.println("hi " + s);
}
}
评论