了解和扩展Java ClassLoader

Java ClassLoader是项目开发中Java的关键但很少使用的组件之一。 就我个人而言,我从未在任何项目中扩展ClassLoader,但是拥有自己的可以自定义Java类加载的ClassLoader的想法让我感到很兴奋。

本文将概述Java类加载,然后继续创建自定义ClassLoader并使用它。

什么是ClassLoader?

我们知道Java程序在Java虚拟机(JVM)上运行。 当我们编译Java类时,它会以字节码的形式将其转换为平台和机器无关的已编译程序,并将其存储为.class文件。 之后,当我们尝试使用类时,Java ClassLoader将该类加载到内存中。

Java中内置了三种类型的内置类加载器:

  1. Bootstrap类加载器 –它加载JDK内部类,通常加载rt.jar和其他核心类,例如java.lang。*包类
  2. 扩展类加载器 –它从JDK扩展目录(通常为$ JAVA_HOME / lib / ext目录)中加载类。
  3. 系统类加载器 –它从当前类路径加载类,这些类可以在使用-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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374616.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAD教程-AL对其命令

AL可以实现不规则的对其功能 1.第一步按下AL,按下Enter 2.选择第一个源点 3.选择第一个目标点 4.选择第二个源点 5.选择第二个目标点 6.按下Enter,完成移位 转载于:https://www.cnblogs.com/weloveshare/p/4739873.html

(一二四)tableView的多组数据展示和手动排序

最近在写一个轻量级的网络游戏,遇到了技能优先顺序手动排序的需求,我就想到了iOS自带的tableView编辑功能,对其进行了初步探索,最后做出的效果如下图所示: 点击左边可以删除,拖住右边可以手动排序&#xff…

知道这 20 个正则表达式,能让你少写 1,000 行代码

CocoaChina05-13正则表达式,一个十分古老而又强大的文本处理工具,仅仅用一段非常简短的表达式语句,便能够快速实现一个非常复杂的业务逻辑。熟练地掌握正则表达式的话,能够使你的开发效率得到极大的提升。下面是技匠整理的&#x…

元素分类--块级元素(特点:独占一行, 宽高边距可改)

什么是块级元素&#xff1f;在html中<div>、 <p>、<h1>、<form>、<ul> 和 <li>就是块级元素。设置display:block就是将元素显示为块级元素。如下代码就是将内联元素a转换为块状元素&#xff0c;从而使a元素具有块状元素特点。 a{display:b…

站立会议05(第二次冲刺)

一、站立会议信息&#xff08;配站立会议照片&#xff09; 第五天我们继续开发&#xff0c;把注册验证信息完善一下&#xff0c;将开始网站公共主页的开发。 二、任务进度 第五天我们注册验证完成。 三、任务看板&#xff08;图&#xff09; 四、燃尽图&#xff08;图&#xff…

[SoapUI] DataSource, DataSourceLoop, DataSink

Script assertion in login: 转载于:https://www.cnblogs.com/MasterMonkInTemple/p/4748189.html

将CAPTCHA添加到您的GWT应用程序

什么是验证码&#xff1f; 在一个充满恶意机器人的世界中&#xff0c;您该怎么做才能保护您宝贵的Web应用程序&#xff1f; 您真正应该做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉&#xff08;听起来有些奇怪&#xff09;&#xff0c;则CAPTCHA是确保用户实际上…

SQL基础语句

数据库面试常见题 一、SQL语言包括数据定义语言、数据操作语言、数据控制语言和事务控制语言1&#xff1a;DDL(Data Definition Language)&#xff0c;是用于描述数据库中要存储的现实世界实体的语言。 CREATE TABLE - 创建新表 ALTER TABLE - 变更&#xff08;改变&#xff0…

iOS学习——ScrollView图片轮播和同类控件优先级问题

iOS学习——ScrollView的使用和同类控件优先级问题 1. 布置界面 ScrollView的使用非常简单&#xff0c;只有三步 1.1 添加一个scrollview 1.2 向scrollview添加内容 1.3 告诉scrollview中内容的实际大小 首先做第一步&#xff0c;布置界面。 拖拽一个scrollview就可以了 就…

Git 分支管理和冲突解决

创建分支 git branch 没有参数&#xff0c;显示本地版本库中所有的本地分支名称。 当前检出分支的前面会有星号。 git branch newname 在当前检出分支上新建分支&#xff0c;名叫newname。 git checkout newname 检出分支&#xff0c;即切换到名叫newname的分支。 git checkout…

git克隆/更新/提交代码步骤及示意图

1. git clone ssh://flycm.intel.com/scm/at/atSrc 或者git clone ssh://flycm.intel.com/scm/at/atJar 或者git clone ssh://flycm.intel.com/scm/at/atFramework 2. git checkout cpeg/scm/stable 切换分支&#xff0c;然后更新代码 3. git pull 先把远程分支上最新的代码拉到…

互联网金融P2P主业务场景自动化测试

互联网金融P2P行业&#xff0c;近三年来发展迅速&#xff0c;如火如荼。据不完全统计&#xff0c;全国有3000的企业。“互联网”企业&#xff0c;几乎每天都会碰到一些奇奇怪怪的bug&#xff0c;作为在互联网企业工作的测试人员&#xff0c;风险和压力都巨大。那么我们如何降低…

OSGi将Maven与Equinox结合使用

很长时间以来&#xff0c;我一直在努力理解OSGi的真正含义。 它已经存在很长时间了&#xff0c;但是没有多少人意识到这一点。 人们已经大肆宣传它是一种非常复杂的技术。 这是我为所有Java开发人员简化的尝试。 简而言之&#xff0c; OSGi是一组规范&#xff0c;这些规范允许对…

Hadoop:简单介绍

什么是Hadoop&#xff1a; Hadoop是一种用Java编写的框架&#xff0c;用于在大型商品硬件集群上运行应用程序&#xff0c;并具有类似于Google File System和MapReduce的功能 。 HDFS是高度容错的分布式文件系统&#xff0c;与Hadoop一样&#xff0c;旨在部署在低成本硬件上。 它…

Javascript 异步编程的4种方法

你可能知道&#xff0c;Javascript语言的执行环境是"单线程"&#xff08;single thread&#xff09;。 所谓"单线程"&#xff0c;就是指一次只能完成一件任务。如果有多个任务&#xff0c;就必须排队&#xff0c;前面一个任务完成&#xff0c;再执行后面一…

JAVA入门之方法

所谓方法&#xff0c;就是用来解决一类问题的代码的有序组合&#xff0c;是一个功能模块。 一般情况下&#xff0c;定义一个方法的语法是&#xff1a; 其中&#xff1a; 1、 访问修饰符&#xff1a;方法允许被访问的权限范围&#xff0c; 可以是 public、protected、private 甚…

JAVA运算符和优先级

1、算术运算符&#xff1a; 和 -- 既可以出现在操作数的左边&#xff0c;也可以出现在右边&#xff0c;但结果是不同&#xff0c;如&#xff1a; ①int a5&#xff1b;int ba&#xff1b; #先把a赋给b&#xff0c;a再自增 ②int a5&#xff1b;int ba&#xff1b;   #a先…

将JSON功能添加到您的GWT应用程序中

JSON简介 在Web应用程序上工作时&#xff0c;总是会出现客户端-服务器数据交换的问题。 在此问题上有多种方法&#xff0c;其中许多使用XML进行交换。 执行此任务的一种不太知名的格式是JSON。 JSON&#xff08;JavaScript对象表示法&#xff09;是一种轻量级的数据交换格式。…

win10资源管理器怎么打开_让你效率倍增的电脑神器,最强资源管理器增强工具「QTTabBar」...

前言Windows 自带的资源管理器的功能比较简单&#xff0c;够用是够用了&#xff0c;但有时也确实无法满足我们对更便捷高效的操作方式的追求。「QTTabBar」正是一个非常强大的 Windows 资源管理器增强工具&#xff01;不仅支持多标签页管理&#xff0c;还有许多便捷的扩展功能&…

android横向滑动选择的view

做文字编辑&#xff0c;从网上找来的。 HorizontalScrollSelectView&#xff1a; public boolean mAlwaysOverrideTouch true;protected ListAdapter mAdapter;private int mLeftViewIndex -1;private int mRightViewIndex 0;protected int mCurrentX;protected int mNextX;…