java类加载器主要分为以下几类:
jvm提供的类加载器
根加载器:一种基本实现,主要加载java核心类库。 示例: java.lang.* )
扩展类库:使用java代码实现,主要加载扩展类库,如jre/lib/ext/。 (父类加载器是根类加载器)
系统类加载器(APP应用程序类加载器)使用java代码实现加载classpath目录下的类。 (父类加载器是扩展类加载器)
用户定义的类加载器:继承ClassLoader类以实现自定义类加载器。
类加载器负责将java字节码文件加载到虚拟机内存(即类的生命周期)中的过程。 (类的生命周期如下所示)
类的生命周期图
下面是实现用户定义类加载器的步骤和代码。
要实现用户定义的类加载器,必须继承ClassLoader类并重写findClass方法,如下所示
package com.space;
import Java.io.bytearray output stream;
import java.io.File;
import java.io.FileInputStream;
import Java.io.file not found exception;
import java.io.IOException;
import java.io.InputStream;
publicclassmyclassloaderextendsclassloader {
私有路径='/home/luciel/'; //默认加载路径
私有字符串名称; //类加载器名称
privatefinalstringfiletype='.class '; //文件类型
publicmyclassloader (字符串名称) {
//todo自动生成构造函数
super (;
this.name=name;
}
publicmyclassloader (类加载器parent,字符串名称)。
super(parent );
this.name=name;
}
@Override
publicclassfindclass (字符串名称) throws ClassNotFoundException {
//todo自动- generated method stub
byte[]b=loadclassdata(name );
returndefineclass(name,b,0,b.length );
}
private byte [ ] loadclassdata (字符串名称) {
byte [ ]数据=null;
InputStream in=null;
name=name.replace ('.','/' );
bytearrayoutputstreamout=newbytearrayoutputstream (;
try {
in=new文件输入(new file (pathname filetype ) );
int len=0;
wile(-1!=(len=in.read ) () ) ) ) 0
out.write(len;
}
data=out.toByteArray (;
}catch(filenotfoundexceptione ) {
//todo auto-generated catch块
e .打印堆栈跟踪(;
}catch(ioexceptione ) {
//todo auto-generated catch块
e .打印堆栈跟踪(;
}finally{
try {
in.close (;
out.close (;
}catch(ioexceptione ) {
//todo auto-generated catch块
e .打印堆栈跟踪(;
}
}
返回数据;
}
公共字符串获取路径
返回路径;
}
公共语音设置路径(stri
ng 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方法类的测试代码:
1 package com.space;
2
3 public class Color {
4 public Color() {
5 // TODO Auto-generated constructor stub
6 System.out.println("Color is loaded by "+this.getClass().getClassLoader());
7
8 }
9 }
1 package com.space;
2
3 public class Red extends Color{
4
5 public Red() {
6 // TODO Auto-generated constructor stub
7 System.out.println("Red is loaded by "+this.getClass().getClassLoader());
8
9 }
10
11 }
1 package com.space;
2
3 public class TestMyClassLoader {
4
5 public static void main(String[] args) throws Exception {
6
7 MyClassLoader loader1=new MyClassLoader("loader1");
8
9 loader1.setPath("/home/luciel/test1/");
10
11 MyClassLoader loader2=new MyClassLoader(loader1, "loader2");
12
13 loader2.setPath("/home/luciel/test2/");
14
15 MyClassLoader loader3=new MyClassLoader(null, "loader3");
16
17 loader3.setPath("/home/luciel/test3/");
18
19 loadClassByMyClassLoader("com.space.Red",loader2);
20
21 loadClassByMyClassLoader("com.space.Red",loader3);
22 }
23
24 private static void loadClassByMyClassLoader(String name,ClassLoader loader) throws Exception{
25
26 Class> c=loader.loadClass(name);
27 Object obj=c.newInstance();
28 }
29
30 }
按照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 null to represent the bootstrap class loader. This method
* will return null in such implementations if this class loader's
* parent is the bootstrap class loader.
*
*
If a security manager is present, and the invoker's class loader is
* not null and is not an ancestor of this class loader, then this
* method invokes the security manager's {@link
* SecurityManager#checkPermission(java.security.Permission)
* checkPermission} method with a {@link
* RuntimePermission#RuntimePermission(String)
* RuntimePermission("getClassLoader")} permission to verify
* access to the parent class loader is permitted. If not, a
* SecurityException will be thrown.
*
* @return The parent ClassLoader
*
* @throws SecurityException
* If a security manager exists and its checkPermission
* 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 null to represent the bootstrap class loader. This method * will return null 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时有关类加载器的相关知识总结。