自定义类加载器(一)
示例代码
1、首先建一个类Test.java(放在com\demo\tcp\kaka\classloader目录下,编译生成Test.class)
public class Test {
private String a;
public Test(){
}
public Test(String a){
this.a=a;
}
public static void main(String[] args) {
System.out.println("aaaaaaaaaa");
}
}
2、自定义类加载器MyClassLoader.java
public class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) {
byte[] bytes=null;
//将点替换成斜杠
String fileName=name.replaceAll("\\.","/");
StringBuilder sb=new StringBuilder("C:\\Users\\Administrator\\Desktop\\");
sb.append(fileName);
sb.append(".class");
fileName=sb.toString();
try {
InputStream is=new FileInputStream(fileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
byte[] buf=new byte[1024];
int r=0;
while ((r=is.read(buf))!=-1){
bos.write(buf,0,r);
}
bytes=bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name,bytes,0,bytes.length);
}
public static void main(String[] args) throws Exception{
//自定义类加载器对象1
MyClassLoader c1=new MyClassLoader();
String className="com.demo.tcp.kaka.classloader.Test";//loadClass调用的就是findClass()
Class clazz1=c1.loadClass(className);
//自定义类加载器对象2
MyClassLoader c2=new MyClassLoader();
Class clazz2=c2.loadClass(className);
System.out.println(clazz1.getClassLoader());
System.out.println(clazz2.getClassLoader());
if(clazz1!=clazz2){
System.out.println("不同的类加载器对象加载相同的class文件,会产生不同的类对象");
}
Object obj1=clazz1.getDeclaredConstructor(new Class[]{String.class}).newInstance("自定义加载器加载进内存的");
Field fa=clazz1.getDeclaredField("a");
fa.setAccessible(true);//将私有变量设置成可以访问的权限
System.out.println(fa.get(obj1));
}
执行结果
com.demo.tcp.kaka.classloader.MyClassLoader@15db9742
com.demo.tcp.kaka.classloader.MyClassLoader@7852e922
不同的类加载器对象加载相同的class文件,会产生不同的类对象
自定义加载器加载进内存的
注意事项
- 如果将Test.java文件拷贝到idea编辑器中,那么MyClassLoader中的findClass就不会执行了,所以需要复制到外边;因为c1和c2都继承自ClassLoader,所以直接交给上级类加载器加载应用程序类加载器:AppClassLoader;它会首先找到Test.class文件,并加入内存;此时就不会在继续再向下传播调用加载了,因此MyClassLoader不会被调用。
- 类加载器在加载的时候虚拟机会首先调用加载器的私用方法loadClassInternal()而这个方法唯一作用就是调用自己的loadClass()方法,如果loadClass()加载失败了,则会调用自己的findClass()。当然loadClass()也能被重写,但是我们不会这样做,因为这样做的话,所有的类都会走这个方法来加载类;那么虚拟机内置的一些类也会用这个方法里面的逻辑来加载,固定会报错。