• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Java > Java 类加载机制详细介绍

Java 类加载机制详细介绍

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了java笔试题及详细讲解,java详细设计,java项目详细设计,java各知识点详细总结,java培训详细描述等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

一、类加载器

  类加载器(ClassLoader),顾名思义,即加载类的东西。在我们使用一个类之前,JVM需要先将该类的字节码文件(.class文件)从磁盘、网络或其他来源加载到内存中,并对字节码进行解析生成对应的Class对象,这就是类加载器的功能。我们可以利用类加载器,实现类的动态加载。

二、类的加载机制

  在Java中,采用双亲委派机制来实现类的加载。那什么是双亲委派机制?在Java Doc中有这样一段描述:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance
 of ClassLoader has an associated parent class loader. When requested to find a class or resource,
 a ClassLoader instance will delegate the search for the class or resource to its parent class loader
 before attempting to find the class or resource itself. The virtual machine's built-in class loader,
 called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a
 ClassLoader instance.
</div>

 从以上描述中,我们可以总结出如下四点:

1、类的加载过程采用委托模式实现

2、每个 ClassLoader 都有一个父加载器。

3、类加载器在加载类之前会先递归的去尝试使用父加载器加载。

4、虚拟机有一个内建的启动类加载器(bootstrap ClassLoader),该加载器没有父加载器,但是可以作为其他加载器的父加载器。

   Java 提供三种类型的系统类加载器。第一种是启动类加载器,由C++语言实现,属于JVM的一部分,其作用是加载 <Java_Runtime_Home>/lib 目录中的文件,并且该类加载器只加载特定名称的文件(如 rt.jar),而不是该目录下所有的文件。另外两种是 Java 语言自身实现的类加载器,包括扩展类加载器(ExtClassLoader)和应用类加载器(AppClassLoader),扩展类加载器负责加载<Java_Runtime_Home>\lib\ext目录中或系统变量 java.ext.dirs 所指定的目录中的文件。应用程序类加载器负责加载用户类路径中的文件。用户可以直接使用扩展类加载器或系统类加载器来加载自己的类,但是用户无法直接使用启动类加载器,除了这两种类加载器以外,用户也可以自定义类加载器,加载流程如下图所示:

  注意:这里父类加载器并不是通过继承关系来实现的,而是采用组合实现的。

  我们可以通过一段程序来验证这个过程:

public class Test {
}
 
public class TestMain {
  public static void main(String[] args) {
 
    ClassLoader loader = Test.class.getClassLoader();
    while (loader!=null){
      System.out.println(loader);
      loader = loader.getParent();
    }
  }
}
</div>

  上面程序的运行结果如下所示:  

  从结果我们可以看出,默认情况下,用户自定义的类使用 AppClassLoader 加载,AppClassLoader 的父加载器为 ExtClassLoader,但是 ExtClassLoader 的父加载器却显示为空,这是什么原因呢?究其缘由,启动类加载器属于 JVM 的一部分,它不是由 Java 语言实现的,在 Java 中无法直接引用,所以才返回空。但如果是这样,该怎么实现 ExtClassLoader 与 启动类加载器之间双亲委派机制?我们可以参考一下源码:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
  {
    synchronized (getClassLoadingLock(name)) {
      // First, check if the class has already been loaded
      Class<?> c = findLoadedClass(name);
      if (c == null) {
        long t0 = System.nanoTime();
        try {
          if (parent != null) {
            c = parent.loadClass(name, false);
          } else {
            c = findBootstrapClassOrNull(name);
          }
        } catch (ClassNotFoundException e) {
          // ClassNotFoundException thrown if class not found
          // from the non-null parent class loader
        }
 
        if (c == null) {
          // If still not found, then invoke findClass in order
          // to find the class.
          long t1 = System.nanoTime();
          c = findClass(name);
 
          // this is the defining class loader; record the stats
          sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
          sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
          sun.misc.PerfCounter.getFindClasses().increment();
        }
      }
      if (resolve) {
        resolveClass(c);
      }
      return c;
    }
  }
</div>

  从源码可以看出,ExtClassLoader 和 AppClassLoader都继承自 ClassLoader 类,ClassLoader 类中通过 loadClass 方法来实现双亲委派机制。整个类的加载过程可分为如下三步:

  1、查找对应的类是否已经加载。

  2、若未加载,则判断当前类加载器的父加载器是否为空,不为空则委托给父类去加载,否则调用启动类加载器加载(findBootstrapClassOrNull 再往下会调用一个 native 方法)。

  3、若第二步加载失败,则调用当前类加载器加载。

  通过上面这段程序,可以很清楚的看出扩展类加载器与启动类加载器之间是如何实现委托模式的。

      现在,我们再验证另一个问题。我们将刚才的Test类打成jar包,将其放置在 <Java_Runtime_Home>\lib\ext 目录下,然后再次运行上面的代码,结果如下:

     现在,该类就不再通过 AppClassLoader 来加载,而是通过 ExtClassLoader 来加载了。如果我们试图把jar包拷贝到<Java_Runtime_Home>\lib,尝试通过启动类加载器加载该类时,我们会发现编译器无法识别该类,因为启动类加载器除了指定目录外,还必须是特定名称的文件才能加载。

三、自定义类加载器

  通常情况下,我们都是直接使用系统类加载器。但是,有的时候,我们也需要自定义类加载器。比如应用是通过网络来传输 Java 类的字节码,为保证安全性,这些字节码经过了加密处理,这时系统类加载器就无法对其进行加载,这样则需要自定义类加载器来实现。自定义类加载器一般都是继承自 ClassLoader 类,从上面对 loadClass 方法来分析来看,我们只需要重写 findClass 方法即可。下面我们通过一个示例来演示自定义类加载器的流程:

package com.paddx.test.classloading;
 
import java.io.*;
 
/**
 * Created by liuxp on 16/3/12.
 */
public class MyClassLoader extends ClassLoader {
 
  private String root;
 
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    byte[] classData = loadClassData(name);
    if (classData == null) {
      throw new ClassNotFoundException();
    } else {
      return defineClass(name, classData, 0, classData.length);
    }
  }
 
  private byte[] loadClassData(String className) {
    String fileName = root + File.separatorChar
        + className.replace('.', File.separatorChar) + ".class";
    try {
      InputStream ins = new FileInputStream(fileName);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      int bufferSize = 1024;
      byte[] buffer = new byte[bufferSize];
      int length = 0;
      while ((length = ins.read(buffer)) != -1) {
        baos.write(buffer, 0, length);
      }
      return baos.toByteArray();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }
 
  public String getRoot() {
    return root;
  }
 
  public void setRoot(String root) {
    this.root = root;
  }
 
  public static void main(String[] args) {
 
    MyClassLoader classLoader = new MyClassLoader();
    classLoader.setRoot("/Users/liuxp/tmp");
 
    Class<?> testClass = null;
    try {
      testClass = classLoader.loadClass("com.paddx.test.classloading.Test");
      Object object = testClass.newInstance();
      System.out.println(object.getClass().getClassLoader());
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }
  }
}
 
</div>

  运行上面的程序,输出结果如下:

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • java反射应用详细介绍
  • java并发之ArrayBlockingQueue详细介绍
  • Java 类加载机制详细介绍
  • Java 基础之事务详细介绍
  • Java中四种引用类型详细介绍
  • java并发之ArrayBlockingQueue详细介绍
  • Java 类加载机制详细介绍
  • Java中四种引用类型详细介绍

相关文章

  • 2017-05-28详解Java动态加载数据库驱动
  • 2017-05-28JFileChooser实现对选定文件夹内图片自动播放和暂停播放实例代码
  • 2017-05-28Servlet实现多文件上传功能
  • 2017-05-28java ant包中的org.apache.tools.zip实现压缩和解压缩实例详解
  • 2017-05-28xml与Java对象的转换详解
  • 2017-05-28Spring4整合Hibernate5详细步骤
  • 2017-05-28Java System类详解_动力节点Java学院整理
  • 2017-05-28Spring-data-redis操作redis知识总结
  • 2017-05-28详解在Spring3中使用注解(@Scheduled)创建计划任务
  • 2017-05-28C#创建Web应用程序代码实例

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 浅析Java中clone()方法浅克隆与深度克隆
    • java三层架构原理与作用小结
    • Java线程实现抽奖
    • Spring Boot使用Druid和监控配置方法
    • Java NIO:浅析IO模型_动力节点Java学院整理
    • JAVAEE中用Session简单实现购物车功能示例代码
    • spring-boot实现增加自定义filter(新)
    • .properties文件读取及占位符${...}替换源码解析
    • 关于java开发的性能问题总结(必看)
    • 判断二叉树是否为完全二叉树的实例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有