(旧文整理)彻底搞懂Java ClassLoader(一)
(归档:https://www.iteye.com/topic/1126847)
在Java的世界中,ClassLoader用来将Java类装载进JVM使之可以运行。当我们使用java命令使用一些.class
的java class时,系统会使用默认的ClassLoader将class读入。
在本文中,将详细探讨ClassLoader
的使用。
动态加载类的几种方法
可以使用下面的几种方法将Class读入:
方法 1 使用 .class
下面的代码会让String[]
这个class被JVM读入:
Class c = String[].class;
此时这个class就已经被加载并得到了对应的Class
。
方法 2: 使用实例的getClass()方法
如果是实例(instance),那么调用它的getClass()
方法就可以得到对应的Class。下面是代码示例:
c = new String[1].getClass();
最后还有通过Class
的forName()
方法来加载class。
方法 3: 使用 Class.forName()
c = Class.forName("[Ljava.lang.String;");
上面使用了Class.forName()
方法来直接加载字串里面表示的方法。
其中第三个方法中的类名有点奇怪,为什么java.lang.String
前面有个L
?这个其实是Java的bytecode的命名约定。
在Java中,我们可以用Class.forName()
来获取类,比如下面这个例子:
System.out.println(Class.forName("Demo1"));
运行结果如下:
class Demo1
但是我们用这种方法获取数组时却会出错:
System.out.println(Class.forName("java.lang.String[]"));
运行结果如下:
Exception in thread "main" java.lang.ClassNotFoundException: java/lang/String[]
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
...
这是为什么呢?因为Array的名字与我们看到的并不一致,可以用下述代码测试:
System.out.println(String[].class.getName());
得到的结果如下:
[Ljava.lang.String;
可以看到,JVM对Array的实际类名编码为:
- 左括号 + 类别代码 + 类名
因此我们用上面的规则来获取Array class
:
Class cls = Class.forName("[Ljava.lang.String;");
因此我们可以得到native type的Array的编码:
System.out.println("byte[]: " + byte[].class.getName());
System.out.println("char[]: " + char[].class.getName());
System.out.println("int[]: " + int[].class.getName());
System.out.println("long[]: " + long[].class.getName());
System.out.println("double[]: " + double[].class.getName());
System.out.println("float[]: " + float[].class.getName());
结果如下:
byte[]: [B
char[]: [C
int[]: [I
long[]: [J
double[]: [D
float[]: [F
可以看到每一种native类型数组对应的JVM中的类名,我们需要知道Java语言的这个约定习惯。
什么是ClassLoader
ClassLoader是Java中的类加载器,可以帮助你把一个Class动态地加载进JVM。每一个JVM环境中,都有一个默认的ClassLoader,我们可以用下述方法获得默认的ClassLoader:
ClassLoader.getSystemClassLoader()
我们撰写一个ClassLoaderName
如下所示:
public class ClassLoaderName {
public static void main(String[] args) throws Exception {
System.out.println(ClassLoader.getSystemClassLoader().toString());
}
}
上面的代码运行结果如下:
jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
从上面的结果可以看到实际的class loader。
ClassLoader的使用方法
下面给出一个完整的使用class loader加载class的例子,代码如下:
package io.alchemystudio.classloader;
public class MyClass {
public static void main(String[] args) throws Exception {
Class clazz = ClassLoader
.getSystemClassLoader()
.loadClass("io.alchemystudio.classloader.MyClass");
MyClass myClass = (MyClass) clazz.getDeclaredConstructor().newInstance();
myClass.sayHello();
}
private void sayHello() {
System.out.println("Hello, world!");
}
}
如上所示,我们首先使用ClassLoader.getSystemClassLoader().loadClass()
方法来加载MyClass
;然后通过clazz.getDeclaredConstructor().newInstance()
生成class的实例,最后使用这个实例的sayHello()
方法。执行上面的代码结果如下:
Hello, world!
以上就是class loader的一个基础的使用方法。在本系列文章的后续文章中,我们将动手制作一个ClassLoader
,并学习ClassLoader
的命名空间Namespace。
本系列文章中使用的源代码
接下来的讲解过程中,要大量用到代码示例,我将这些代码统一放到了github上面:
这是一个基于maven的项目,因此可以很方便地打包执行,接下来的讲解将大量用到这个项目中的代码。
这篇文章的样例代码放在了这里:
- java-snippets/ClassLoaderName.java at master · alchemy-studio/java-snippets · GitHub
- java-snippets/MyClass.java at master · alchemy-studio/java-snippets · GitHub
有兴趣可以运行看看。