首先讲下java类加载的顺序

每个类加载器有自己的名字空间,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。不管该类有没有变化,下次再需要加载时,它只是从自己的缓存中直接返回已经加载过的类引用。
接下来讲如何加载外部jar
首先要有一个工程外的jar包,这里假定有个Test.jar,里面有个com.chentaoqian.test.Test类。
使用系统类加载器,加载一个jar
Java
package com.chentaoqian.classloaderdemo;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class MyClassloader {
public static void classLoader(File file, String className, String methodName, Object[] args, Class>[] parameterTypes) {
try {
URL url = file.toURI().toURL();
//得到系统类加载器,利用该加载器加载指定路径下的jar包
URLClassLoader urlClassLoader= (URLClassLoader) ClassLoader.getSystemClassLoader();
Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{ URL.class});
add.setAccessible(true);
add.invoke(urlClassLoader, new Object[] {url});
urlClassLoader.loadClass(className);
Class> c = urlClassLoader.loadClass(className);
//列出所有方法
// Method[] methods = c.getMethods();
// for (Method m : methods) {
// System.out.println(m.getName());
// }
//调用方法
// if (args == null) {
// c.getMethod(methodName, parameterTypes).invoke(c.newInstance());
// } else {
// c.getMethod(methodName, parameterTypes).invoke(c.newInstance(), args);
// }
// urlClassLoader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//无参方法的例子
classLoader(new File("E:\\TestJar.jar"), "com.chentaoqian.test.Test", "sayHi", null, null);
//有参方法的例子
classLoader(new File("E:\\TestJar.jar"), "com.chentaoqian.test.Test", "sayBye", new String[]{"byebye"}, new Class>[]{String.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
packagecom.chentaoqian.classloaderdemo;
importjava.io.File;
importjava.lang.reflect.Method;
importjava.net.URL;
importjava.net.URLClassLoader;
publicclassMyClassloader{
publicstaticvoidclassLoader(Filefile,StringclassName,StringmethodName,Object[]args,Class>[]parameterTypes){
try{
URLurl=file.toURI().toURL();
//得到系统类加载器,利用该加载器加载指定路径下的jar包
URLClassLoaderurlClassLoader=(URLClassLoader)ClassLoader.getSystemClassLoader();
Methodadd=URLClassLoader.class.getDeclaredMethod("addURL",newClass[]{URL.class});
add.setAccessible(true);
add.invoke(urlClassLoader,newObject[]{url});
urlClassLoader.loadClass(className);
Class>c=urlClassLoader.loadClass(className);
//列出所有方法
// Method[] methods = c.getMethods();
// for (Method m : methods) {
// System.out.println(m.getName());
// }
//调用方法
// if (args == null) {
// c.getMethod(methodName, parameterTypes).invoke(c.newInstance());
// } else {
// c.getMethod(methodName, parameterTypes).invoke(c.newInstance(), args);
// }
// urlClassLoader.close();
}catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
//无参方法的例子
classLoader(newFile("E:\\TestJar.jar"),"com.chentaoqian.test.Test","sayHi",null,null);
//有参方法的例子
classLoader(newFile("E:\\TestJar.jar"),"com.chentaoqian.test.Test","sayBye",newString[]{"byebye"},newClass>[]{String.class});
}
}
使用jdk自带的classloader,可以正确的加载jar包中的指定class,但如果要加载单独的一个class文件,则会报错。
使用自定义类加载器,加载一个class
通过编写一个类继承自ClassLoader,并重写findClass方法
package com.chentaoqian.classloaderdemo;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassloader2 extends ClassLoader {
private String fileName;
public MyClassloader2(String fileName) {
this.fileName = fileName;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
File file = new File(fileName);
try {
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
while ((len = fis.read()) != -1) {
bos.write(len);
}
byte[] data = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name, data, 0, data.length);
} catch (Exception ex) {
ex.printStackTrace();
}
return super.findClass(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
packagecom.chentaoqian.classloaderdemo;
importjava.io.ByteArrayOutputStream;
importjava.io.File;
importjava.io.FileInputStream;
publicclassMyClassloader2extendsClassLoader{
privateStringfileName;
publicMyClassloader2(StringfileName){
this.fileName=fileName;
}
@Override
protectedClass>findClass(Stringname)throwsClassNotFoundException{
Filefile=newFile(fileName);
try{
FileInputStreamfis=newFileInputStream(file);
ByteArrayOutputStreambos=newByteArrayOutputStream();
intlen=0;
while((len=fis.read())!=-1){
bos.write(len);
}
byte[]data=bos.toByteArray();
fis.close();
bos.close();
returndefineClass(name,data,0,data.length);
}catch(Exceptionex){
ex.printStackTrace();
}
returnsuper.findClass(name);
}
}
package com.chentaoqian.classloaderdemo;
import java.lang.reflect.Method;
public class ClassloaderTest {
public static void main(String[] args) {
MyClassloader2 myClassloader = new MyClassloader2("E:\\Test.class");
try {
//加载class文件
Class> c = myClassloader.loadClass("com.chentaoqian.test.Test");
if(c != null){
try {
Object obj = c.newInstance();
// Method method = c.getDeclaredMethod("sayHi", null);
Method method = c.getMethod("sayHi", null);
//通过反射调用Test类的say方法
method.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
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
packagecom.chentaoqian.classloaderdemo;
importjava.lang.reflect.Method;
publicclassClassloaderTest{
publicstaticvoidmain(String[]args){
MyClassloader2myClassloader=newMyClassloader2("E:\\Test.class");
try{
//加载class文件
Class>c=myClassloader.loadClass("com.chentaoqian.test.Test");
if(c!=null){
try{
Objectobj=c.newInstance();
// Method method = c.getDeclaredMethod("sayHi", null);
Methodmethod=c.getMethod("sayHi",null);
//通过反射调用Test类的say方法
method.invoke(obj,null);
}catch(Exceptione){
e.printStackTrace();
}
}
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}
}
}
这种方式可以正确加载class文件。需要注意的是:若该class文件使用了extends,则加载会报错,因为父类找不到。