了解和扩展Java ClassLoader
本文将概述Java类加载,然后继续创建自定义ClassLoader并使用它。
什么是ClassLoader?
我们知道Java程序在Java虚拟机(JVM)上运行。 当我们编译Java类时,它会以字节码的形式将其转换为平台和机器无关的已编译程序,并将其存储为.class文件。 之后,当我们尝试使用类时,Java ClassLoader将该类加载到内存中。
Java中内置了三种类型的内置类加载器:
- Bootstrap类加载器 –它加载JDK内部类,通常加载rt.jar和其他核心类,例如java.lang。*包类
- 扩展类加载器 –它从JDK扩展目录(通常为$ JAVA_HOME / lib / ext目录)中加载类。
- 系统类加载器 –它从当前类路径加载类,这些类可以在使用-cp或-classpath命令行选项调用程序时进行设置。
让我们通过执行以下java程序更好地理解这一点:
ClassLoaderTest.java
package com.journaldev.classloader;public class ClassLoaderTest {public static void main(String[] args) {System.out.println("class loader for HashMap: "+ java.util.HashMap.class.getClassLoader());System.out.println("class loader for DNSNameService: "+ sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());System.out.println("class loader for this class: "+ ClassLoaderTest.class.getClassLoader());System.out.println(com.mysql.jdbc.Blob.class.getClassLoader());}}
上面程序的输出:
HashMap的类加载器:null
DNSNameService的类加载器:sun.misc.Launcher$ExtClassLoader@51f12c4e
此类的类加载器:sun.misc.Launcher$AppClassLoader@799134f4
sun.misc.Launcher$AppClassLoader@799134f4
如您所见,java.util.HashMap ClassLoader作为null来反映Bootstrap ClassLoader,而DNSNameService ClassLoader是ExtClassLoader。 由于类本身位于CLASSPATH中,因此System ClassLoader会加载它。
当我们尝试加载HashMap时,我们的System ClassLoader将其委托给Extension ClassLoader,后者再将其委托给找到该类的Bootstrap ClassLoader并将其加载到JVM中。 DNSNameService类遵循相同的过程,但是Bootstrap ClassLoader无法定位它,因为它位于$ JAVA_HOME / lib / ext / dnsns.jar中,因此由扩展类加载器加载。
还有一点要注意的是,子类加载器加载的类可以查看其父类加载器加载的类。 因此,由System ClassLoader加载的类具有对Extensions和Bootstrap ClassLoader加载的类的可见性。
如果有同级类加载器,则它们将无法访问彼此加载的类。
为什么要编写ClassLoader?
Java默认的ClassLoader可以从本地文件系统中加载文件,这在大多数情况下已经足够了。 但是,如果在加载类时希望在运行时或从FTP服务器或通过第三方Web服务获取类,则必须扩展现有的类加载器。 例如,AppletViewers从远程Web服务器加载类。
ClassLoader如何工作?
当JVM请求一个类时,它通过传递类的完全分类名称来调用ClassLoader的loadClass函数。
loadClass函数调用findLoadedClass()方法来检查该类是否已加载。 需要避免多次加载该类。
如果尚未加载该类,则它将把请求委派给父ClassLoader以加载该类。
如果父ClassLoader找不到该Class,则它将调用findClass()方法在文件系统中查找这些类。
创建我们自己的ClassLoader
我们将通过扩展ClassLoader类并覆盖loadClass(String name)函数来创建自己的ClassLoader。 如果名称以com.journaldev(即我们的示例类包)开头,则将使用我们自己的类加载器加载它,否则我们将调用父ClassLoader loadClass()方法加载该类。
项目结构如下图所示:
CCLoader.java:这是我们的自定义类加载器,具有以下方法。
1. 专用字节[] loadClassFileData(字符串名称)
此方法将从文件系统读取类文件到字节数组。
2. 私有类getClass(String name)
此方法将调用loadClassFileData()函数,并通过调用父defineClass()方法,它将生成Class并返回它。
3. public Class loadClass(字符串名称)
此方法负责加载Class。 如果类名以com.journaldev(我们的示例类)开头,则它将使用getClass()方法加载它,否则它将调用父loadClass函数来加载它。
4. public CCLoader(ClassLoader的父对象)
这是负责设置父ClassLoader的构造函数。
CCLoader源代码:
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;/*** Our Custom Class Loader to load the classes. Any class in the com.journaldev* package will be loaded using this ClassLoader. For other classes, it will* delegate the request to its Parent ClassLoader.**/
public class CCLoader extends ClassLoader {/*** This constructor is used to set the parent ClassLoader*/public CCLoader(ClassLoader parent) {super(parent);}/*** Loads the class from the file system. The class file should be located in* the file system. The name should be relative to get the file location** @param name* Fully Classified name of class, for example com.journaldev.Foo*/private Class getClass(String name) throws ClassNotFoundException {String file = name.replace('.', File.separatorChar) + ".class";byte[] b = null;try {// This loads the byte code data from the fileb = loadClassFileData(file);// defineClass is inherited from the ClassLoader class// that converts byte array into a Class. defineClass is Final// so we cannot override itClass c = defineClass(name, b, 0, b.length);resolveClass(c);return c;} catch (IOException e) {e.printStackTrace();return null;}}/*** Every request for a class passes through this method. If the class is in* com.journaldev package, we will use this classloader or else delegate the* request to parent classloader.*** @param name* Full class name*/@Overridepublic Class loadClass(String name) throws ClassNotFoundException {System.out.println("Loading Class '" + name + "'");if (name.startsWith("com.journaldev")) {System.out.println("Loading Class using CCLoader");return getClass(name);}return super.loadClass(name);}/*** Reads the file (.class) into a byte array. The file should be* accessible as a resource and make sure that its not in Classpath to avoid* any confusion.** @param name* File name* @return Byte array read from the file* @throws IOException* if any exception comes in reading the file*/private byte[] loadClassFileData(String name) throws IOException {InputStream stream = getClass().getClassLoader().getResourceAsStream(name);int size = stream.available();byte buff[] = new byte[size];DataInputStream in = new DataInputStream(stream);in.readFully(buff);in.close();return buff;}
}
CCRun.java:
这是带有主要功能的测试类,我们在其中创建ClassLoader的对象并使用其loadClass方法加载示例类。 加载该类后,我们将使用Java Reflection API来调用其方法。
import java.lang.reflect.Method;public class CCRun {public static void main(String args[]) throws Exception {String progClass = args[0];String progArgs[] = new String[args.length - 1];System.arraycopy(args, 1, progArgs, 0, progArgs.length);CCLoader ccl = new CCLoader(CCRun.class.getClassLoader());Class clas = ccl.loadClass(progClass);Class mainArgType[] = { (new String[0]).getClass() };Method main = clas.getMethod("main", mainArgType);Object argsArray[] = { progArgs };main.invoke(null, argsArray);// Below method is used to check that the Foo is getting loaded// by our custom class loader i.e CCLoaderMethod printCL = clas.getMethod("printCL", null);printCL.invoke(null, new Object[0]);}}
Foo.java和Bar.java:
这些是由我们的自定义类加载器加载的测试类。 它们还具有一个printCL()方法,该方法将被调用以打印已加载该类的ClassLoader。 Foo类将由我们的自定义类加载器加载,后者又使用Bar类,因此Bar类也将由我们的自定义类加载器加载。
Foo.java源代码:
package com.journaldev.cl;public class Foo {static public void main(String args[]) throws Exception {System.out.println("Foo Constructor " + args[0] + " " + args[1]);Bar bar = new Bar(args[0], args[1]);bar.printCL();}public static void printCL() {System.out.println("Foo ClassLoader: "+Foo.class.getClassLoader());}
}
Bar.java源代码:
package com.journaldev.cl;public class Bar {public Bar(String a, String b) {System.out.println("Bar Constructor " + a + " " + b);}public void printCL() {System.out.println("Bar ClassLoader: "+Bar.class.getClassLoader());}
}
执行步骤:
首先,我们将通过命令行编译所有类。 之后,我们将通过传递三个参数来运行CCRun类。 第一个参数是Foo类的完全分类名称,它将由我们的类加载器加载。 其他两个参数将传递给Foo类的主函数和Bar构造函数。 输出的执行步骤如下所示。
Pankaj $ javac -cp。 com / journaldev / cl / Foo.java
Pankaj $ javac -cp。 com / journaldev / cl / Bar.java
Pankaj $ javac CCLoader.java
Pankaj $ javac CCRun.java
CCRun.java:18:警告:对最后一个参数使用不精确参数类型的varargs方法的非varargs调用;
转换为java.lang.Class以进行varargs调用
强制转换为java.lang.Class []进行非可变参数调用并禁止显示此警告
方法printCL = clas.getMethod(“ printCL”,null);
^
1警告
Pankaj $ java CCRun com.journaldev.cl.Foo 1212 1313
正在加载类“ com.journaldev.cl.Foo”
使用CCLoader加载类
加载类“ java.lang.Object”
加载类“ java.lang.String”
加载类“ java.lang.Exception”
加载类“ java.lang.System”
加载类“ java.lang.StringBuilder”
加载类'java.io.PrintStream'
Foo构造函数1212 1313
加载类“ com.journaldev.cl.Bar”
使用CCLoader加载类
酒吧建设者1212 1313
加载类“ java.lang.Class”
栏类加载器:CCLoader @ 71f6f0bf
Foo ClassLoader:CCLoader @ 71f6f0bf
ctk-pcs1313512-2:src pk93229 $
如果仔细查看输出,则首先尝试加载com.journaldev.cl.Foo类,但是由于扩展了java.lang.Object类,因此尝试首先加载它,并请求将其委托给CCLoader loadClass方法它到父类。 因此,父类加载器正在加载Object,String和其他Java类。 我们的ClassLoader仅从文件系统加载Foo和Bar类,而当我们调用它们的printCL()函数时,该文件系统将变得很清晰。
请注意,我们可以更改loadClassFileData()功能以从FTP服务器读取字节数组,或者通过调用任何第三方服务来即时获取类字节数组。
参考: Java面试问题:从我们的JCG合作伙伴Pankaj 了解和扩展Java ClassLoader 。
相关文章:
- JDK中的设计模式
- Java内存模型–快速概述和注意事项
- Java Fork / Join进行并行编程
- 依赖注入–手动方式
翻译自: https://www.javacodegeeks.com/2011/03/understanding-extending-java.html
了解和扩展Java ClassLoader相关推荐
- Java ClassLoader详解
翻译原文链接 背景 Java平台旨在提供健壮,安全和可扩展的功能,以支持代码和数据的移动性.Java虚拟机(JVM)中的Java ClassLoader是实现这些目标的关键组件. JVM负责在Java ...
- [转载] 深入了解Java ClassLoader、Bytecode 、ASM、cglib
转载自http://www.iteye.com/topic/98178 一.Java ClassLoader 1,什么是ClassLoader 与 C 或 C++ 编写的程序不同,Java 程序并不 ...
- 深入理解Java ClassLoader及在 JavaAgent 中的应用
转载自 深入理解Java ClassLoader及在 JavaAgent 中的应用 背景 众所周知, Java 或者其他运行在 JVM(java 虚拟机)上面的程序都需要最终便以为字节码,然后被 ...
- java面试题28 牛客 下面有关java classloader说法错误的是?
java面试题28 牛客 下面有关java classloader说法错误的是? A Java默认提供的三个ClassLoader是BootStrap ClassLoader,Extension Cl ...
- 深入了解Java ClassLoader、Bytecode 、ASM、cglib (I)
http://hi.baidu.com/maoshenmusic/blog/item/5e65dc2419baa6044c088d1a.html 一.Java ClassLoader 1,什么是Cla ...
- 老大难的 Java ClassLoader 再不理解就老了
老大难的 Java ClassLoader 再不理解就老了 ClassLoader 是 Java 届最为神秘的技术之一,无数人被它伤透了脑筋,摸不清门道究竟在哪里.网上的文章也是一篇又一篇,经过本人的 ...
- 理解Java ClassLoader机制 |用Java说话,人气战胜时间!Come On
理解Java ClassLoader机制 |用Java说话,人气战胜时间!Come On 我在参加一个比赛. 欢迎大家都来我的网站参观一下. http://home.fego.cn/members/l ...
- java classloader详解_Java类加载器(ClassLoader)详解
本文主要讲述Java ClassLoader的工作原理,这为后面将Android App代码热替换或者插件化升级做铺垫 一. 类加载器 ClassLoader即常说的类加载器,其功能是用于从Class ...
- Java ClassLoader
Java ClassLoader (1) – What is a ClassLoader? Java ClassLoader (2) – Write your own ClassLoader Java ...
最新文章
- PE里在计算机本地磁盘大小,没有U盘如何在电脑本地磁盘制作一个PE系统
- 嵌入式系统学习-面试要点总结
- Eclipse常见问题集锦
- JPA学习(6)JPQL
- @springbootapplication 注解_Spring Boot最核心的27个干货注解,你了解多少?
- CentOS配置启动ssh与开机自启
- android官方夜间模式,Android夜间模式实践
- android cmd
- 如何使用Gradle外部脚本进行项目构建
- 汽车电子专业知识篇(六)-DDS如何满足自动驾驶汽车中的应用?
- input内强制保留小数点后两位 位数不足时自动补0
- mysql加索引后查询时间变长了(终于有头绪了)
- DOMContentLoaded事件
- 如果Linux从未出现,我们的生活会变成怎样?
- java long 空判断_Long类型null判断带值判断,null必须写在最前面
- windows系统下的文件长名和文件短名
- Origin绘制双x,y轴图像
- SysML实践指南第二版(中文翻译:刘亚龙)第四章 汽车案例
- 教你科学实施有氧运动
- 张飞硬件开发视频第五部电路详细讲解,纯硬件也可以做PWM波
热门文章
- SonarQube 8.3.x中的Maven项目的测试覆盖率报告
- apache ignite_从In Memory Data Grid,Apache Ignite快速入门
- netbeans连接数据库_NetBeans Java EE技巧#1 –数据库中的实体类
- css外墙法_外墙设计模式示例
- Neo4J OGM与Quarkus
- java 泛型嵌套泛型_Java泛型简介–第6部分
- lucene索引_在崩溃或断电后测试Lucene的索引耐久性
- Hibernate事实:有利于双向集vs列表
- 透视变换–鸟瞰图_单例设计模式–鸟瞰
- Java命令行界面(第17部分):jw-options