类加载器是java的一块较难啃的骨头,本人通过查看文档,和同事讨论,敲代码实验,有了一些心得,所以写下来备忘,也请各路大神批评指教。
我偏向认为java是一种编译+解释型语言,jvm首先会通过编译器把java文件编译成class中间文件,然后通过加载器把class文件加载成机器可识别的机器语言,然后jvm提供解释执行的环境,因此java是一种可跨平台执行的语言(一次编写,多处执行)。
1.关于A.class.getClassLoader()的疑问
我们应该都知道jvm启动时会预先加载一些基础类,主要是rt.jar。我们编写的类一般会在第一次被使用的加载进内存。于是在最开始了解classLoader的时候,我就出现这样一个疑问,如果A类所在的路径同时可以被App的两个孩子的类加载器来加载,那么如下代码中的loader是哪一个加载器呢?
package iteye; public class Test { public static void main(String[] args){ ClassLoader loader = A.class.getClassLoader(); } }
因为A.class.getClassLoader()在被执行之前,A已经就被加载了,而且显然是被当前程序的类加载器AppClassLoader来加载。而如果Test这个类是由AppClassLoader的一个子加载器MyClassLoader1来加载,那么这时候loader是什么类加载器呢?答案还是AppClassLoader,因为委托机制,子加载器为优先委托给父加载器来加载。我们可以把A的class文件剪切到MyClassLoader1指定的路径,让AppClassLoader找不到,于是这个时候loader就是MyClassLoader1了。
2.Class.forName("iteye.A")能初始化static里面的代码
字节码的加载会涉及到如下几个阶段:装载-链接-验证-准备-解析-初始化。而类变量在字节码被加载的时候会被初始化,但是如下代码会打印"initail A!"吗?
package iteye; public class A { static{ System.out.println("initail A!"); } } package iteye; public class Test { public static void main(String[] args) throws ClassNotFoundException{ ClassLoader.getSystemClassLoader().loadClass("iteye.A"); } }答案是不会。因为字节码的加载是有阶段性的,这种加载类的方法不会执行初始化阶段,所以要打印出"initail A!",就应该用Class.forName("iteye.A");
3.为什么要用Thread.currentThread().setContextClassLoader()和Thread.currentThread().getContextClassLoader()
既然已经可以用程序类加载器和自定义加载器来加载类,为什么还要当前线程加载器呢?因为有的时候我们需要突破类加载器委托父类加载这套机制。由于子加载器会优先让父加载器来加载,而父加载器又不可见子加载器,如果此时有一个类在由父加载器加载,而这个类里面又有一些代码需要有子加载器来加载,这时候就需要用到Thread.currentThread().setContextClassLoader()和Thread.currentThread().getContextClassLoader()了。我们可以在需要被子加载器加载的类的当前线程中指定该子加载器,然后在被引用的并且需要被父加载器加载的程序中显示的得到该线程的类加载器,然后用这个加载器来加载类。
4.如何实现一个跨classLoader的单例
package zctPackage.classLoader; import java.lang.reflect.Method; import java.util.Date; public class SingletonClass extends Date{ private SingletonClass (){} private static SingletonClass INSTANCE = new SingletonClass (); private static final Date getInstance() { return INSTANCE; } private static Class getClass(String classname){ ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println("system classloader: " + classLoader); try { return classLoader.loadClass("zctPackage.classLoader." + classname.substring(classname.lastIndexOf('.')+1)); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public static Date getInstance(String classname){ Date singleton = null; Class<?> clazz = null; ClassLoader classLoader = ClassLoader.getSystemClassLoader(); try { clazz = classLoader.loadClass("zctPackage.classLoader." + classname.substring(classname.lastIndexOf('.')+1)); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } if(clazz != null){ try { Method staticMethod = clazz.getDeclaredMethod("getInstance"); staticMethod.setAccessible(true); singleton = (Date) staticMethod.invoke(clazz); } catch (Exception e) { e.printStackTrace(); } } return singleton; } }为什么要让SingletonClass继承Date? 如果此处singleton = (Date) staticMethod.invoke(clazz);改成singleton = (SingletonClass) staticMethod.invoke(clazz);会报ClassCastException。想想为什么,因为本例中,不管这个单例类开始由什么不同类加载器去加载,最终我始终用SystemClassLoader来加载,通过反射获取实例,这样就可以实现跨类加载器的单例模式。实际应用中,此单例类可能由不同的类加载器去加载,我们知道不同类加载器加载同一个类,class是不相等的。因此会报ClassCastException。而用Date来父类引用就没有问题,因为他们都会委托BootStrap类加载器去加载。
测试类:
package zctPackage.classLoader;
import java.lang.reflect.Method;
import java.util.Date;
public class SingletonTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
ClassLoader curentLoader = Thread.currentThread().getContextClassLoader();
System.out.println(curentLoader);
System.out.println("-----------------------------------------");
//UndelegationClassLoader为自定义类加载器
ClassLoader loader1 = new UndelegationClassLoader("zctPackage.classLoader");
ClassLoader loader2 = new UndelegationClassLoader("zctPackage.classLoader");
System.out.println("loader1: " + loader1);
System.out.println("load the classloader's class: " + loader1.getClass().getClassLoader());
Class clazz1 = loader1.loadClass("SingletonClass");
Class clazz2 = loader2.loadClass("SingletonClass");
System.out.println(clazz1.getClassLoader());
System.out.println(clazz2.getClassLoader());
System.out.println("compare the clazz: " + clazz1.equals(clazz2));
Method method1 = clazz1.getMethod("getInstance", String.class);
Method method2 = clazz2.getMethod("getInstance", String.class);
Date s1 = (Date) method1.invoke(clazz1, "SingletonClass");
Date s2 = (Date) method2.invoke(clazz2, "SingletonClass");
System.out.println("-------------------------------------------------");
System.out.println("compare the instance: " + s1.equals(s2));
}
}
最后比较两个实例s1和s2是相等的。
相关推荐
asyncJS - 稍微不同的 JavaScript 加载器和依赖管理器 asyncJS是一个稍微不同的 JavaScript 加载器和浏览器依赖管理器。 与许多其他脚本加载器不同,asyncJS 可以异步加载内联函数和脚本字符串以及外部 JavaScript ...
简单委托 Groovy 类加载器描述这是一个使用自定义类加载器的简单项目。 简单的想法是嵌入 GroovyClassLoader,并且所以,能够使用 Groovy 语言的所有能力,但是这还不够。 目标是能够暴露一个普通的 ClassLoader ...
2. **加载预训练模型**:利用transformers库(如Hugging Face的实现)加载预训练的BERT模型,可以是base或large版本,以及适当的分类头。 3. **构建模型结构**:根据任务类型(序列标注或文本分类)构建模型,可能...
在实际应用中,对于未指定为 :visited 的链接,如果浏览器已经加载过该链接,它将默认为已访问状态,除非使用 :link 伪类明确进行设置。例如,页面中有两个链接,一个有href属性,另一个没有。有href属性的链接可以...
**Vision Transformer(ViT)**是一种深度学习...通过阅读和理解这段代码,你可以学习如何构建和训练自己的Transformer模型,这将有助于你掌握Transformer的核心概念,并可能激发你在计算机视觉领域的新想法和应用。
如果您有一个很棒的装载机想法,请贡献 贡献指南: 请在looaders/vars.css文件中使用。 确保加载程序中的自定义测量值与vars文件中的值相关。 如有必要,请使用 。 请确保loaders文件夹中的文件夹名称,css文件...
在书中,作者通过具体的例子展示了一系列的反射技术,包括但不限于动态代理、类加载器的使用、内省机制等。这些技术通常在开发中较为复杂且易出错,但本书深入浅出地解释了这些技术背后的概念,并提供了足够的背景...
除加载器/保护程序外,所有内容均与 DX11 紧密相关。保存 DX11 设备和直接上下文的 DxRenderer 类。它创建默认的后台缓冲区和深度模板缓冲区。它还包含将每帧依次执行的所有渲染例程的列表。DxRende
添加带有版本控制的离线存储 - 这个想法是在加载脚本后,应该保存到 localStorage/localDB 以防止从服务器重新加载 将代码重组为适当的类 安装 通过鲍尔 $ bower install ng-ondemand 用法 controllers = [ { name...
这是一个关于计算机视觉领域,特别是图像分类任务的数据集,包含了4种特定动物——斑马、犀牛、大象和水牛的图片。这个数据集总计有4000张图像,每种类别的动物都有相等数量的图片,这为训练深度学习模型提供了一个...
- **类加载机制**:Java类的加载是由类加载器完成的,主要包括启动类加载器、扩展类加载器和应用类加载器。 - **内存区域**:JVM内存主要分为堆内存和非堆内存两大部分。堆内存用于存储对象实例和数组;非堆内存包括...
- **自定义数据集**:通过继承`torch.utils.data.Dataset`类,用户可以轻松地创建自己的数据加载器。 ##### 模型构建 PyTorch提供了多种方法帮助用户快速构建深度学习模型: - **众包模型库**:通过`torch.utils....
Discovery 包支持服务定位和自动加载,Lang 包提供了一系列扩展的 Java 核心类,Collections 包则包含对 Java 集合框架的增强和扩展,提供了更丰富的数据结构和算法。 总的来说,Jakarta Commons 是一个宝贵的资源...
布鲁诺介绍Laravel基本控制器类和特征,可以为您的资源URL添加过滤,排序,热切加载和分页。 致力于佐丹奴布鲁诺这个包裹以我的英雄乔治·布鲁诺(Giordano Bruno)的名字命名。 一位真正的有远见的人,敢于梦想超出...
classload是用类加载器实现jar细节隐藏的一个想法,其中EvlJar为替换用到的文件 go-socket为golang实现socket,包含TCP和UDP实现 出现了找不到类加载器的问题,似乎是环境问题2021-2-12 已解决参数错误2021-2-13 ...
7. 加载器:THREE.js提供了各种加载器,如OBJLoader、MTLLoader、GLTFLoader等,用于加载外部3D模型和其他资源。 8. 雷达图(RadarChart)和地形(Terrain):虽然不是THREE.js的基本部分,但社区开发的扩展库提供了...
贾沃 注意:(以前称为Jago ) 用 Go 语言编写的简化 Java 虚拟机。 一个目标是深入学习 ... 类加载器委托 多线程支持 监控、 sleep 、 wait 、 notify支持 JDK 本地方法 GC 准时制 怎么跑 构建和安装 ❯ cd ~
Jekyll的特点在于其轻量级和离线生成,使得网站加载速度快,维护简单。Ruby是Jekyll的编程基础,它是一门面向对象的、动态且易于学习的编程语言,广泛应用于Web开发。 **关于Jekyll的知识点:** 1. **安装与配置**...
4. **自定义样式**:Bootstrap的类可以用来定制编辑器的外观,比如改变按钮的大小、颜色和边距,以及调整编辑区域的宽度和高度。通过使用Bootstrap的预定义类和自定义CSS,我们可以让编辑器融入到任何Bootstrap页面...
8. **JVM和类装载器**:JVM负责解释和执行Java字节码,而类装载器则动态地加载类到JVM,实现程序的模块化和动态性。 9. **垃圾回收**:Java的垃圾回收机制自动回收不再使用的对象,释放内存,程序员无需手动管理...