学习ClassLoader和自定义ClassLoader的使用

这几天在看ClassLoader,推荐一篇讲ClassLoader的文章,地址:http://longdick.iteye.com/blog/442213,然后又看了下《深入java虚拟机》里的ClassLoader章节,下面就随便说点,主要还是以练习代码为主。

1.对于任意一个类,由加载它的ClassLoader和它本身决定了在java虚拟机中的唯一性。
也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。否则,即使是同一个类文件,如果加载它们的ClassLoader不同,那么这2个类必定不相等。

2.类加载的双亲委派机制是可以破坏的,通过改变CallClassLoader和ContextClassLoader。
被当前类引用的类的加载也是由加载当前类的ClassLoader加载,子线程的ContextClassLoader是由父线程的ContextClassLoader派生出来。

3.扩展ClassLoader一般应该建议重写findClass(String)方法。
原因是自定义的ClassLoader只专注于加载自己的Class,在加载自己的Class的过程中,又会先加载父类Object,Object类是由启动类加载器加载的,默认可以直接由loadClass(String)执行,这么做可以用ClassLoader来选择性加载某一些类做隔离。

贴代码

自定义ClassLoader,重写loadClass()。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@Override

public Class<?> loadClass(String name) throws ClassNotFoundException {

try {

String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";

InputStream is = getClass().getResourceAsStream(fileName);

if (is == null) {

return super.loadClass(name);

}

byte[] b = new byte[is.available()];

is.read(b);

return defineClass(name, b, 0, b.length);

}

catch (Exception e) {

throw new ClassNotFoundException(name);

}

}

测试类

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

public void say() {

System.out.println("hello world");

}

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

// 打印java虚拟机的ClassLoader

System.out.println(Thread.currentThread().getContextClassLoader());

System.out.println(Thread.currentThread().getContextClassLoader().getParent());

System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent());

// 自定义ClassLoader

ClassLoader myLoader = new MyClassLoader();

Class<?> clazz = myLoader.loadClass("com.zoo.classloader.ClassLoaderTest");

Object obj = clazz.newInstance();

// 打印自定义ClassLoader加载的Class对象

System.out.println(obj.getClass());

// 打印被加载的Class对象是由哪个ClassLoader加载的

System.out.println(obj.getClass().getClassLoader());

/*

* 对于任意一个类,由加载它的ClassLoader和它本身决定了在jvm虚拟机中的唯一性。

* 也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。

* 否则,即使是同一个类文件,只要加载它们的ClassLoader不同,那么这2个类必定不相等。

*/

System.out.println(obj instanceof com.zoo.classloader.ClassLoaderTest);

// 由自定义ClassLoader加载后,在程序里运行。

Method method = clazz.getDeclaredMethod("say", new Class<?>[] {});

method.invoke(obj, new Object[] {});

// 获取当前上下文的ClassLoader

System.out.println(Thread.currentThread().getContextClassLoader());

// 改变上下文的ClassLoader

Thread.currentThread().setContextClassLoader(myLoader);

// 获取当前上下文的ClassLoader

System.out.println(Thread.currentThread().getContextClassLoader());

// 改变当前上下文的ClassLoader可以改变在当前线程派生出的子线程的上下文ClassLoader

Thread t = new Thread(new Runnable() {

@Override

public void run() {

Thread t2 = Thread.currentThread();

System.out.println(t2.getName() + ":" + t2.getContextClassLoader());

}

});

t.start();

Thread.sleep(3000);

// 获取CallClassLoader

Class<?> clz = Class.forName("com.zoo.classloader.ClassLoaderTest");

System.out.println(clz);

System.out.println(clz.getClassLoader());

System.out.println(clz.getClass());

System.out.println(clz.getClass().getClassLoader());

}

执行结果:
sun.misc.Launcher$AppClassLoader@addbf1 ==> 当前上下文类加载器是应用类加载器
sun.misc.Launcher$ExtClassLoader@42e816 ==> 应用类加载器的父类加载器是扩展类加载器
null ==> 扩展类加载器的父类加载器是启动类加载器,因为启动类加载器是C++编写,java里获取不到
class com.zoo.classloader.ClassLoaderTest ==> 自定义类加载器加载进行加载
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 通过Class对象获取它的类加载器
false ==> 同一个Class,不同的类加载器加载,那么Class不相等
hello world ==> 被自定义类加载器加载的Class在程序中执行
sun.misc.Launcher$AppClassLoader@addbf1 ==> 获取当前上下文类加载器
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 设置上下文类加载器
Thread-0:com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 派生出来的子线程的类加载器
class com.zoo.classloader.ClassLoaderTest ==> 默认加载类
sun.misc.Launcher$AppClassLoader@addbf1 ==> 默认加载用的是应用类加载器
class java.lang.Class ==> 获取Class
null ==> 获取类加载器,由于是启动类加载器,所以为null,java基础类是由启动类加载器加载。

练习代码看这里


上一篇:java.lang.UnsatisfiedLinkError: 包名.方法名([BLjava


下一篇:认缴出资额和实缴出资额的区别