Java classloader和namespace详细介绍

Java classloader和namespace详细介绍

Java虚拟机通过装载、连接和初始化一个JAVA类型,使该类型可以被正在运行的JAVA程序所使用。其中,装载就是把二进制形式的JAVA类型读入JAVA虚拟机中。连接就是把这种已经读入虚拟机的二进制形式的类型数据合并到虚拟机的运行时状态中去。连接阶段分为三个步骤-验证、准备和解析。验证确保了JAVA类型数据格式正确并适于JAVA虚拟机使用。准备负责为该类分配它所需的内存,比如为它的类变量分配内存。解析把常量池中的符号引用转换为直接引用,如内存地址指针。在初始化期间,激活类的静态变量的初始化代码和静态代码块。

装载步骤的最终产品是一个被装载类型的Class类的实例对象,它成为JAVA程序与内部数据结构之间的接口。对于每一个被装载的类型,虚拟机都会相应地为它创建一个Class类的实例。

1 类装载器的安全作用

JAVA类装载器在JAVA安全体系结构中起着最关重要的作用,是JAVA安全沙箱的第一道防线。类装载器体系结构在三个方面对JAVA的沙箱起作用:

1) 它防止恶意代码去干涉善意的代码
2) 它守护了被信任的类库的边界
3) 它将代码归入某类(称为保护域),该类确定了代码可以进行哪些操作。

类装载器体系结构可以防止恶意代码去干涉善意的代码,这是通过为不同的类装载器装入的类提供不同的命名空间来实现的。

2双亲委派模型

JAVA虚拟机规范定义了两种类型的类装载器-启动类装载器和用户自定义类装载器,启动类装载器是JAVA虚拟机实现的一部分,通过继承ClassLoader类,用户可以创建自定义的类装载器来完成特定要求的加载。JAVA虚拟机已经创建了2个自定义类装载器-扩展类装载器和系统类装载器。
每一个用户自定义的类装载器在创建时被分配一个“双亲”parent类装载器。如果没有显示地传递一个双亲类装载器给用户自定义的类装载器的构造方法,系统类装载器就默认被指定为双亲。如果传递到构造方法的是一个已有的用户自定义类装载器的引用,该用户自定义类装载器就作为双亲;如果向构造方法传递了null,启动类装载器就是双亲。
启动类装载器Bootstrap Classloader:它是JAVA虚拟机实现的一部分,是c/c++实现的,它没有双亲。启动类装载器装载JAVA核心库代码。

扩展类装载器Extension Classloader:继承自URLClassLoader,初始化向构造方法传递了null,所以双亲是Bootstrap Classloaser。它从java.ext.dirs扩展目录中装载代码。

系统类装载器Application Classloader:继承自URLClassLoader,双亲是Extension Classloaser。它从CLASSPATH路径中装载应用程序代码。

其中,网络类装载器URLClassLoader是JAVA库提供的一个类装载器,用来从网络其他位置装载类。

双亲孩子类装载器委派链

在双亲委派模型下,当一个装载器被请求装载某个类时,它首先委托自己的双亲parent去装载,若parent能装载,则返回这个类所对应的Class对象,若parent不能装载,则由parent的请求者去装载。

现在假设要求Cindy去装载一个名为java.io.FileReader的类型。Cindy第一件事情就是去找Mom来装载那个类型;Mom所做的第一件事情就是去找Grandma来装载那个类型;而Grandma首先去找启动类装载器去装载。在这个例子中,启动类装载器可以装载那个类型,它就返回代表java.io.FileReader的Class实例给Grandma。Grandma传递该Class的引用 Mom,Mom再回传给Cindy,Cindy返回给程序。

在此模型下,启动类装载器可以抢在扩展类装载器之前去装载类,而扩展类装载器可以抢在系统类装载器之前去装载那个类,系统类装载器又可以抢在网络类装载器之前去装载它。这样,使用双亲-孩子委派链的方式,启动类装载器会在最可信的类库-核心Java API-中首先检查每个被装载的类型,然后,才依次到扩展路径、系统类路径中检查被装载的类型文件。用这种方法,类装载器的体系结构就可以防止不可靠的代码用它们自己的版本来替代可以信任的类。

3命名空间 

由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间。命名空间由一系列唯一的名称组成,每一个被装载的类有一个名字。JAVA虚拟机为每一个类装载器维护一个名字空间。例如,一旦JAVA虚拟机将一个名为Volcano的类装入一个特定的命名空间,它就不能再装载名为Valcano的其他类到相同的命名空间了。可以把多个Valcano类装入一个JAVA虚拟机中,因为可以通过创建多个类装载器从而在一个JAVA应用程序中创建多个命名空间。

1)  初始类装载器/ 定义类装载器

命名空间有助于安全的实现,因为你可以有效地在装入了不同命名空间的类之间设置一个防护罩。在JAVA虚拟机中,在同一个命名空间内的类可以直接进行交互,而不同的命名空间中的类甚至不能觉察彼此的存在,除非显示地提供了允许它们进行交互的机制,如获取Class对象的引用后使用反射来访问。

如果要求某个类装载器去装载一个类型,但是却返回了其他类装载器装载的类型,这种装载器被称为是那个类型的初始类装载器 ;而实际装载那个类型的类装载器被称为该类型的定义类装载器 。任何被要求装载类型,并且能够返回Class实例的引用代表这个类型的类装载器,都是这个类型的初始类装载器。在上面的一个例子中,java.io.FileReader定义类装载器是启动类装载器,Cindy、Mom、Grandma、启动类装载器都是初始类装载器。

虚拟机会为每一个类装载器维护一张列表,列表中是已经被请求过的类型的名字。这些列表包含了每一个类装载器被标记为初始类装载器的类型,它们代表了每一个类装载器的命名空间。虚拟机总是会在调用loadClass()之前检查这个内部列表,如果这个类装载器已经被标记为是这个具有该全限定名的类型的初始类装载器,就会返回表示这个类型的Class实例,这样,虚拟机永远不会自动在同一个用户自定义类装载器上调用同一个名字的类型两次。

2) 命名空间的类型共享

前面提到过只有同一个命名空间内的类才可以直接进行交互,但是我们经常在由用户自定义类装载器定义的类型中直接使用Java API类,这不是矛盾了吗?这是类型共享 原因-如果某个类装载器把类型装载的任务委派给另外一个类装载器,而后者定义了这个类型,那么被委派的类装载器装载的这个类型,在所有被标记为该类型的初始类装载器的命名空间中共享。
例如上面的例子中,Cindy可以共享Mon、Grandma、启动类装载器的命名空间中的类型,Kenny也可以共享 Mon、Grandma、启动类装载器的 命名空间中的 类型,但是Cindy和Kenny的命名空间不能共享。

3) 运行时包

每个类装载器都有自己的命名空间,其中维护着由它装载的类型。所以一个JAVA程序可以多次装载具有同一个全限定名的多个类型。这样一个类型的全限定名就不足以确定在一个JAVA虚拟机中的唯一性。因此,当多个类装载器都装载了同名的类型时,为了唯一表示该类型,还要在类型名称前加上装载该类型的类装载器来表示-[classloader class]。
  在允许两个类型之间对包内可见的成员进行访问前,虚拟机不但要确定这个两个类型属于同一个包,还必须确认它们属于同一个运行时包-它们必须有同一个类装载器装载的。这样,java.lang.Virus和来自核心的java.lang的类不属于同一个运行时包,java.lang.Virus就不能访问JAVA API的java.lang包中的包内可见的成员。

4自定义类装载器

JAVA类型要么由启动类装载器装载,要么通过用户自定义的类装载器装载。启动类装载器是虚拟机实现的一部分,它以与实现无关的方式装载类型,JAVA提供了抽象类java.lang.ClassLoader,用户自定义的类装载器是类ClassLoader的子类实例,它以定制的方式装载类。所有用户自定义类装载器都实例化自ClassLoader的子类。
下面提供一个简单的用户自定义类装载器。

import java.io.*;
public class UserDefinedClassLoader extends ClassLoader
{
 private String directory = "d:/classes/";
 private String extensionType = ".class"; 

 public UserDefinedClassLoader()
 {
  super(); // this set the parent as the AppClassLoader by default
 } 

 public UserDefinedClassLoader( ClassLoader parent )
 {
  super( parent );
 } 

 public Class findClass( String name )
 {
  byte[] data = loadClassData( name ); 

  return defineClass( name, data, 0, data.length );
 } 

 private byte[] loadClassData( String name )
 {
  byte[] data = null;
  try
  {
   FileInputStream in = new FileInputStream( new File( directory + name.replace( '.', '/') + extensionType ) );
   ByteArrayOutputStream out = new ByteArrayOutputStream();
   int ch = 0; 

   while( ( ch = in.read() ) != -1 )
   {
    out.write( ch );
   } 

   data = out.toByteArray();
  }
  catch ( IOException e )
  {
   e.printStackTrace();
  }
  return data;
 }
}
public class Valcano
{
 static
 {
  System.out.println("Valcano Class Initialized");
 } 

 public Valcano()
 {
 }
}
public class ClassLoaderTest
{
 public static void main( String[] args )
 {
  try
  {
   UserDefinedClassLoader userLoader = new UserDefinedClassLoader();
   Class valcanoClass1 = userLoader.loadClass( "Valcano" ); 

   URL url = new URL("file:/d:/classes/" );
   ClassLoader urlLoader = new URLClassLoader( new URL[] { url } );
   Class valcanoClass2 = urlLoader.loadClass( "Valcano" );
   System.out.println( "valcanoClass1 classloaer = " + valcanoClass1.getClassLoader() );
   System.out.println( "valcanoClass2 classloaer = " + valcanoClass2.getClassLoader() ); 

   System.out.println( "valcanoClass1 = valcanoClass2 ? " + ( valcanoClass1 == valcanoClass2 ) );
  }
  catch( Exception e )
  {
   e.printStackTrace();
  }
 }
}

输出结果:

valcanoClass1 classloaer = UserDefinedClassLoader@1fb8ee3
valcanoClass2 classloaer = java.NET.URLClassLoader@14318bb
valcanoClass1 = valcanoClass2 ? false

我们可以看到,有两个不同的Valcano的Class实例被加载到同一个虚拟机中。

另外我们看到Valcano类静态初始化语句没有被执行,意味着类没有被初始化,这是因为JAVA中只有当类被主动使用时类型才会进行初始化。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Java 两种延时thread和timer详解及实例代码

    Java 两种延时thread和timer详解及实例代码 在Java中有时候需要使程序暂停一点时间,称为延时.普通延时用Thread.sleep(int)方法,这很简单.它将当前线程挂起指定的毫秒数.如 try { Thread.currentThread().sleep(1000);//毫秒 } catch(Exception e){} 在这里需要解释一下线程沉睡的时间.sleep()方法并不能够让程序"严格"的沉睡指定的时间.例如当使用5000作为sleep()方法的参数时,线 程

  • Java Process类的详解及实例代码

    Java Process类的详解 前言: 今天用了下Java.lang.Process类,只是初步的学习,并没有深入实践,因为感觉它的用途并不是很大,偶尔才可能用上,如果要经常使用它的人可以自行参考JDk文档. 对Process类的简要说明: Process类是一个抽象类,方法都是抽象的,它封装了一个进程,也就是一个可执行的程序  该类提供进程的输入.执行输出到进程.等待进程的完成和检查进程的退出状态及销毁进程的方法 ProcessBuilder.start()和Runtime.exec方法创建

  • Java 转型(向上或向下转型)详解及简单实例

    在Java编程中经常碰到类型转换,对象类型转换主要包括向上转型和向下转型. 向上转型 我们在现实中常常这样说:这个人会唱歌.在这里,我们并不关心这个人是黑人还是白人,是成人还是小孩,也就是说我们更倾向于使用抽象概念"人".再例如,麻雀是鸟类的一种(鸟类的子类),而鸟类则是动物中的一种(动物的子类).我们现实中也经常这样说:麻雀是鸟.这两种说法实际上就是所谓的向上转型,通俗地说就是子类转型成父类.这也符合Java提倡的面向抽象编程思想.来看下面的代码: package a.b; publ

  • java 使用URLDecoder和URLEncoder对中文进行处理

    java  使用URLDecoder和URLEncoder对中文进行处理 一 URLEncoder HTML 格式编码的实用工具类.该类包含了将 String 转换为 application/x-www-form-urlencoded MIME 格式的静态方法.有关 HTML 格式编码的更多信息,请参阅 HTML 规范. 对 String 编码时,使用以下规则: 字母数字字符 "a" 到 "z"."A" 到 "Z" 和 &qu

  • Java 直接插入排序的三种实现

    直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止. 设数组为a[0-n-1]. 1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1].令i=1 2. 将a[i]并入当前的有序区a[0-i-1]中形成a[0-i]的有序区间. 3. i++并重复第二步直到i==n-1.排序完成. 下面给出严格按照定义书写的代码(由小到大排序): void Insertsort1(int a[

  • Java instanceof用法详解及实例代码

    Java instanceof用法详解 Java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. 用法: result = object instanceof class 参数: Result:布尔类型. Object:必选项.任意对象表达式. Class:必选项.任意已定义的对象类. 说明: 如果 object 是 class 的一个实例,则 instanceof 运

  • Java使用组件编写窗口实现网上文件下载

    本文实例为大家分享了Java使用组件编写窗口下载网上文件的具体代码,供大家参考,具体内容如下 如图 实现代码: package com.rain.get; import java.awt.*; import java.awt.event.*; import java.net.*; import java.io.*; import javax.swing.*; //从网络取得文件 public class GetFileDemo extends JFrame{ JTextField jtfUrl;

  • Java实现图片上传到服务器并把上传的图片读取出来

    在很多的网站都可以实现上传头像,可以选择自己喜欢的图片做头像,从本地上传,下次登录时可以直接显示出已经上传的头像,那么这个是如何实现的呢? 下面说一下我的实现过程(只是个人实现思路,实际网站怎么实现的不太清楚) 实现的思路: 工具:MySQL,eclipse 首先,在MySQL中创建了两个表,一个t_user表,用来存放用户名,密码等个人信息, 一个t_touxiang表,用来存放上传的图片在服务器中的存放路径,以及图片名字和用户ID, T_touxiang表中的用户ID对应了t_user中的i

  • Java classloader和namespace详细介绍

    Java classloader和namespace详细介绍 Java虚拟机通过装载.连接和初始化一个JAVA类型,使该类型可以被正在运行的JAVA程序所使用.其中,装载就是把二进制形式的JAVA类型读入JAVA虚拟机中.连接就是把这种已经读入虚拟机的二进制形式的类型数据合并到虚拟机的运行时状态中去.连接阶段分为三个步骤-验证.准备和解析.验证确保了JAVA类型数据格式正确并适于JAVA虚拟机使用.准备负责为该类分配它所需的内存,比如为它的类变量分配内存.解析把常量池中的符号引用转换为直接引用,

  • Java AOP动态代理详细介绍

    目录 1.IOC与AOP概念 2.为何使用动态代理 2.1 JDK原生动态代理 2.1.1 MathService接口类 2.1.2 MathServiceImpl实现接口类 2.1.3 ProxyFactory动态代理工厂 2.1.4 测试类 2.2 cglib动态代理 2.2.1 MathServiceImpl类 2.2.2 ProxyFactory动态代理工厂 2.2.3 测试类 3.AOP动态代理 3.1 添加对应依赖 3.2 配置spring.xml文件 3.3 MathService

  • java并发之ArrayBlockingQueue详细介绍

    java并发之ArrayBlockingQueue详细介绍 ArrayBlockingQueue是常用的线程集合,在线程池中也常常被当做任务队列来使用.使用频率特别高.他是维护的是一个循环队列(基于数组实现),循环结构在数据结构中比较常见,但是在源码实现中还是比较少见的. 线程安全的实现 线程安全队列,基本是离不开锁的.ArrayBlockingQueue使用的是ReentrantLock,配合两种Condition,实现了集合的线程安全操作.这里稍微说一个好习惯,下面是成员变量的声明. pri

  • java数据类型与二进制详细介绍

    java数据类型与二进制详细介绍 在java中 Int 类型的变量占 4个字节 Long 类型的变量占8个字节 一个程序就是一个世界,变量是这个程序的基本单位. Java基本数据类型 1.        整数类型 2.        小数(浮点数)类型 3.        布尔类型 4.        字符类型 整数类型 整数类型可以表示一个整数,常用的整数类型有:byte,short,int,long Byte  一个字节  -128到127 注:0有两个表示0000 0000正零  1000

  • Java类加载基本过程详细介绍

    Java类加载基本过程详细介绍 基本过程: 根据类的全限定名称加载定义类的二进制字节流. 将字节流代表的静态存储结构转化为方法区的运行时数据结构 内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据访问入口 数组类本身不通过类加载器创建,由java虚拟机直接创建,数组类的元素类型由类加载器加载. 数组类的元素类型:数组去掉所有维度后的类型, 文件格式验证: 0xCAFEBABY 魔数开头: 主次版本号当前虚拟机可处理: 常量类型: 索引执行类型: utf8编码

  • java 注解的基础详细介绍

    java 注解的基础详细介绍 前言 注解是Java引入的一项非常受欢迎的补充,它提供了一种结构化的,并且具有类型检查能力的新途径,从而使得程序员能够为代码加入元数据,而不会导致代码杂乱且难以阅读.使用注解能够帮助我们避免编写累赘的部署描述文件,以及其他生成的文件. 注解的语法比较简单,除了@符号的使用之外,它基本与java固有的语法一致.但由于java源码中提供的内置注解很少,所以大部分同学对注解都不是很了解,虽然我们都接触过,比如java内置的几种注解: @Override,表示当前的方法定义

  • Java多线程的用法详细介绍

    Java多线程的用法详细介绍 最全面的Java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); publi

  • Java中ArrayList类详细介绍

    Java中ArrayList类详细介绍 ArrayList是一个可变长度数组,它实现了List接口,因此它也可以包含重复元素和Null元素,也可以任意的访问和修改元素,随着向 ArrayList 中不断添加元素,其容量也自动增长.不过ArrayList是非同步(同步的意思是如果多个线程同时访问一个实例,任何一个线程对实例做了修改之后,其他线程所访问到的实例应该是修改过的最新的实例)的, 我们经常使用List list = Collections.synchronizedList(new Arra

  • Java规则引擎easy-rules详细介绍

    目录 简介 开始使用 引入依赖 定义规则 使用注解定义规则 使用RuleBuilder定义规则 组合规则 规则优先级 Rules API 定义事实 定义规则引擎 创建规则引擎 规则引擎参数 定义规则监听器 定义规则引擎监听器 表达式语言(EL)支持 EL提供者注意事项 通过编程的方式定义规则 通过规则描述文件定义规则 规则定义中的错误处理 实际栗子 最近在思考一个基于规则进行挑选的技术重构,想通过规则引擎进行实现,借着这个机会正好可以详细了解一下规则引擎.本篇文章将会详细介绍规则引擎easy-r

  • Java中CompletableFuture 的详细介绍

    目录 1.概述 1.0 创建 CompletableFuture 的对象的工厂方法 1.1 non-async 和 async 区别 1.1.1 non-async 示例:注册 action 的时候任务可能已经结束 1.1.2 non-async 示例:注册 action 的时候任务未完成 1.2 Run 类方法 1.3 Accept 类方法 1.4 Apply 类方法 2 单个任务执行完成后执行一个动作(action) 2.0 示例 exceptionally 3 两个任务执行编排 4 多个任

随机推荐