您的位置:首页 > 编程语言 > Java开发

一个自定义类加载器ClassLoader示例

2014-07-12 01:34 453 查看
我们的自定义类加载器

package cn.gd.cjz.class_loader;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
* 自定义类加载器
*/
public class CustomClassLoader extends ClassLoader {
/**类名**/
private String name;
/**通过构造方法设置父类加载器和要热加载的类名**/
public CustomClassLoader(ClassLoader parent , String name) {
super(parent);
if(name == null || name.length() <= 0)
throw new NullPointerException();

this.name = name;
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = null;
/**如果是我们想要热加载的类则调用我们重写的findClass方法来加载**/
if(this.name.equals(name) && !"java".equals(name)){
/**先看看要热加载的类之前是否已经加载过了,因为一个类加载器只能加载一个类一次,加载多次会报异常**/
clazz = findLoadedClass(name);
/**clazz==null说明之前没有加载过**/
if(clazz == null)
clazz = findClass(name);

/**
* 类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接
* 如果要连接类
*/
if(resolve)
resolveClass(clazz);//如果类已连接过,resolveClass方法会直接返回
return clazz;
}
return super.loadClass(name , resolve);
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String fileName = c2f(name);
byte[] bytes = f2b(fileName);
return defineClass(name, bytes, 0, bytes.length);
}

/**
* 类名转为文件名
* @param name
* @return
*/
private String c2f(String name){
/**编译后的class文件存放的目录**/
String baseDir = "F:\\idea_workspace\\Test\\target\\classes\\";
name = name.replace("." , File.separator);
name = baseDir + name + ".class";
return name;
}

/**
* 读取文件byte数组
* @param fileName
* @return
*/
private byte[] f2b(String fileName){
RandomAccessFile file = null;
FileChannel channel = null;
byte[] bytes = null;
try {
/**随机存取文件对象,只读取模式**/
file = new RandomAccessFile(fileName , "r");
/**NIO文件通道**/
channel = file.getChannel();
/**NIO字节缓冲**/
ByteBuffer buffer = ByteBuffer.allocate(1024);
int size = (int) channel.size();
bytes = new byte[size];
int index = 0;
/**从NIO文件通道读取数据**/
while (channel.read(buffer) > 0){
/**字节缓冲从写模式转为读取模式**/
buffer.flip();
while (buffer.hasRemaining()){
bytes[index] = buffer.get();
++index;
}
/**字节缓冲的readerIndex、writerIndex置零**/
buffer.clear();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bytes;
}

/**
* 热加载类
* @return
*/
public Class<?> loadClass(){
try {
return loadClass(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}


将要被热加载的类接口
package cn.gd.cjz.class_loader;

/**
* 测试类接口
*/
public interface IPrinter {
public void print();
}


将要被热加载的类
package cn.gd.cjz.class_loader;

/**
* 测试类
*/
public class Printer implements IPrinter {
@Override
public void print() {
System.out.println("彪悍的人生不需要解释是谁说的?");
}
}

好了,写个测试类来测试一下
package cn.gd.cjz.class_loader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/***
* 自定义类加载器测试类
*/
public class CustomClassTest {
public static void main(String[] args) {
/**要进行热加载的类名**/
String name = "cn.gd.cjz.class_loader.Printer";
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
IPrinter printer = null;
while (true) {
System.out.println("输入任意字符进行热加载,直接敲回车键退出程序");
try {
String line = reader.readLine();
if(line != null && line.length() > 0){
CustomClassLoader loader = new CustomClassLoader(Thread.currentThread().getContextClassLoader() , name);
Class<?> clazz = loader.loadClass();
/**
* 被子加载器加载的类拥有被父加载器加载的类的可见性
* Printer是由自定义类加载器加载的,
* 而它的父类IPrinter是由系统类加载器加载的,
* 因此IPrinter对于Printer具有可见性,
* 因此转型成功,并不会因为类加载器不同导致ClassCastException异常
*/
printer = (IPrinter) clazz.newInstance();
/**看看是否热加载成功了**/
printer.print();
}else{
break;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}


首先运行测试类,输入任意字符看看控制台输出了什么?再把Printer类改成下面的代码,编译后再在控制台输入任意字符,看看控制台又输出了什么?
package cn.gd.cjz.class_loader;

/**
* 测试类
*/
public class Printer implements IPrinter {
@Override
public void print() {
System.out.println("锤子老罗说的。");
}
}


以下是我的测试结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息