基本知识点
- 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
- 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
Class类的常用方法
1、getName()
(类、接口、数组类、基本类型或 void)名称。
2、newInstance()
为类创建一个实例,newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
3、getClassLoader()
返回该类的类加载器。
4、getComponentType()
返回表示数组组件类型的 Class。
5、getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
6、isArray()
判定此 Class 对象是否表示一个数组类。
Class的一些使用技巧
1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例如
Object obj = Class.forName(s).newInstance();
2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象。例如:
if(e.getClass() == Employee.class)...
双亲委托模型
类加载器:自顶向下加载,自底向上校验判断是否加载
防止重复加载
resolveClass 类的解析
defineClass 加载字节码生成Class对象
loadClass双亲委托模型的展示可自定义实现
findClass加载字节码生成Class对象 自定义加载器必须实现的
类加载器jar包卸载
URLClassLoader 1.7之后有close方法
类加载器实例应用
JavaSPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。
DriverManager通过ServiceLoader加载
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
被加载的类S
对象实例化S p = service.cast(c.newInstance());【Object——》S类型,校验及转换】
多线程和类加载器
package practise;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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);
System.out.println(Thread.currentThread().getName());
/**
* 类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接 如果要连接类
*/
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 = "D:\\workspace\\practise\\bin\\";
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;
}
}
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalArgumentException,
InvocationTargetException, ClassNotFoundException, FileNotFoundException, InterruptedException {
// BufferedReader in=new BufferedReader(new FileReader("gc.log"));
// FilterInputStream filterInputStream=new DataInputStream(new FileInputStream(""));
// StringBufferInputStream stringBufferInputStream=mew StringBufferInputStream("123");
byte []arrayBytes=new byte[50];
// ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(arrayBytes);
/** 要进行热加载的类名 **/
String name = "practise.Dictionary";
HashMapP<String, String> printer = null;
/*Constructor<?> segmentConstructor = HashMapP.Entry.class
.getConstructor(int.class, String.class,String.class,
HashMapP.Entry.class);
segmentConstructor.setAccessible(true);*/
System.out.println("输入任意字符进行热加载,直接敲回车键退出程序");
try {
ClassLoader appLoader=Thread.currentThread().getContextClassLoader();
Class<?> clazz1 = appLoader.loadClass("practise.HashMapP");
printer=(HashMapP<String, String>) clazz1.newInstance();
printer.put("123", "123");
System.out.println(printer);
// appLoader.loadClass(name);
CustomClassLoader loader = new CustomClassLoader(appLoader, name);
//loader.loadClass();
Class<?> clazz = loader.loadClass("practise.Dictionary");
/**
* 被子加载器加载的类拥有被父加载器加载的类的可见性 Printer是由自定义类加载器加载的,
* 而它的父类IPrinter是由系统类加载器加载的, 因此IPrinter对于Printer具有可见性,
* 因此转型成功,并不会因为类加载器不同导致ClassCastException异常
*/
Constructor<?> constructor = clazz.getConstructor();
constructor.setAccessible(true);
// AccessibleObject[] array;
// constructor.setAccessible(array, true);
//都是可访问状态
// Dictionary map = (Dictionary) constructor
// .newInstance();
// /** 看看是否热加载成功了 **/
// System.out.println(map);
System.out.println(clazz.getClassLoader().toString());
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
ClassLoader loader=Thread.currentThread().getContextClassLoader();
System.out.println(loader.toString());
Dictionary dictionary=new Dictionary<>();
//new实例依然有当前AppClassLoader加载
System.out.println(dictionary.getClass().getClassLoader().toString());
}
});
thread.setContextClassLoader(loader);
Thread.sleep(1000*3);
thread.start();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
双亲委派模型的破坏者
DriverManager在获取外部jdbc中Driver实现类对象时,根加载器是使用不到应用加载器的类,因此通过Thread.getContextLoader上下文加载器得到,即也就是破坏了双亲委派模型,类之间是相互隔离的
几个api获取区别
- getSystemClassLoader:当前应用类加载器,就是classpath目录下
- Thread.getContextLoader:是线程设置的类加载器,默认为父线程的类加载,例如tomcat获取到是WebAppClassLoader,而非ApplicationClassLoader
QA
同一个字节码第二次加载会报异常
类加载、jar加载太多,如何卸载
参考文献
http://blog.csdn.net/javazejian/article/details/73413292