java类加载器主要分为如下几种:

  • jvm提供的类加载器
    1. 根类加载器:底层实现,主要加载java核心类库(如:java.lang.*)
    2. 扩展类加载器:使用java代码实现,主要加载如:jre/lib/ext/ 下的扩展类库。(父类加载器为根类加载器)
    3. 系统类加载器(应用类加载器):使用java代码实现,加载classpath目录下的类。(父类加载器为扩展类加载器)
  • 用户自定义类加载器:去继承ClassLoader类实现自定义类加载器。

  类加载器负责将java字节码文件加载到虚拟机内存中也就是类的生命周期的装载过程。(如下为类的生命周期)

        

  下面是用户自定义类加载器的实现过程及代码:

  实现一个用户自定义类加载器需要去继承ClassLoader类并重写findClass方法,代码如下

package com.space;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; public class MyClassLoader extends ClassLoader { private String path="/home/luciel/"; //默认加载路径 private String name; //类加载器名称 private final String filetype=".class"; //文件类型 public MyClassLoader(String name) {
// TODO Auto-generated constructor stub
super();
this.name=name;
} public MyClassLoader(ClassLoader parent,String name){
super(parent);
this.name=name;
} @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
byte[] b=loadClassData(name);
return defineClass(name, b, 0, b.length);
} private byte[] loadClassData(String name) {
byte[] data=null;
InputStream in=null;
name=name.replace('.', '/');
ByteArrayOutputStream out=new ByteArrayOutputStream();
try {
in=new FileInputStream(new File(path+name+filetype));
int len=0;
while(-1!=(len=in.read())){
out.write(len);
}
data=out.toByteArray();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return data;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} @Override
public String toString() {
// TODO Auto-generated method stub
return this.name;
} }
public MyClassLoader(String name) {
// TODO Auto-generated constructor stub
super();
this.name=name;
}

这个构造方法中去调用ClassLoader无参构造方法从ClassLoader源码中可以得出:调用此构造方法会让系统类加载器成为该类加载器的父加载器。(注意:此处父类加载器不一定是继承关系,只是一种包装关系)。

在重写findClass方法时参照java API中实现一个网络类加载器的例子,API例子如下:

class NetworkClassLoader extends ClassLoader {
String host;
int port; public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
} private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}

下面是测试类以及main方法类的测试代码:

 package com.space;

 public class Color {
public Color() {
// TODO Auto-generated constructor stub
System.out.println("Color is loaded by "+this.getClass().getClassLoader()); }
}
 package com.space;

 public class Red extends Color{

     public Red() {
// TODO Auto-generated constructor stub
System.out.println("Red is loaded by "+this.getClass().getClassLoader()); } }
 package com.space;

 public class TestMyClassLoader {

     public static void main(String[] args) throws Exception {

         MyClassLoader loader1=new MyClassLoader("loader1");

         loader1.setPath("/home/luciel/test1/");

         MyClassLoader loader2=new MyClassLoader(loader1, "loader2");

         loader2.setPath("/home/luciel/test2/");

         MyClassLoader loader3=new MyClassLoader(null, "loader3");

         loader3.setPath("/home/luciel/test3/");

         loadClassByMyClassLoader("com.space.Red",loader2);

         loadClassByMyClassLoader("com.space.Red",loader3);
} private static void loadClassByMyClassLoader(String name,ClassLoader loader) throws Exception{ Class<?> c=loader.loadClass(name);
Object obj=c.newInstance();
} }

按照main方法中给三个类加载器传入的路径创建相应的环境并将com.space.Red、com.space.Color的class类拷贝到

/home/luciel/test1/和/home/luciel/test2/以及/home/luciel/test3/目录中去将com.space.TestMyClassLoader类和com.space.MyClassLoader拷贝
/home/luciel/main/ 中去并在该目录下执行

最终运行结果如下显示:

[root@localhost main]# java com.space.TestMyClassLoader
Color is loaded by loader1
Red is loaded by loader1
Color is loaded by loader3
Red is loaded by loader3
loadClassByMyClassLoader("com.space.Red",loader2);
如测试代码中 我们调用了loader2去加载Red类但Red类却打印出由loader1加载,这是由于类加载器秉承的是父委托机制loader2在创建时包装了loader1为其父类加载器,而loader1创建时由于调用的是没有传入父类加载器的构造方法,因此它的父加载器为系统类加载器。因此几个加载器的关系如下:

由于loader1的路径下有Red类class文件所以loader1可以加载,因此载Red类构造方法中打印的类加载器为loader1.

我门看似只去加载了Red类但运行结果却将Color父类加载了,而且Color类的加载在Red类之前,那是由于Red类主动使用 了Color类,因此在初始化Red类之前必须先初始化Color类,要初始化就必须先加载,所以先打印出了Color类的输出信息。(关于类的主动使用大家如果不清楚可以查查,一共有6种)

loadClassByMyClassLoader("com.space.Red",loader3);
再分析第二个测试代码,由于loader3创建时传入的父类加载器为 null,看下面关于ClassLoader类源码部分代码或查看java API
/**
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
*
* <p> If a security manager is present, and the invoker's class loader is
* not <tt>null</tt> and is not an ancestor of this class loader, then this
* method invokes the security manager's {@link
* SecurityManager#checkPermission(java.security.Permission)
* <tt>checkPermission</tt>} method with a {@link
* RuntimePermission#RuntimePermission(String)
* <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
* access to the parent class loader is permitted. If not, a
* <tt>SecurityException</tt> will be thrown. </p>
*
* @return The parent <tt>ClassLoader</tt>
*
* @throws SecurityException
* If a security manager exists and its <tt>checkPermission</tt>
* method doesn't allow access to this class loader's parent class
* loader.
*
* @since 1.2
*/
@CallerSensitive
public final ClassLoader getParent() {
if (parent == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}
getParent方法的说明如下部分文字:
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
意思是说我们可以使用null表示 the bootstrap class loader(根类加载器)
那么loader3的父类加载器就是 根类加载器 ,而根类加载器只会去加载那些系统核心类库,显然我们的Red和Color类不属于此范围,而就只能让loader3加载,loader3的加载路径下有这两个类对应的字节码可以成功加载,所以大引出Red和Color类的类加载器为loader3

以上是自己最近在学习jvm时有关类加载器的相关知识总结。

java类加载器-----用户自定义类加载器实现的更多相关文章

  1. Java魔法堂:类加载器入了个门

    一.前言 <Java魔法堂:类加载机制入了个门>中提及整个类加载流程中只有加载阶段作为码农的我们可以入手干预,其余均由JVM处理.本文将记录加载阶段的核心组件——类加载器的相关信息,以便日 ...

  2. Java虚拟机10:类加载器

    类与类加载器 虚拟机设计团队把类加载阶段张的"通过一个类的全限定名来获取此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...

  3. java类加载器-Tomcat类加载器

    在上文中,已经介绍了系统类加载器以及类加载器的相关机制,还自定制类加载器的方式.接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的.(本介绍是基于tomcat6.0.41,不同版本可 ...

  4. java类加载器-系统类加载器

    系统类加载器 系统类加载器可能都耳详能熟,但是为了完整点,还是先简单的说说系统的类加载器吧. public class Test { public static void main(String[] ...

  5. JAVA提高七:类加载器

    今天我们学习类加载器,关于类加载器其实和JVM有很大关系,在这里这篇文章只是简单的介绍下类加载器,后面学习到JVM的时候还会详细讲到类加载器,本文分为下面几个小节讲解: 一.认识类加载器 1.什么是类 ...

  6. Java自定义类加载和ClassPath类加载器

    1 自定义类加载器: 实现规则: 自定义类加载器,需要重写findClass,然后通过调用loadClass进行类加载(loadClass通过递归实现类的双亲委派加载) package com.dax ...

  7. Java虚拟机14:类加载器

    类与类加载器 虚拟机设计团队把类加载阶段张的"通过一个类的全限定名来获取此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...

  8. 进阶Java编程(11)ClassLoader类加载器【待完成】

    1,ClassLoader类加载器简介 在Java里面提供一个系统的环境变量:ClassPath,这个属性的作用主要是在JVM进程启动的时候进行类加载路径的定义,在JVM里面可以根据类加载器而后进行指 ...

  9. 深入理解java:1.1. 类加载器

    从java的动态性到类加载机制   我们知道,Java是一种动态语言. 那么怎样理解这个“动态”呢? 或者说一门语言具备了什么特性,才能称之为动态语言呢? 对于java,我是这样理解的. 我们都知道J ...

随机推荐

  1. spring jdbcTemplate的CRUD操作

    一.jdbcTemplate准备 1.导入与jdbcTemplate相关的jar包 2.设置数据库信息 3.创建jdbcTemplate对象,设置数据源 二.添加操作 1.代码 2.结果 三.修改操作 ...

  2. 'gbk' codec can't encode character '\xa0' in position 34: illegal multibyte sequence

    今天在爬某广告贼多的网站遇到的问题,简单记录下

  3. 保持linux下保持ssh不断线

    用ssh链接服务端,一段时间不操作或屏幕没输出(比如复制文件)的时候,会自动断开,有两种解决办法: 1.在客户端配置 #vi  /etc/ssh/ssh_config(注意不是/etc/ssh/ssh ...

  4. 【Flask】模板继承

    # 模版继承笔记: ### 为什么需要模版继承:模版继承可以把一些公用的代码单独抽取出来放到一个父模板中.以后子模板直接继承就可以使用了.这样可以重复性的代码,并且以后修改起来也比较方便. ### 模 ...

  5. Tab动画菜单

    在线演示 本地下载

  6. webpack打包笔记

    optimist是一个node库,将webpack.config.js与shell参数整合成options对象 options对象包含之后构建的重要信息,类似于webpack.config.js we ...

  7. codeforces 154A 贪心

    贪心 题目自身限制每个字母最多出现在一个限制词语中,给出k个限制词语,将问题转化为k个子问题,对每个限制词语遍历给出的字符串,如限制词do,若出现连续的oddoood(连续的o和d),统计o和d出现的 ...

  8. Delphi UniDAC 通过http协议连接数据库的设置

    Connection through HTTP tunnel(using http protocol) Sometimes client machines are shielded by a fire ...

  9. ubuntu下搭建Scrapy框架简单办法

    1. 先执行以下命令 sudo apt-get install python-lxml sudo apt-get install libxslt1-dev sudo apt-get install p ...

  10. JavaWeb -- Session应用实例 -- 随机中文验证码 检验

    注册页面 login.html <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html ...