Java-反射

10 篇文章 0 订阅
9 篇文章 0 订阅

1 类加载

image-20221022112026823

1.1 过程

  • 加载
    通过类加载器(ClassLoader)加载.class文件,读取到内存
    在这个过程中,生成这个类所对应的字节码文件对象(java.lang.Class)

  • 链接

    • 验证: 对字节码文件格式的验证(aced babe 咖啡宝贝 魔法数字)
    • 准备: 给类的静态成员分配内存并赋予默认初始值
    • 解析: 把符号引用(用一组符号来描述被引用的目标)转化为直接引用(真实的地址)
  • 初始化
    给静态成员赋真实的值, 并且执行静态代码块中的内容

1.2 类加载器

1.2.1 分类

  • Bootstrap ClassLoader 根类加载器
    负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar

  • Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录

  • Sysetm(App) ClassLoader 系统类加载器/应用加载器
    负责加载自己定义的Java类

1.2.2 查看类加载器的方法

  • 系统类加载器
 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
 System.out.println(systemClassLoader);
  • 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
  • 根类加载器
//null 不是java写的
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
  • 查看加载路径
System.out.println(System.getProperty("java.class.path")  .replace(";", System.lineSeparator()));
System.out.println(System.getProperty("java.ext.dirs")  .replace(";", System.lineSeparator()));

1.2.3 双亲委派模型

Java的双亲委派模型是一种类加载机制,用于确保Java类的安全性和一致性。这一模型是通过层次结构的类加载器来实现的,主要分为以下几个层次:

  1. Bootstrap Class Loader(启动类加载器): 这是Java虚拟机的最顶层的加载器,它负责加载Java的核心库,通常是JVM自带的rt.jar等。由于它是用C++语言实现的,无法在Java中直接获得引用。

  2. Extension Class Loader(扩展类加载器): 扩展类加载器是用来加载Java的扩展库,通常位于jre/lib/ext目录下。它是sun.misc.Launcher$ExtClassLoader类的实例。

  3. Application Class Loader(应用程序类加载器): 也叫系统类加载器,是用来加载应用程序的类路径上的类,即classpath。它是sun.misc.Launcher$AppClassLoader类的实例。

这三个加载器构成了双亲委派模型的层次结构。其核心思想是,当一个类加载请求到达某个加载器时,该加载器首先检查是否能够加载这个类。如果能够加载,就加载完成;如果不能加载,加载器就会把请求委托给它的父加载器。这个过程一直递归进行,直到达到最顶层的启动类加载器。

双亲委派模型的工作过程:

  1. 当应用程序试图加载一个类时,应用类加载器会首先检查它已加载的类是否包含这个类。如果包含,则直接返回这个类的Class对象。
  2. 如果应用类加载器不能加载这个类,它就会委托给扩展类加载器,让扩展类加载器尝试加载。
  3. 扩展类加载器也是同样的过程,首先检查自己已加载的类,如果找到了就返回。如果没有找到,就委托给启动类加载器。
  4. 启动类加载器尝试加载,如果它找到了这个类,加载完成。如果仍然没有找到,就会抛出ClassNotFoundException

这种层次结构的加载机制保证了类的一致性,即使是在不同的应用程序中,也可以确保同名的类是相同的。这同时提高了Java平台的安全性,因为核心库是由启动类加载器加载的,避免了恶意类库的注入。

总体来说,双亲委派模型是Java安全性和一致性的基石,确保了类的正确加载和防止类冲突。
![image-20221024094028570]](https://img-blog.csdnimg.cn/5de3d826ad3a49fa91190a7fd6c83dd1.png#pic_center)

1.3 类加载时机

创建类的实例(首次创建该类对象)

访问类的静态变量(首次)

调用类的静态方法(首次)

加载某个类的子类,会先触发父类的加载

直接使用java.exe命令来运行某个主类,也就是执行了某个类的main()方法

使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

2 Java代码的3个阶段

image-20221024095115934

3 反射

3.1 定义

获取运行时类信息的一种手段

反射的起点是字节码文件对象

3.2 获取字节码文件对象的几种方式

  • 对象.getClass()
  • 类名.class
  • Class.forName(String className) 全限定名
  • ClassLoader里的loadClass(String className)

注意:

无论通过什么方式获取的字节码文件对象 都是同一个

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        // - 对象.getClass()
        Student student = new Student();
        Class c1 = student.getClass();
        //- 类名.class
        Class c2 = Student.class;

        System.out.println(c1 == c2);

        //- Class.forName(String className)  全限定名
        Class c3 = Class.forName("day22_reflect.com.cskayan._02cls.Student");
        System.out.println(c1 == c3);

        //- ClassLoader里的loadClass(String className)
        Class c4 = ClassLoader.getSystemClassLoader().loadClass("day22_reflect.com.cskayan._02cls.Student");
        System.out.println(c1 == c4);
    }
}
class Student{

}

注意:

/**
 * @package: _01code._03reflect._02class
 * @description:
 * @author: Yunyang
 * @date: 2023/11/7  16:17
 * @version:1.0
 **/
/*
类加载时机:
1.首次new对象
2.执行main
3.访问类中的静态成员(静态成员变量 静态成员方法)
4.加载子类先进行父类加载
5.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
 */
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException {
        
        // Class.forName()  完整的类加载
        // 执行静态代码块
        Class<?> c1 = Class.forName("_01code._03reflect._02class.AA");


        // 类名.class 不完整的类加载
        // 没有执行静态代码块
        Class c2 = A.class;

    }

}
class AA{
    static {
        System.out.println("AA static block");
    }
}

3.3 关于Class

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
D:\Desktop\baiduyunTemp\Snipaste-1.16.2-x64\history\5K98WJ

3.4 配置文件

  • 配置文件的几种格式:.properties .xml .yml
  • 配置文件的作用: 放配置信息的 (数据库的, 第三方服务的配置信息)
  • .properties文件的格式
    • 键值对(key-value) key=value
    • key是不能重复的
    • 注释是#
    • 文件里面全是String
    • IDEA创建:src文件夹右击——>New——>Resoure Bundle
      image-20221024101640808

获取配置文件信息

Properties类

  • Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

  • 构造方法

    • Properties() 创建一个无默认值的空属性列表
  • 成员方法

    • load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
    • load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
    • getProperty(String key) 用指定的键在此属性列表中搜索属性。
  • 步骤

    • 创建Properties对象: Properties properties = new Properties();
    • Load:load(InputStream in)/load(Reader in) properties.load(in);
    • 获取属性值:getProperty(String key) String port = properties.getProperty("port");
/*
获取配置文件中的信息
 */
public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建properties对象
        Properties properties = new Properties();

        // load(InputStream inStream)
        // 输入流中读取属性列表(键和元素对)。
        properties.load(new FileInputStream("_01code/config.properties"));

        // String getProperty(String key)
        // 用指定的键在此属性列表中搜索属性。
        String port = properties.getProperty("port");
        String host = properties.getProperty("host");
        String user = properties.getProperty("username");
        String password = properties.getProperty("password");
        System.out.println("port = " + port);
        System.out.println("user = " + user);
        System.out.println("password = " + password);
        System.out.println("host = " + host);

    }
}

3.5 通过反射获取构造方法(Constructor)

通过反射获取所有构造方法

Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()

获取指定构造方法

Constructor getConstructor(Class<?>... parameterTypes) Constructor getDeclaredConstructor(Class<?>… parameterTypes)

使用Constructor创建对象

Person p = new Person(“zs”,20,true)
newInstance(参数列表)

暴力破解

setAccessible(true)

/**
 * @package: _01code._03reflect._04api._01constructor
 * @description:通过反射获取构造方法(Constructor)
 * @author: Yunyang
 * @date: 2023/11/7  17:19
 * @version:1.0
 **/
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //反射的起点是字节码文件对象
        // 获取字节码文件对象
        Class<?> c = Class.forName("_01code._03reflect._04api.bean.Person");

        System.out.println("获取所有public的构造方法--------");

        Constructor<?>[] constructors = c.getConstructors();

        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }

        System.out.println("获取所有的构造方法----------");
        Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        System.out.println("获取指定的public的构造方法---------");
        Constructor<?> constructor = c.getConstructor(String.class, int.class, boolean.class);
        // Constructor<?> constructor = c.getConstructor(String.class);
        // java.lang.NoSuchMethodException
        System.out.println(constructor);

        System.out.println("获取指定的构造方法-----------");
        Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor);

        System.out.println("使用构造方法创建对象----------");
        // newinstance
        Object o = constructor.newInstance("zs", 20, true);
        System.out.println(o);

        // java.lang.IllegalAccessException
        declaredConstructor.setAccessible(true);
        Object o1 = declaredConstructor.newInstance("ls", 21);
        System.out.println(o1);

    }
}

3.6 通过反射获取成员变量(Field)

通过反射获取所有成员变量

Field[] getFields()
Field[] getDeclaredFields()

获取指定成员变量

Field getField(String name)
Field getDeclaredField(String name)

通过Field读写对象的成员变量(可暴力破解)

Object get(Object obj):获取值,传入对象
void set(Object obj, Object value):赋值,传入对象

/**
 * @package: _01code._03reflect._04api._01constructor
 * @description:通过反射获取成员变量(Field)
 * @author: Yunyang
 * @date: 2023/11/7  17:56
 * @version:1.0
 **/
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 获取字节码文件对象
        Class<?> c2 = Class.forName("_01code._03reflect._04api.bean.Person");
        System.out.println("获取所有public的成员变量----------------");
        // Field[] getFields()
        Field[] fields = c2.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //Field[] getDeclaredFields()
        System.out.println("获取所有的成员变量--------------------");
        Field[] declaredFields = c2.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        // Field getField(String name)
        System.out.println("获取指定的public成员变量-------------------");
        Field nameField = c2.getField("name");
        System.out.println(nameField);

        //Field getDeclaredField(String name)
        System.out.println("获取指定的成员变量---------------");
        Field ageFeild = c2.getDeclaredField("age");
        System.out.println(ageFeild);

        System.out.println("成员变量赋值--------------");
        //给成员变量赋值 获取成员变量的值
        //void set(Object obj, Object value):赋值,传入对象
        Constructor<?> declaredConstructor =
                c2.getDeclaredConstructor();

        // 实例化对象
        Object o = declaredConstructor.newInstance();
        nameField.set(o,"zs");
        System.out.println(o);

        // java.lang.IllegalAccessException
        ageFeild.setAccessible(true);
        ageFeild.set(o,22);
        System.out.println(o);

        System.out.println("获取成员变量的值------------");
        // Object get(Object obj):获取值,传入对象
        Object o1 = nameField.get(o);
        System.out.println(o1);
    }
}

3.7 通过反射获取成员方法(Method)

获取所有成员方法

Method[] getMethods()// 父类的也能获取到
Method[] getDeclaredMethods()

获取指定的成员方法

Method getMethod(String name, Class<?>... parameterTypes) Method getDeclaredMethod(String name, Class<?>… parameterTypes)

利用Method调用对象的方法

Object invoke(Object obj, Object… args)

/**
 * @package: _01code._03reflect._04api._03method
 * @description:通过反射获取成员方法(Method)
 * @author: Yunyang
 * @date: 2023/11/7  20:05
 * @version:1.0
 **/
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 获取字节码文件对象
        Class<?> c3 = Class.forName("_01code._03reflect._04api.bean.Person");

        System.out.println("获取所有public方法---------------");
        // Method[] getMethods()
        Method[] methods = c3.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }


        System.out.println("获取所有的方法-------------------");
        //Method[] getDeclaredMethods()
        Method[] declaredMethods = c3.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }


        System.out.println("获取指定的public方法--------------");
        Method eatMethod1 = c3.getMethod("eat");
        System.out.println(eatMethod1);


        System.out.println("获取指定的方法-----------------");
        //Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        Method eatMethod2 = c3.getDeclaredMethod("eat", String.class);
        System.out.println(eatMethod2);

        // Person p = new Person();
        // p.eat();


        System.out.println("反射调用方法-----------------------");
        // 反射调用方法
        // Object invoke(Object obj, Object... args)
        Constructor<?> declaredConstructor = c3.getDeclaredConstructor();
        Object o = declaredConstructor.newInstance();
        Object invoke = eatMethod1.invoke(o);
        System.out.println(invoke);

        eatMethod2.setAccessible(true);
        eatMethod2.invoke(o,"Apple");
    }
}

4 补充

4.1 其它API

可以通过Class直接实例化 , 但是要有一个无参构造方法

/**
 * @package: _01code._03reflect._04api._04add
 * @description:可以通过Class直接实例化 , 但是要有一个无参构造方法
 * @author: Yunyang
 * @date: 2023/11/7  20:20
 * @version:1.0
 **/
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> c4 = Class.forName("_01code._03reflect._04api.bean.Person");

        // 通过class对象直接实例化对象
        Object o = c4.newInstance();
        System.out.println(o);
    }

}

class A{
    int a;

    public A(int a) {
        this.a = a;
    }

    public A() {
    }
}
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        // 获取字节码文件对象
        Class<?> c = Class.forName("_01code._03reflect._04api.bean.Person");


        // 获取全限定类名
        // _01code._03reflect._04api.bean.Person
        System.out.println(c.getName());

        // 获取简单名称
        // Person
        System.out.println(c.getSimpleName());

        // 获取父类
        Class<?> superclass = c.getSuperclass();
        System.out.println("superclass = " + superclass);


        // 获取实现的接口
        Class<?>[] interfaces = c.getInterfaces();
        for (Class<?> i : interfaces) {
            System.out.println(i);
        }

        // 获取类加载器
        ClassLoader classLoader = c.getClassLoader();
        System.out.println("classLoader = " + classLoader);


        // 获取name这个成员变量对象
        Field nameField = c.getDeclaredField("name");

        // 获取权限修饰符
        int modifiers = nameField.getModifiers();
        System.out.println(modifiers);

        // static String toString(int mod)
        // 返回描述指定修饰符中的访问修饰符标志的字符串
        // String s =  Modifier.toString(modifiers);


        // 获取eat(String s)方法对象
        Method eatMethod = c.getDeclaredMethod("eat", String.class);
        Class<?> returnType = eatMethod.getReturnType();
        System.out.println("returnType = " + returnType);
        Class<?>[] parameterTypes = eatMethod.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType.getSimpleName());
        }

    }
}

4.2 自定义类加载器

  • 继承ClassLoader
  • 重写findClass方法
public class MyClassLoader extends ClassLoader {

    private String path;

    public MyClassLoader(String path) {
        this.path = path;
    }

    //重写findClass方法

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        // 读取.class文件的内容
        byte[] data = new byte[0];
        try {
            data = getData();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 想要得到class对象,利用defineClass方法
        // protected  Class<?> defineClass(String name, byte[] b, int off, int len)
        //将一个 byte 数组转换为 Class 类的实例。
        defineClass(name,data,0,data.length);

        // 最终执行完,要返回Class对象
        return super.findClass(name);
    }

    private byte[] getData() throws IOException {
        //读取.class文件的内容
        File file = new File(path);
        long length = file.length();
        byte[] bytes = new byte[(int) length];
        //创建字节输入流
        FileInputStream in = new FileInputStream(file);
        in.read(bytes);

        return bytes;


    }
}
public class MyClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        String path = "D:\\wangdao_Java55\\55th-homework\\out\\production\\55th-homework\\_01code\\_03reflect\\_04api\\_04add\\Log.class";

        MyClassLoader myClassLoader = new MyClassLoader(path);

        Class<?> c = myClassLoader.loadClass("Log");

        Method funcMethod = c.getDeclaredMethod("func");

        Object o = c.newInstance();

        funcMethod.invoke(o);

    }
}

5 反射应用场景

通过反射获取注解信息

动态代理

ORM(Object Relational Mapping)框架, 数据库框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值