类加载器

根类加载器(Bootstrap)


它用来加载 Java 的核心库。使用c++编写,程序员无法在Java代码中获得该类 获取类的加载器,如果这个类是由Bootstrap来加载的,那个getClassLoader返回的就是null
1
2
Class clazz=Class.forName("java.lang.String");
System.out.println(clazz.getClassLoader()); //null

扩展类加载器(Extensin)


它用来加载 Java 的扩展库

系统类加载器(System)


它根据 Java 应用的类路径(classpath)来加载 Java

用户自定义的类加载器


java.lang.ClassLoader的子类,用户可以定制类的加载方式。
类加载器并不需要等到某一个类被“首次主动使用”时再加载它。
调用ClassLoader类的loadClass方法加载一个类,并不是对一个类的主动使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
static{
System.out.println("abc");
}
}
public class Main {

public static void main(String[] args) throws Exception{
ClassLoader loader=ClassLoader.getSystemClassLoader();

loader.loadClass("com.kongzhong.gw2.account.service.Test");//并不会打印出"abc"

System.out.println("----------");

Class.forName("com.kongzhong.gw2.account.service.Test");//反射会导致主动使用,会打印出"abc"
}
}

父类委托加载机制


当Java程序请求加载器loader1加载某一个类的时候,loader1首先委托自己的父加载器去加载这个类,若父加载器能加载,则由父加载器完成加载任务。否则才由加载器loader1本身加载这个类。这样做是为了安全考虑,防止有人冒充Java核心类库。
例如:用户自定义了一个类,类的全名为java.lang.String
1
2
3
4
5
6
7
8
9
10
11
12
13
package java.lang;

public class String {

public void sayHello(){
System.out.println("aaa");
}
}

public static void main(String[] args) {
String string=new String();
string.sayHello();
}

结果:运行时的结果为:Exception in thread “main” java.lang.NoSuchMethodError: java.lang.String.sayHello()V
总结:说明类加载是父类委托机制。先让父加载器去加载。运行的永远是java类库中String

定义类加载器与初始化类加载器


如果某一个类加载器能够加载一个类,那么该类加载器就被叫做定义类加载器,定义类加载器及其所有子加载器都被叫做:初始类加载器。
当生成一个自定义类加载器实例时,如果没有指定它的父加载器,那么系统类加载器就将成为该类加载器的父加载器。

命名空间


每一个类加载器都有自己的命名空间,命名空间由该加载器及其所有父加载器的类组成。在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

运行时包


由同一个类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同。只有属于
同一个运行时包的类才能互相访问(即默认访问级别)类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类。去访问核心类库的包可见成员。
假设用户自己定义了一个类java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载,它们属于不同的运行时包。
所以java.lang.Spy不能访问核心类库java.lang包中的包可见成员。

创建用户自定义的类加载器


要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字返回对应的Class对象的引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader{

private String name;//类加载器的名字

private String path="d:\\";//加载类的路径

private static final String FILE_TYPE=".class";//class文件的扩展名

public MyClassLoader(String name){
super();
this.name=name;
}

public MyClassLoader(ClassLoader parent,String name){
super(parent);
this.name=name;
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data=this.loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}

/**
* 把.class字节码转变成字节数组
* @param name
* @return
*/
private byte[] loadClassData(String name){
InputStream is=null;
byte[] data=null;
ByteArrayOutputStream baos=null;
try {

name=name.replace(".","\\");
is=new FileInputStream(new File(path+name+FILE_TYPE));
baos=new ByteArrayOutputStream();
int ch=0;
while((ch=is.read())!=-1){
baos.write(ch);
}
data=baos.toByteArray();

} catch (Exception e) {
e.printStackTrace();
}finally {
try{
is.close();
baos.close();
}catch(Exception e){
e.printStackTrace();
}
}
return data;
}

@Override
public String toString() {
return this.name;
}


public static void main(String[] args) throws Exception{
MyClassLoader loader1=new MyClassLoader("loader1");//loader1的父加载器是系统类加载器

loader1.setPath("c:\\myapp\\serverlib\\");//从c:\\myapp\\serverlib\\加载类

MyClassLoader loader2=new MyClassLoader(loader1,"loader2");//loader2的父加载器是loader1

loader2.setPath("c:\\myapp\\clientlib\\");//从c:\\myapp\\clientlib\\加载类

MyClassLoader loader3=new MyClassLoader(null,"loader3");//loader3的父加载器是根类加载器

loader3.setPath("c:\\myapp\\otherlib\\");//c:\\myapp\\otherlib\\加载类

test(loader1);//如果Student.class在c:\\myapp\\serverlib\\下则打印出loader1。
test(loader2);//如果Student.class在c:\\myapp\\serverlib\\下,而c:\\myapp\\clientlib\\没有的话。那么则打印出loader1,如果两个都有Student.class那么打印出loader1
test(loader3);//打印出loader3,因为根类加载器不会加载Student.class。只有loader3加载。
}

public static void test(ClassLoader loader) throws Exception{
Class clazz=loader.loadClass("Student");//加载Student这个类
Object object=clazz.newInstance();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPath() {
return path;
}

public void setPath(String path) {
this.path = path;
}

public static String getFileType() {
return FILE_TYPE;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Student {

private String name;

public Student(){
System.out.println(this.getClass().getClassLoader());
System.out.println(this.getClass().getClassLoader().getParent());
}
public Student(String name){
this.name=name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}