类加载器的关系

类加载器的分类

  • JVM支持两种类加载器,一种为引导类加载器(Bootstrap ClassLoader),另外一种是自定义类加载器(User Defined ClassLoader)
  • 引导类加载器是由C/C++编写的无法访问到
  • Java虚拟机规定:所有派生于抽象类ClassLoader的类加载器都划分为自定义加载器
  • 最常见的类加载器只有三个(如上图所示)

用户自定义的类会被系统类加载器所加载,核心类库的类会被引导类加载器所加载

public class ClassLoaderTest {public static void main(String[] args) {//获取系统类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//获取其上层:扩展类加载器ClassLoader extClassLoader = systemClassLoader.getParent();System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d//获取其上层:获取不到引导类加载器ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println(bootstrapClassLoader);//null//对于用户自定义类来说:默认使用系统类加载器进行加载ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。ClassLoader classLoader1 = String.class.getClassLoader();System.out.println(classLoader1);//null}
}

系统自带的类加载器介绍

  • 启动类加载器(引导类加载器、Bootstrap ClassLoader)

    • 由c/c++语言实现的,嵌套在jvm内部
    • 用来加载java核心库
    • 并不继承java.lang.ClassLoader,没有父加载器
    • 为扩展类加载器和系统类加载器的父加载器
    • 只能加载java、javax、sun开头的类
  • 扩展类加载器(Extension ClassLoader)
    • java语言编写,sun.misc.Launche包下。
    • 派生于ClassLoader类,父类加载器为Bootstrap ClassLoader
    • 从java.ext.dirs系统属性指定的目录中加载类库或者加载jre/lib/ext子目录下的类库(用户可以在该目录下编写JAR,也会由此加载器所加载)
  • 系统类加载器(System ClassLoaderAppClassLoader)
    • 派生于ClassLoader,父类加载器为Extension ClassLoader
    • 负责加载classpath或者系统属性java.class.path指定路径下的类库
    • java语言编写,sun.misc.Launche包下。
    • 负责加载程序中默认的类,可以通过getSystemClassLoader()方法获取该类的加载器。
  • 用户自定义类加载器(后面详细介绍)
    • 隔离加载类
    • 修改类加载的方式
    • 扩展加载源
    • 防止源码泄漏(可以对字节码文件加密)
    • 继承ClassLoader类方式实现自定义类加载器

关于ClassLoader

  • ClassLoader是一个抽象类,其后的所有类加载器都继承此类

注:这些方法都不是抽象方法。

获取ClassLoader的路径

public class ClassLoaderTest2 {public static void main(String[] args) {try {//1.ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();System.out.println(classLoader);//2.ClassLoader classLoader1 = Thread.currentThread().getContextClassLoader();System.out.println(classLoader1);//3.ClassLoader classLoader2 = ClassLoader.getSystemClassLoader().getParent();System.out.println(classLoader2);} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

双亲委派机制(面试)

  • Java虚拟机对class文件采用的是按需加载的方式,当需要使用到这个类的时候才会对它的class文件加载到内存生成class对象,加载的过程中使用的双亲委派模式,即把请求交给父类处理。
  • 如果一个类加载器收到了类加载的请求,它不会自己加载,而是先把这个请求给自己的父类加载器去执行
  • 如果这个父类加载器还有父类加载器,则会再将请求给自己的父类加载器,依次递归到顶层的启动类加载器
  • 依次进行判断是否能完成委派(加载此类),若能完成委派则该类就由此加载器加载,若无法完成委派,则将委托给子类加载器进行判断是否能完成委派,依次递归到底层加载器,若期间被加载则完成加载阶段不会再递归(注)。
注:类只能被一个加载器所加载。

  • 双亲委派的优势
  • 避免类的重复加载
  • 保护程序的安全,防止核心API被篡改
  • 例如:
  • 创建一个java.lang.String类,因为有双亲委派的机制,所以会将String类交给引导类加载器来判断是否能被加载。引导加载器判断可以加载此类(是核心类中的String),完成加载,则"恶意"写的String类无法生效,防止String类被恶意篡改。这里也称沙箱安全机制(保护java核心源代码)
package java.lang;
public class String {//static{System.out.println("我是自定义的String类的静态代码块");}//错误: 在类 java.lang.String 中找不到 main 方法public static void main(String[] args) {System.out.println("hello,String");}
}//因为加载的是核心类的String,在String中找不到main方法
public class StringTest {public static void main(String[] args) {java.lang.String str = new java.lang.String();//无输出StringTest test = new StringTest();System.out.println(test.getClass().getClassLoader());}
}
package java.lang;public class ShkStart {//错误:java.lang.SecurityException: Prohibited package name: java.langpublic static void main(String[] args) {System.out.println("hello!");}
}//因为java.lang包由引导类加载器加载,引导类中并没有此类,为了安全引导类

破坏双亲委派模型:

较大规模的破坏双亲委派模型的有3种:

  • 由于双亲委派模型是在JDK1.2之后才引入的,所以在JDK1.2之前是不符合双亲委派模型的:
  • ClassLoader这类在JDK1.0开始有存在的,在JDK1.2之前,ClassLoader中是通过私有方法loadClassInternal()去调用自己内部的loadClass()。为了满足双亲委派以及向下兼容,在JDK1.2后的ClassLoader类中,又为该类添加了protected的findClass()方法,JDK1.2之后就不推荐通过覆盖重写loadClass()方法了,而是在新添加的findClass()方法中书写自己的类加载逻辑,若loadClass()方法中的父类加载失败则会调用自己的findClass()方法。
  • 由于双亲委派模型的旨意是越核心的类越由高层的加载器所加载(上文提到过的String类),倘若这些核心类要去调用用户的基础类,例如JNDI服务(是对 资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI,Service Provider Interface)的代码,但启动类加载器不可能"认识"这些代码)
  • 为了解决调用问题,设计了一个线程上下文类加载器(Thread Context ClassLoader),这个类加载器可以通过java.lang.Thread类的setContextClassLoaser()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器。
  • 有了这个上下文类加载器,就可以去加载所需要的SPI代码,实际上就是从父类加载器去请求子类加载器去完成类的加载驱动,违背了双亲委派的一般性规则。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI、JDBC、JCE、JAXB和JBI等。
  • 为了满足"热部署"、"动态部署"等功能而导致的。在OSGi(动态模块技术)环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当收到类加载请求时,OSGi将按照顺序进行类搜索

关于类加载器的一些补充

1. JVM中判断一个类是否是同一个类有两个必要条件:

  • 这两个类的全限定名要一致
  • 这两个类被同一个类加载器加载。

2. 对类加载器的引用:

  • JVM必须知道一个类型是由引导类加载器(启动类加载器)加载的还是由用户类加载器加载的。
  • 如果一个类是由用户类加载器所加载的,那么JVM会将这个类加载器的一个引用作为类信息的一部分保存在方法区。
  • 当解析一个类型到另外一个类型的引用的时候,JVM需要保证这两个类型的类加载器是相同的。

3. 类的主动使用和被动使用

  • 主动使用:
  • 创建类的实例
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射
  • 初始化一个类的子类
  • JVM启动时被标明为启动类的类
  • JDK 7 开始提供的动态代理:java.invoke.MethodHandle实例的解析结果,REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化、则初始化
  • 除以上7种情况,其他使用Java类的方式都为被动使用,被动使用不会导致类的初始化。

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

thinkphp3.2.3 调用自定义模型提示找不到类_面试BAT必问的JVM,今天我们来说一说它类加载器的底层原理...相关推荐

  1. 关于Cause: java.lang.ClassNotFoundException: Cannot find class: 0(提示找不到类)报错

    关于Cause: java.lang.ClassNotFoundException: Cannot find class: 0(提示找不到类)报错 搭建mybatis架构过程中idea包出这么一个莫名 ...

  2. javah提示找不到类文件

    dos命令行直接编译错误场景如下: .java文件所处位置:E:\workspace\Trunk\Development\Source\Server\src\main\java\com\muyi\QT ...

  3. eclipse提示找不到类的解决方法

    今天导入了一个项目后运行,老是提示无法实例化demo类,找不到demo类,可那个类是存在的,也有在Androidmanifest.xml里边声明了,clean了也没用. 经过一番搜索,原来是要在 an ...

  4. idea java 找不到符号_idea 有时提示找不到类或者符号的解决

    1.可能是缓存导致的. 解决方法:清除缓存! 2.全局编译 可能项目依赖别的模块,别的模块修改未进行编译,这时须先对依赖模块进行编译 补充知识:IDEA下lombok安装,以及找不到get,set的问 ...

  5. 调用枚举接口重写方法提示找不到符号_看看人家那后端API接口写得,那叫一个巴适~...

    前言 在目前应用发达的场景下,我们往往需要接入客户端(win).APP程序.网站程序.以及目前热火朝天的微信应用等,这些数据应该可以由同一个服务或者同事由多个提供, Web API作为整个纽带的核心, ...

  6. jar调用dll文件提示找不到指定的模块Unable to load library

    目录 问题描述 解决方案一 解决方案二 效果 补充说明 参考链接 问题描述 在Windows系统上,自己开发的需要使用JNA调用动态链接库dll文件的Swing项目(Java简易系统监视器system ...

  7. 调用Com+时提示找不到文件

    这是因为引用的程序集未同时部署到GAC时造成的. 解决方法:在指定目录运行 gacutil /i *.dll 或你指定的文件即可

  8. annotationprocessor 提示找不到类_StackOverflow上87万访问量的问题:什么是“找不到符号”?...

    你好呀,我是沉默王二,一个和黄家驹一样身高,和刘德华一样颜值的程序员.为了输出更好的内容,我就必须先输入更多的内容,于是我选择 Stack Overflow 作为学习的第一战线,毕竟很多大牛都在强烈推 ...

  9. 运行java提示找不到符号_运行java代码时出现找不到符号错误怎么解决

    运行java代码时出现找不到符号错误怎么解决 发布时间:2020-06-23 11:37:00 来源:亿速云 阅读:113 作者:Leah 运行java代码时出现找不到符号错误怎么解决?针对这个问题, ...

最新文章

  1. Windows下使用Tesseract进行OCR文字识别
  2. mysql pom依赖关系_常用的POM依赖
  3. android 按键kl文件,Android添加新按键
  4. 我的.net程序在linux上运行起啦
  5. 元素在父元素内垂直居中的思路
  6. python标准库os的方法listdir_使用python标准库快速修改文件名字
  7. Redis(三)、支持数据类型及常用操作命令
  8. LA 4123 (计数 递推) Glenbow Museum
  9. SQLi LABS Less-18
  10. 智能一代云平台(三十六):项目中如何做到避免传递依赖
  11. 小强的HTML5移动开发之路(27)—— JavaScript回顾2
  12. play ---------idea
  13. 2022最新爱思助手怎么用 爱思助手使用方法【教程】
  14. 【PC工具】windows免安装录屏绿色软件,无需注册无水印绿色录屏软件
  15. 实现渐变彩色消隐旋转立方体
  16. c++ PDFium pdf转为图片(实例)
  17. 360随身wifi作为无线网卡使用(在电脑有线网卡和无线网卡都没有的情况下)
  18. [自用代码]基于python的遥感影像传统分割方法(直方图双峰法,阈值分割法,模糊C均值法,超像素分割法,K-means,snake)
  19. CSS 实现炫酷的动态背景效果
  20. php函数参数中括号,thinkphp手册中函数的可选参数的中括号为什么是包含的关系?...

热门文章

  1. Js中Reflect对象
  2. python十大语言领域_十大编程语言榜单
  3. Mybatis mapper代理SqlMapConfig.xml配置详解
  4. Java常用接口与类——main方法/Object类/Scanner类
  5. LeetCode 315. 计算右侧小于当前元素的个数(Count of Smaller Numbers After Self)
  6. es6 ArrayBuffer的应用
  7. es6 Class 的静态方法
  8. git 添加用户名和邮箱_Git实用教程(二) | Git简介及安装详解
  9. Git笔记(5) 状态记录
  10. cpp 原子操作_C++ 新特性学习(八) — 原子操作和多线程库[多工内存模型]