类装载子系统

  在JAVA虚拟机中,负责查找并装载类型的那部分被称为类装载子系统。

  JAVA虚拟机有两种类装载器:启动类装载器和用户自定义类装载器。前者是JAVA虚拟机实现的一部分,后者则是Java程序的一部分。由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间中。

  类装载器子系统涉及Java虚拟机的其他几个组成部分,以及几个来自java.lang库的类。比如,用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,JAVA虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其他对象一样,用户自定义的类装载器以及Class类的实例都放在内存中的堆区,而装载的类型信息则都位于方法区。

  类装载器子系统除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及帮助解析符号引用。这些动作必须严格按以下顺序进行:

  (1)装载——查找并装载类型的二进制数据。

  (2)连接——指向验证、准备、以及解析(可选)。

    ● 验证  确保被导入类型的正确性。

    ● 准备  为类变量分配内存,并将其初始化为默认值。

    ● 解析  把类型中的符号引用转换为直接引用。

  (3)初始化——把类变量初始化为正确初始值。

  每个JAVA虚拟机实现都必须有一个启动类装载器,它知道怎么装载受信任的类,每个类装载器都有自己的命名空间,其中维护着由它装载的类型,所以一个Java程序可以多次装载具有同一个全限定名的多个类型,这样一个类型的全限定名就不足以确定在一个Java虚拟机中的唯一性。因此,当多个类装载器都装载了同名的类型时,为了惟一地标识该类型,还要在类型名称前加上装载该类型(指出它所位于的命名空间)的类装载器标识。位于不同命名空间的相同类无法相互转换,下面程序演示了这一点:

 import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; public class MyClass extends ClassLoader{ private String name;//类加载器的名称 private String path;//加载类的路径 private final String extendName = ".class";//文件扩展名 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} public String getExtendName() {
return extendName;
} public MyClass(String name){
super();
this.name = this.name;
} public MyClass(ClassLoader parent,String name){
super(parent);
this.name=name;
} @Override
public String toString() {
return this.name;
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = this.loadDate(name);
return this.defineClass(name, bytes, 0, bytes.length);
} public byte[] loadDate(String name){
String filename = name.replace(".", "\\");
String filepath = this.path+filename+this.extendName;
File file = new File(filepath);
InputStream in = null;
ByteArrayOutputStream out = null;
byte[] bytes = null;
try {
in = new FileInputStream(file);
int len = 0;
out = new ByteArrayOutputStream();
while((len=in.read())!=-1){
out.write(len);
}
bytes=out.toByteArray();
return bytes;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
} public static void main(String[] args) {
MyClass loader1 = new MyClass("loader1");
loader1.setPath("E:\\test\\loader1");
MyClass loader2 = new MyClass(loader1,"loader2");
loader2.setPath("E:\\test\\loader2"); MyClass loader3 = new MyClass(null,"loader3");
loader3.setPath("E:\\test\\loader3\\");
test(loader1);
test(loader2);
test(loader3); try{
Class classzz = loader3.loadClass("Sample");
Object object = classzz.newInstance();
Sample sample = (Sample)object;
System.out.println(sample.toString());
}catch(Exception e){
e.printStackTrace();
}
} public static void test(ClassLoader loader){
try{
Class classzz = loader.loadClass("Sample");
Object object = classzz.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
}
 public class Sample {
public Sample(){
System.out.println("i am Sample......,加载我的类加载器的名称是:"+this.getClass().getClassLoader().toString());
new Person();
}
}
 public class Person {
public Person(){
System.out.println("i am person......,加载我的类加载器的名称是:"+this.getClass().getClassLoader().toString());
}
}

上面程序通过继承ClassLoader,实现自定义类加载器,在主方法中,创建三个自定义类加载器,其中loader2的父类加载器为loader1,loader3的父类加载器为根类加载器,在E盘下创建三条路径,分别为E:\\test\\loader1,E:\\test\\loader2,E:\\test\\loader3,将三段程序的.class文件分别放置在三个文件夹内,通过DOS命令切换到该目录下,首先运行命令:java MyClass,结果如下:

 E:\test\loader1>java MyClass
i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
i am sample......,加载我的类加载器的名称是:loader3
i am person......,加载我的类加载器的名称是:loader3

从结果可以看出,test1,test2均为系统类加载加载所需要类,对于test3,加载所需类的类加载器为自定义类加载器MyClass,可以肯出虚拟机在加载类的过程中使用父类委托机制,loader3的父类为根类加载器,在JDK中找不到自定义类Sample,所以只能靠自定义类加载器加载该类,对于该自定义类加载器加载的类则位于自己命名空间下,其他明明空间下无法调用,可以通过修改上述程序进行验证,

 //package com.swust.自定义类加载器;

 import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; public class MyClass extends ClassLoader{ private String name;//类加载器的名称 private String path;//加载类的路径 private final String extendName = ".class";//文件扩展名 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} public String getExtendName() {
return extendName;
} public MyClass(String name){
super();
this.name = this.name;
} public MyClass(ClassLoader parent,String name){
super(parent);
this.name=name;
} @Override
public String toString() {
return this.name;
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = this.loadDate(name);
return this.defineClass(name, bytes, 0, bytes.length);
} public byte[] loadDate(String name){
String filename = name.replace(".", "\\");
String filepath = this.path+filename+this.extendName;
File file = new File(filepath);
InputStream in = null;
ByteArrayOutputStream out = null;
byte[] bytes = null;
try {
in = new FileInputStream(file);
int len = 0;
out = new ByteArrayOutputStream();
while((len=in.read())!=-1){
out.write(len);
}
bytes=out.toByteArray();
return bytes;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
} public static void main(String[] args) {
MyClass loader1 = new MyClass("loader1");
loader1.setPath("E:\\test\\loader1\\");
MyClass loader2 = new MyClass(loader1,"loader2");
loader2.setPath("E:\\test\\loader2\\"); MyClass loader3 = new MyClass(null,"loader3");
loader3.setPath("E:\\test\\loader3\\");
// test(loader1);
// test(loader2);
// test(loader3); try{
Class classzz = loader3.loadClass("Sample");
Class classess = loader1.loadClass("Sample");
Object object = classzz.newInstance();
Object object1 = classess.newInstance();
System.out.println("由不同类加载器加载的类类型是否可以转换:"+(object==object1));
// Sample sample = (Sample)object;
// System.out.println(sample.toString());
}catch(Exception e){
e.printStackTrace();
}
} public static void test(ClassLoader loader){
try{
Class classzz = loader.loadClass("Sample");
Object object = classzz.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
}

执行结果如下:

 E:\test\loader1>java -cp .;E:\test\loader3 MyClass
i am sample......,加载我的类加载器的名称是:loader3
i am person......,加载我的类加载器的名称是:loader3
i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
由不同类加载器加载的类类型是否可以转换:false

从结果可以看出,由不同类加载器加载的同一类无法相互引用,虽然都是相同的类Sample,但由于他们位于不同的命名空间中,但新建实例却不是同一对象,这样做也保证了类的唯一性

JavaEE学习之类加载器的更多相关文章

  1. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  2. JVM学习--(六)类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...

  3. JVM学习记录-类加载器

    前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...

  4. JVM学习笔记——类加载器与类加载过程

    类加载器与类加载过程 类加载器ClassLoader 类加载器 ClassLoader 用于把 class 文件装载进内存. 启动类加载器(Bootstrap ClassLoader): 这个类加载使 ...

  5. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  6. JVM的艺术—类加载器篇(二)

    分享是价值的传递,喜欢就点个赞 引言 今天我们继续来深入的剖析类加载器的内容.上节课我们讲了类加载器的基本内容,没看过的小伙伴请加关注.今天我们继续. 什么是定义类加载器和初始化类加载器? 定义类加载 ...

  7. 【JAVAWEB学习笔记】25_基础加强:类加载器、注解 @xxx和动态代理

    基础加强 学习目标 案例-自定义单元测试@MyTest 案例-全局的编码的解决 一.类加载器 1.什么是类加载器,作用是什么? 类加载器就加载字节码文件(.class) 2.类加载器的种类 类加载器有 ...

  8. JVM学习一:JVM之类加载器概况

    18年转眼就3月份都快结束了,也就是说一个季度就结束了:而我也因为年前笔记本坏了,今天刚修好了,那么也应该继续学习和博客之旅了.今年的博客之旅,从JVM开始学起,下面我们就言归正传,进入正题. 一.J ...

  9. Javaweb学习笔记——(二十八)——————Servlet3.0、动态代理、类加载器

    web最后一天:完了. Servlet3.0          一.要求         1.MyEclipse10.0或以上版本         2.发布到Tomcat7.0或以上版本 二.步骤   ...

随机推荐

  1. Exec sql/c

    Exec sql/c 利用高级语言的过程性结构来弥补SQL语言实现复杂应用方面的不足. 嵌入SQL的高级语言称为主语言或宿主语言. 在混合编程中,SQL语句负责操作数据库,高级语言语句负责控制程序流程 ...

  2. SPOJ GSS1 && GSS3 (无更新/更新单点,并询问区间最大连续和)

    http://www.spoj.com/problems/GSS1/ 题意:无更新询问区间最大连续和. 做法:线段树每个节点维护sum[rt],maxsum[rt],lsum[rt],rsum[rt] ...

  3. C++重载运算符的规则

    (1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载. 例如,有人觉得BASIC中用“* *”作为幂运算符很方便,也想在C++中将“* *”定义为幂运算符,用“3* *5”表示3 ...

  4. java自己主动生成验证码

    代码结构: web.xml <? xml version="1.0" encoding="UTF-8"?> <web-app version= ...

  5. hibernate环境配置和使用

    一.hibernate简单介绍                Hibernate是一个开放源码的对象关系映射框架,它对JDBC进行了很轻量级的对象封装,使得Java程序猿能够随心所欲的使用对象编程思维 ...

  6. Android @+id与@id的区别

      Android中的组件需要用一个int类型的值来表示,这个值也就是组件标签中的id属性值.id属性只能接受资源类型的值,也就是必须以@开头的值,例如,@id/abc.@+id/xyz等. 如果在@ ...

  7. Dearmweaver CS6 如何添加emmet 插件

     一.关于emmet插件 已经接触前端工具的小伙伴们早听说过这个插件的鼎鼎大名了吧,emmet可以说是前端工程师的利器,就连老牌dreamweaver 都可以支持,我们怎么好意思拒绝这个好东西呢? 有 ...

  8. activity的打开关闭动画

    Activity的打开关闭或者说相互跳转之间可以设置动画的.默认的打开关闭直接消失或出现,比较不优美,但是有的手机Rom对这个默认做了修改,比如红米HM1,默认的就是新页面自右向左滑动出现,自左向右滑 ...

  9. English - allow to do 与 allow doing 的区别

    英语中并没有allow to do sth这种结构,只有allow doing sth 及allow sb to do sth这两个结构. 你这样记忆可能方便一些: 1. 在主动语态中,如果allow ...

  10. socket(套接字)

    客户端: 创建套接字(socket) 连接服务器(connect) 通信(send,recv或者write,read) 关闭套接字(closesocket) 示例代码: int main(int ar ...