目录
目录
摘要:
java之所以跨平台,就是因为jvm对其可以解析的文件做了规范,你的所有的java代码再编译之后会生成Class文件,程序运行的时候载入jvm的就是class文件,class文件是被以二进制流的形式读入jvm内存的。所以jvm的跨平台性,其实就是jvm的跨平台性,此外由于class文件是一种规范,那么我只要保证编译后的class文件维持这个规范的话,就可以被jvm虚拟机识别,所以其他的语言只要遵守class文件规范,其编译后的class文件就可以在jvm上运行。这也做到了与语言的无关性。所以,jvm可以运行在不同的平台,同时运行在jvm上的可以是任何一种遵守class文件生成规范的语言。
1:类加载器做了什么
类加载器,通过一个类的全限定名来获取描述此类的二进制细节流,这就是类加载器的功能。 这个动作不是JVM本身实现的,而是放在了JVM外部来实现,这样做的目的是为了让程序能够自己去决定怎么获取以及获取哪一个类,这整个的加载动作的代码块称为"类加载器"。
2:类的唯一性
类加载器虽然只用于类的初始加载阶段,但是他的功能不止于此,对于任意一个类都需要由加载他的类加载器,以及这个类本省来确定其在JVM中的唯一性。每一个加载器都有自己的唯一的命名空间,如果两个不同的类加载器,同时加在同一个class文件中同一个类,那么被加载之后生成的类并不是同一个类,类的相等性和是不是同一个类加载器加载的息息相关。
3:加载过程 假如我有一个test.class
- 从磁盘或者是请求web上的test.class文件 进行加载
- 若是test.class域中有别的类引用,或者是其有父类,那么类加载器会全部加载相关类
- JVM执行test中的main方法
- 如果main中调用了别的类,那么涉及到的相关类也会依次加载
类加载机制并非只有一个类加载器,每个java程序会涉及到至少三个类加载器:
- 引导类加载器:负责加载系统类通常从JAR 文件,tr.jar中加载,这是jvm不可分割的一部分,通常是用C语言加载的,引导类加载器没有对应的ClassLoader对象,例如String.class.getClassLoader 为null
- 拓展类加载器:用于从/jar/lib/ext 目录加载“标准的拓展”,可以将jar文件放入这里,这样即便没有类路径,一就可以被加载
- 系统类加载器:用于加载应用类,用参数-classpath 命令设置的路径,拓展类加载器和系统类加载器都是java实现的,这一点和引导类加载器不同。他们都是URLClassLoader的实例。
4:类加载器的层次结构
类加载器有父/子结构,除了引导类加载器之外每一个类加载器都有父加载器,根据规定,每一个类加载器工作的时候都会提供其父加载器提供以一个机会,当父加载器失败的时候,他自己才会轮到。例如当系统类加载器压迫加载一个jar的时候,系统类会先让其父加载器,拓展类加载器加载,而拓展类加载器也不先加载,而是让自己的父加载器引导类加载器加载。就是这么个过程。这么做的目的是为了加载不遗漏。
5:加载插件以及层次结构图
假如有一个插件需要加载,这个插件是你自己的JAR,那么上面说了,拓展类以及系统类加载器都是URLClassLoader的实例,那么就可以这么做:
URL url = new URL("/../../plug.jar") URLClassLoader classloader = new URLClassLoader(NEW url[] {url}) Class<?> class = classloader.loadClass("com.demo.test.MyClass")
因为在urlclassloader构造器中没有指定父加载器,那么其父加载器就是系统类加载器,整个加载顺序如下:
所以在你用类加载器进行加载类的时候,因为父/子关系的原因,很多时候 ,对你而言这个调用层次是不可见的,那么也有一些时候你需要自己用指定的类加载器。这个时候你需要了解下面的概念。
每个线程都有一个随类加载器的引用,称为上下文加载器,主线程main的类加载器是,系统类加载器,当新建一个线程的时候,该线程的类加载器默认和创建该线程的线程所用的类加载器是一致的,也就是统统都是系统类加载器,假如我们有一个自定义的类加载器需要制定,可以利用线程上下文的设置。
Thread thread = Thread.currentThread(); t.setContextClassLoader(myloader) ClassLoader classloader = t.getContextClassLoader() Class c = myloader.loadClass("MyClass")
6:自定义类加载器:
public class MyClassLoader extends ClassLoader {
//指定class文件路径
private String path ;
public MyClassLoader(String classPath){
path=classPath;
}
/**
* 重写findClass方法
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class log = null;
// 获取该class文件字节码数组
byte[] classData = getData();
if (classData != null) {
// 将class的字节码数组转换成Class类的实例
log = defineClass(name, classData, 0, classData.length);
}
return log;
}
/**
* 将class文件转化为字节码数组
* @return
*/
private byte[] getData() {
File file = new File(path);
if (file.exists()){
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int size = 0;
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
}else{
return null;
}
}