Java ClassLoader类加载器基础详解

 更新时间:2023年09月11日 17:30:01   作者:Tinyspot  
这篇文章主要为大家介绍了Java ClassLoader类加载器基础详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1. 类加载

  • JVM 首次使用某个类时,需通过 ClassPath 查找该类的 .class 文件
  • 将 .class 文件中对类的描述信息加载到内存中,进行保存
  • 加载时机
    • 创建对象
    • 创建子类对象
    • 访问静态属性
    • 调用静态方法
    • 主动加载:Class.forName("full-name")

1.1 class 文件

包名、类名、父类、属性、方法、构造方法.....

2. 类加载器

  • 在运行期间,如果我们要产生某个类的对象,JVM 会检测该类型的 Class 对象是否已被加载; 如果没有加载,JVM 会根据类的名称找到 .class 文件并加载它
  • Class 对象代表 Java 应用程序在运行时所加载的类或接口实例,每加载一个类,JVM自动生成一个Class对象

2.1 ClassLoader的分类

  • Bootstrap ClassLoader 启动类加载器(引导类加载器)
  • ExtClassLoader 扩展类加载器(Java9 之后改为 Platform Classloader)
  • Application Classloader(系统类加载器或应用类加载器)默认的类加载器
  • 自定义类加载器,父类加载器为AppClassLoader

2.2 ClassLoader 层次结构

  • 系统类加载器 --父--> 扩展类加载器 --父--> 引导类加载器
  • 除了引导类加载器之外,所有的类加载器都有一个父类加载器。 通过 getParent()方法可以得到
  • 注意:父加载器不是父类

2.3 类与类加载器

  • 在JVM中表示两个class对象是否为同一个类对象的两个必要条件
    • 类的全限定名必须一致
    • 加载这个类的ClassLoader必须相同
  • 在JVM中,即使这个两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的

2.3 获取 ClassLoader

@Test
public void getClassLoader() {
    Class<?> clazz = String.class;
    ClassLoader classLoader = clazz.getClassLoader();
    System.out.println(classLoader);
    // null, 根加载器并不是由Java语言实现的,因此拿不到根加载器对象
}
@Test
public void getClassLoader2() throws ClassNotFoundException {
    // this.getClass().getClassLoader();
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Class<?> clazz = classLoader.loadClass("com.example.concrete.common.domain.User");
    System.out.println(classLoader);
    System.out.println(classLoader.getParent());
}

打印结果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@51efea79

2.4 获得class对象的三种方法

方式一:对象.getClass()

String str = "hello";
Class<?> clazz = str.getClass();

方式二:类.class
Class<?> clazz = String.class;

方式三:Class. forName() 动态加载类

// 静态方法 forName("类的全限定名")
Class<?> clazz3 = Class.forName("java.lang.String");

3. ClassLoader 分析

loadClass(String name)

findClass(String name)

defineClass(String name, byte[] b, int off, int len)

// ClassLoader 的默认实现就是双亲委托
public abstract class ClassLoader {
  //每个类加载器都有个父加载器
  private final ClassLoader parent;
  public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
  }
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
  }
  protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
    return defineClass(name, b, off, len, null);
  }
}

name: 类的全限定名

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
/**
 * classpath 就是一组目录的集合;classpath 是 JVM 用到的一个环境变量,它用来指示 JVM 如何搜索class
 * 在启动 JVM 时设置 classpath 变量
 * java -cp  or  java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello
 * 如果不设置,JVM 默认的 classpath 为. 即当前目录
 */
String classpath = System.getProperty("java.class.path");
Properties properties = System.getProperties();
Set<String> strings = properties.stringPropertyNames();
for (String string : strings) {
    System.out.println(string);
}
/**
 * 若以“/”开头的,表示要从项目的 ClassPath 开始的( /mapper/xxx.xml ),
 * 如果前面没有这个“/”,那么表示的就是相对于该类的路径继续往下
 */
URL resource = classLoader.getResource("");  // file:/.../target/classes/
URL resource1 = classLoader.getResource("bean.xml");  // file:/.../target/classes/bean.xml

4. Classpath

  • lib 和 classes 同属 classpath,访问优先级为: lib > classes
  • Java 项目 /src 目录下的文件(*.xml, *.properties)编译后会放到 WEB-INF/classes 目录,默认的 classpath 就是 WEB-INF/classes
  • WEB-INF/ 是资源目录,客户端不能直接访问
  • Maven 项目 resources 目录下的文件编译后在 BOOT-INF/classes

4.1 maven 项目 classpath 路径

Maven 项目目录

src
  |-- main
    |-- java
      |-- com.xxx
  |-- resources
    |-- application.yml

编译后目录

target
  |-- classes
    |-- com.xxx
    |-- application.yml

打包的 jar 解压后目录

|-- BOOT-INF
  |-- classes
    |-- com.xxx
    |-- application.yml
  |-- lib
|-- org.springframework.boot.loader...

引用 classpath 路径下的文件,只需在文件名前加 classpath:

classpath:application-*.xml
# 子目录
classpath:config/*.xml
# **/ 表示任意目录
classpath:**/bean.xml

4.2 classpath vs classpath*

  • classpath 在当前classpath 中查找,只加载第一个 classpath 路径
  • classpath* 不仅包含 class 路径, 还包括 jar 文件(classpath目录)
  • classpath* 会从所有的classpath中加载文件
classpath:*.xml
classpath*:config.xml

以上就是Java ClassLoader类加载器基础详解的详细内容,更多关于Java ClassLoader类加载器的资料请关注脚本之家其它相关文章!

相关文章

  • 如何在IDEA运行spark程序(搭建Spark开发环境)

    如何在IDEA运行spark程序(搭建Spark开发环境)

    spark程序可以通过pom.xml的文件配置,添加spark-core依赖,可以直接在IDEA中编写spark程序并运行结果,这篇文章主要介绍了如何在IDEA运行spark程序(搭建Spark开发环境),需要的朋友可以参考下
    2024-02-02
  • Java解析xml文件和json转换的方法(DOM4j解析)

    Java解析xml文件和json转换的方法(DOM4j解析)

    相信大家都知道Java解析xml的方法有四种,每种方法都很不错,今天通过本文给大家分享使用DOM4j进行解析的方法,文章通过两种方法给大家进行解析,感兴趣的朋友一起看看吧
    2021-08-08
  • Java数据存储的“双子星”对决(Map和Set的区别)

    Java数据存储的“双子星”对决(Map和Set的区别)

    文章主要介绍了Java中Map和Set两种数据结构的定义、实现、方法及应用场景,Map用于存储键值对,键唯一,值可重复;Set用于存储唯一元素,无序,两者都提供了丰富的操作方法,如添加、删除、查找等,感兴趣的朋友一起看看吧
    2025-02-02
  • java的多线程高并发详解

    java的多线程高并发详解

    这篇文章主要介绍了java的多线程高并发详解,文中有非常详细的代码示例,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-04-04
  • java 中如何获取字节码文件的相关内容

    java 中如何获取字节码文件的相关内容

    这篇文章主要介绍了java 中如何获取字节码文件的相关内容的相关资料,需要的朋友可以参考下
    2017-04-04
  • 浅谈Java的两种多线程实现方式

    浅谈Java的两种多线程实现方式

    本篇文章主要介绍了浅谈Java的两种多线程实现方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • HashMap底层原理全面详解面试绝对不慌

    HashMap底层原理全面详解面试绝对不慌

    这篇文章主要介绍了HashMap底层实现原理详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • 浅谈使用setBounds()方法需要注意的地方

    浅谈使用setBounds()方法需要注意的地方

    下面小编就为大家带来一篇浅谈使用setBounds()方法需要注意的地方。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • java 实例化类详解及简单实例

    java 实例化类详解及简单实例

    这篇文章主要介绍了java 实例化类详解及简单实例的相关资料,需要的朋友可以参考下
    2017-03-03
  • SpringBoot动态定时任务实现与应用详解

    SpringBoot动态定时任务实现与应用详解

    定时任务在许多应用场景中是必不可少的,特别是在自动化任务执行、定期数据处理等方面,定时任务能极大地提高系统的效率,然而,随着业务需求的变化,定时任务的执行频率或时间点可能需要动态调整,所以本文给大家介绍了SpringBoot动态定时任务实现与应用
    2024-08-08

最新评论