侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计收到 5 条评论

Chapter23_反射

bbchen
2023-02-26 / 0 评论 / 40 阅读 / 正在检测是否收录...

反射(Reflection)

反射机制

  1. 反射机制允许程序再执行期间 借助于 Reflection API 取得任何类的内部消息(比如成员变量、构造器、成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
  2. 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个Class 对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射

Java反射机制原理图

image-20221015145729643

反射机制可以完成

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时得到任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

反射相关的主要类

  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法
  3. java.lang.reflect.Field:代表类的成员变量
  4. java.lang.reflect.Cdnstructor:代表类的构造方法

Class 类

  1. Class 类也是类,因此也继承Object类
  2. Class 类对象不是 new 出来的,而是系统创建的

    image-20221102204142463

  3. 对于某个类的Class 类对象,而内存中只有一份,因此类只加载一次
  4. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  5. 通过 Class 可以完整地得到一个类的完整结构,通过一系列 API
  6. Class 对象是存放在堆的
  7. 类的字节码二进制数据,是存放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等等)

常用方法

image-20221102204907011

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 对象的六种方式

  1. 前提:已知一个类的全类名,且该类在类路径下,可以通过 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 对象

image-20221102220037287

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 实现动态语言的关键,也就是通过反射实现类动态加载

  1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
  2. 动态加载:运行时夹杂i需要的累,如果运行时不用该类,则不报错,降低了依赖性

类加载过程图

image-20221103112824684

加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络) 转化位二进制数据加载到内存中,并生成一个代表该类的 java.lang.Class 对象

链接阶段

-验证

image-20221103122243410

-准备

image-20221103122335384

-解析

虚拟机将常量池内的符号引用替换为直接引用的过程

初始化

image-20221103123953084

获取类的结构信息

image-20221103135654179

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() {

    }
}

image-20221103135710985

image-20221103135718832

image-20221103135727156

反射爆破

创建实例

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);
    }
}
0

评论

博主关闭了所有页面的评论