JavaSE之类加载与反射

本文详细介绍了Java中类加载的过程及其分类,并深入探讨了双亲委派模型的工作原理。此外,还讲解了如何利用反射机制动态获取类信息及调用类的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类加载器

日常程序员在编写代码时,编写的是java文件,但实际上jvm虚拟机运行的是时编译后的字节码文件。而字节码文件从我们本地硬盘加载到jvm虚拟机的这个过程,称之为类加载。
而加载字节码文件到虚拟机这个过程是由类加载器完成的。
image.png

类加载时机

  • 创建一个类的实例(对象)
  • 调用类的类方法(静态方法)
  • 访问类或者接口的类变量(静态变量),或者为该类变量赋值
  • 反射强制创建某个类或者接口对应的class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令运行某个主类

总结:
用到就加载,不用不加载

类加载过程

image.png

加载
  • 通过一个类的全限定名来获取定义此类的二进制字节流

通过包名+类名,获取这个类,准备用流进行传输

  • 将者字节流所代表的静态存储结构转化为运行时数据结构

将这个类加载到内存中

  • 在内存中生成一个代表这个类的java.lang.Class对象

加载完毕创建一个class对象

  • 任何类被使用时,系统都会为之创建一个java.lang.Class对象

示意图:
image.png

验证
  • 链接阶段的第一步,这一阶段为了确保Class文件字节流中
  • 包含的信息符合虚拟机的要求,并且不会危害虚拟机的自身安全

文件中的信息是否符合虚拟机规范,有没有安全隐患

准备
  • 负责为类的类变量(被static修饰的变量分配内存),并设置默认初始化值

初始化静态变量

解析
  • 将类的二进制数据流中的符号引用替换为直接引用

本类中如果用到了其他类,此时需要找到对应的类
eg:
如果有一个String类型的name,那么这个阶段需要找到String这个类的引用地址,
并指向我们原本代替的临时符号。

初始化
  • 根据程序员通过程序制定的主观计划去初始化类变量和其他资源(静态变量赋值以及初始化其他资源)

类加载器分类

  • 启动类加载器(BootstrapClassLoader):虚拟机内置的类加载器

底层是用c++语言实现,当jvm启动的时候,会自动进行启动

  • 平台类加载器(PlatformClassLoader):负责加载JDK中的一些特殊模块
  • 系统类加载器(SystemClassLoader):负责加载用户类路径上所制定的类库

一般情况下,我们自己写的代码都是由系统类加载器去加载的

不同的类由什么类型的加载器去加载,载Java中已经被规定好了,不需要额外代码去实现

双亲委派模型

image.png

  • 要求除了顶层的启动类加载器之外,其他的加载器都要有自己的父类加载器
  • 这里的父子关系并不是extend的继承,而是逻辑上的继承
  • 当我们使用最下面的类加载器加载一个字节码文件,首先不会自己尝试去加载,而是委派给自己的父类去完成
  • 自定义的会交给系统类加载器去加载
  • 类加载器会委托给平台类加载器去加载
  • 平台类加载器会委托给启动类加载器去加载
  • 当委托到启动类这个顶层的加载器时,不会继续向上委托,所以,所有的加载请求都会传递到启动类加载器中
  • 传递委托也被认为是逻辑上的继承
  • 每个加载器都有自己的加载范围,如果父类加载器无法加载请求时,便会一层一层往下返回
  • 启动类–>平台类—>系统类—>自定义类
  • 此时子加载器才会去尝试自己去加载字节码文件

image.png
代码演示:

/**
 * @author zhangzengxiu
 * @date 2022/12/5
 */
public class ClassLoaderDemo {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        //平台类加载器
        ClassLoader classLoader1 = systemClassLoader.getParent();

        //启动类加载器
        ClassLoader classLoader2 = classLoader1.getParent();
        
        System.out.println("系统类加载器=" + systemClassLoader);
        System.out.println("平台类加载器=" + classLoader1);
        System.out.println("启动类类加载器=" + classLoader2);
    }
}

image.png

类加载器常用方法

方法名说明
public static ClassLoader getSystemClassLoader();获取系统类加载器
public InputStream getResourceAsStream(String name);加载某一个资源文件

代码演示:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author zhangzengxiu
 * @date 2022/12/5
 */
public class ClassLoaderDemo2 {
    public static void main(String[] args) throws IOException {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        
        //利用加载器去获取一个指定的文件
        InputStream is = systemClassLoader.getResourceAsStream("prop.properties");
        Properties properties = new Properties();
        properties.load(is);
    }
}

反射

概述

Java反射机制

  • 运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意对象,都能够调用它的任意属性和方法;

这种动态获取信息以及动态调用对象的功能称为Java语言的反射机制。

动态获取信息以及动态调用,取决于传过来什么类。

利用反射去调用它类中的属性和方法时,无视修饰符
先获取配置文件的信息,动态获取信息并创建对象和调用方法

获取class对象

  • 利用class对象创建
  • 反射创建对象
  • 反射调用方法

说明:
class对象是由类加载器加载编译后的class字节码文件,并在内存中生成一个class对象。
该class对象中记录所有的信息,包括成员变量信息、静态变量信息等。

源码阶段:Class.forName(“全类名”)
class对象阶段:类名.class
Runtime运行时阶段:对象.getClass()
代码演示:

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        System.out.println(clazz);

        Class clazz2 = Stu.class;
        System.out.println(clazz2);

        Stu stu = new Stu();
        Class clazz3 = stu.getClass();
        System.out.println(clazz3);

    }
}

获取Constructor对象

标准java类

package com.test.springcloud.domain;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Stu {

    private String name;

    private int age;

    private Stu(String name){
        System.out.println(name);
    }

    public Stu() {
        System.out.println("public stu Constructor");
    }

    public Stu(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

获取构造方法:
getConstructors能获取到所有公共的构造方法
代码演示:

import java.lang.reflect.Constructor;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        /*
            获取所有公共构造方法对象的数组
            Constructor<?>[] getConstructors()
         */
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

image.png
getDeclaredConstructors能获取所有构造方法对象的数组
代码演示:

import java.lang.reflect.Constructor;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        /*
            获取所有构造方法对象的数组
            Constructor<?>[] getDeclaredConstructors()
         */
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

image.png
getConstructor根据传入的类型返回指定的构造器(只能获取到public修饰的)
代码演示:

import com.test.springcloud.domain.Stu;
import java.lang.reflect.Constructor;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        /*
            获取单个构造方法对象
            Constructor<?>[] getConstructor()
         */
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor constructor = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor);

        Constructor constructor1 = clazz.getConstructor();
        System.out.println(constructor1);
    }
}

image.png

如果根据传入的参数找不到对应的处理器则会报错:没有这样的构造

image.png
getDeclaredConstructor可以获取到任意修饰符修饰的构造方法
代码演示:

import java.lang.reflect.Constructor;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        /*
            获取单个构造方法对象
            Constructor<?>[] getDeclaredConstructor()
         */
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        System.out.println(constructor);
    }
}

image.png
总结:

  • Constructor<?>[] getConstructors() :返回所有公共构造方法对象的数组
  • Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
  • Constructor getConstructor(Class<?>… parameterTypes):返回单个公共构造方法对象
  • Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回单个(任意)构造方法对象

利用Constructor创建对象

利用newInstance创建对象:
代码演示:

import java.lang.reflect.Constructor;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor constructor = clazz.getConstructor(String.class, int.class);
        //利用newInstance创建对象
        Stu stu = (Stu) constructor.newInstance("zhangsan", 23);
        System.out.println(stu);
    }
}

class类中有个newInstance方法,简写格式

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Stu stu = (Stu)clazz.newInstance();
        System.out.println(stu);
    }
}

获取私有构造,并创建对象

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        Stu stu = (Stu) constructor.newInstance("zhangsan");
        System.out.println(stu);
    }
}

image.png

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.Stu");
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
        //被private修饰的成员,不能直接被使用,需要临时取消访问检查
        constructor.setAccessible(true);
        Stu stu = (Stu) constructor.newInstance("zhangsan");
        System.out.println(stu);
    }
}

总结:

  • 获取class对象

三种方式—Class.forName(“全类名”)

  • 获取构造方法对象

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

  • 如果是public修饰的,直接创建对象

T newInstance(Object … initargs)

  • 如果是非public,需要临时取消访问检查,然后再创建对象

constructor.setAccessible(true) 暴力反射

获取Field成员变量对象

  • Field[] getFields():返回所有public公共的成员变量对象数组
  • Field[] getDeclaredFields() :返回所有成员变量对象的数组,包括所有权限
  • Field getField(String name):返回单个公共成员变量对象
  • Field getDeclaredField(String name):返回单个成员变量对象,包括所有权限
public class User {

    public String name;

    public int age;

    public String gender;

    private int money = 100;

}

Field[] getFields():返回所有public公共的成员变量对象数组
代码演示:


import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取所有public公共的成员变量对象数组
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

    }
}

image.png
Field[] getDeclaredFields() :返回所有成员变量对象的数组,包括所有权限
代码演示:

import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取所有成员变量对象的数组,包括所有权限
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}

image.png
Field getField(String name):返回单个公共成员变量对象
代码演示:

import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取单个公共成员变量对象
        Field field = clazz.getField("name");
        System.out.println(field);
    }
}

image.png
如果获取的成员变量不存在,则会报错:
image.png

注意点:
Field getField(String name)这个方法,想要获取到的变量必须是真实存在的,且必须是public修饰的

Field getDeclaredField(String name):返回单个成员变量对象,包括所有权限
代码演示:

import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取单个成员变量对象,无需考虑修饰符
        Field field = clazz.getDeclaredField("money");
        System.out.println(field);
    }
}

image.png

  • set(Object obj, Object value):给指定对象的成员变量赋值
  • get(Object obj):返回指定对象的Field的值

set(Object obj, Object value):给指定对象的成员变量赋值
代码演示:


import com.test.springcloud.domain.User;

import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取单个成员变量对象,无需考虑修饰符
        Field field = clazz.getField("name");
        //反射空参创建对象
        User user = (User) clazz.newInstance();
        //赋值
        field.set(user, "zhangsan");
        System.out.println(user);
    }
}

image.png
get(Object obj):返回指定对象的Field的值
代码演示:

import com.test.springcloud.domain.User;

import java.lang.reflect.Field;

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //获取单个成员变量对象,无需考虑修饰符
        Field field = clazz.getDeclaredField("money");
        //私有的必须取消访问检查
        field.setAccessible(true);
        //反射空参创建对象
        User user = (User) clazz.newInstance();
        //获取值
        Object obj = field.get(user);
        System.out.println(obj);
    }
}

image.png

获取Method方法对象

  • Method[] getMethods():返回所有公共成员方法对象数组,包含继承的
  • Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
  • Method getMethod(String name, Class<?>… parameterTypes):返回单个公共成员方法对象
  • Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回单个成员方法对象

代码演示:

package com.test.springcloud.domain;

/**
 * @author zhangzengxiu
 * @date 2022/12/7
 */
public class User {

    /**
     * 私有无参无返回
     */
    private void method1() {
        System.out.println("私有 无参 无返回");
    }

    /**
     * 公共有参无返回
     *
     * @param name
     */
    public void method2(String name) {
        System.out.println("公共 有参 无返回,参数=" + name);
    }

    /**
     * 公共无参有返回
     *
     * @return
     */
    public String method3() {
        System.out.println("公共 无参 有返回");
        return "qqq";
    }

    /**
     * 公共有参有返回
     *
     * @param name
     * @return
     */
    public String method4(String name) {
        System.out.println("公共 有参 有返回,参数=" + name);
        return "qqq";
    }
}

Method[] getMethods():返回所有公共成员方法对象数组,包含继承的
代码演示:

import java.lang.reflect.Method;


/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //返回所有公共成员方法对象数组,包含从父类继承的
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

image.png
Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //返回所有成员方法对象的数组,不包括继承的
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

image.png
Method getMethod(String name, Class<?>… parameterTypes):返回单个公共成员方法对象
代码演示:

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //返回单个公共成员方法对象
        Method method1 = clazz.getMethod("method0");
        System.out.println(method1);
    }
}

image.png
Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回单个成员方法对象
代码演示:

import java.lang.reflect.Method;


/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        //返回单个成员方法对象
        Method method1 = clazz.getDeclaredMethod("method3", String.class);
        System.out.println(method1);
    }
}

image.png

  • Object invoke(Object obj, Object… args):运行方法

参数解释:
obj:用obj对象调用该方法
args:调用方法的传递的参数(没有就不写)
返回值:方法的返回值(没有就不写)

代码演示:

/**
 * @author zhangzengxiu
 * @date 2022/12/6
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.test.springcloud.domain.User");
        Method method1 = clazz.getDeclaredMethod("method5", String.class);
        User user = (User) clazz.newInstance();
        //user是调用的对象
        //第二个是传入的参数
        Object res = method1.invoke(user, "zhangsan");
        System.out.println(res);
    }
}

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值