学习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基础类是由启动类加载器加载。

练习代码看这里


相关文章
|
10月前
|
运维 网络安全
解决ssh: connect to host IP port 22: Connection timed out报错(scp传文件指定端口)
通过这些步骤和方法,您可以有效解决“ssh: connect to host IP port 22: Connection timed out”问题,并顺利使用 `scp`命令传输文件。
9867 7
|
Java
深入理解 Java 8 函数式接口:定义、用法与示例详解
深入理解 Java 8 函数式接口:定义、用法与示例详解
674 2
|
Android开发
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
Android源代码定制:Overlay目录定制|调试Overlay资源是否生效
1336 0
|
SQL 存储 测试技术
提升50%+!Presto如何提升Hudi表查询性能?
提升50%+!Presto如何提升Hudi表查询性能?
277 0
|
存储 编译器 Linux
【C++】float / double 与 0 值比较
使用普通的比较没有问题,如果考虑精度的话,需要与具体的值比较
1542 0
【C++】float / double 与 0 值比较
阿里云个人博客备案网站名称怎么填写?
阿里云个人博客备案网站名称怎么填写?阿里云个人网站备案对网站名称是有要求的,网站命名不能出现博客、论坛、在线、社区、交流、分享、个人空间、爱好者、博客、导航、工作室、论坛、平台、热线、社区、社团、网络、网站、网址、主页、资讯、作品展示等词汇,阿里云百科分享阿里云个人网站备案名称要求:
1568 0
阿里云个人博客备案网站名称怎么填写?
|
Android开发
ScrollView 与 ListView 以及 GridView 滑动冲突完美解决
ScrollView 与 ListView 以及 GridView 滑动冲突完美解决
|
测试技术 BI Apache
Apache Kafka-消息丢失分析 及 ACK机制探究
Apache Kafka-消息丢失分析 及 ACK机制探究
389 0

热门文章

最新文章