文档章节

Java程序员的必备知识-类加载机制详解

fengsehng
 fengsehng
发布于 2016/11/09 09:08
字数 1664
阅读 1
收藏 0

类加载器的概念

类加载器是一个用来加载类文件的类。

Java源代码通过javac编译器编译成类文件。然后JVM来执行类文件中的字节码来执行程序。类加载器负责加载文件系统、网络或其他来源的类文件。

JVM中类加载器的树状层次结构

Java 中的类加载器大致可以分成两类,一类是系统提供的,另外一类则是由 Java 应用开发人员编写的。

引导类加载器(bootstrap class loader):

它用来加载 Java 的核心库(jre/lib/rt.jar),是用原生C++代码来实现的,并不继承自java.lang.ClassLoader。

加载扩展类和应用程序类加载器,并指定他们的父类加载器,在java中获取不到。

扩展类加载器(extensions class loader):

它用来加载 Java 的扩展库(jre/ext/*.jar)。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

系统类加载器(system class loader):

它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

自定义类加载器(custom class loader):

除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

Java类加载器基于三个机制:委托、可见性和单一性

委托机制

是指将加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它。

可见性的原理

是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。

单一性原理

是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。

类加载过程详解

这里写图片描述

JVM将类加载过程分为三个步骤:

装载(Load),链接(Link)和初始化(Initialize)

1) 装载:

  查找并加载类的二进制数据;

2)链接:

  验证:确保被加载类信息符合JVM规范、没有安全方面的问题。

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

  解析:把虚拟机常量池中的符号引用转换为直接引用。

3)初始化:

  为类的静态变量赋予正确的初始值。

解析部分解释

Java 中,虚拟机会为每个加载的类维护一个常量池【不同于字符串常量池,这个常量池只是该类的字面值(例如类名、方法名)和符号引用的有序集合。 而字符串常量池,是整个JVM共享的】这些符号(如int a = 5;中的a)就是符号引用,而解析过程就是把它转换成指向堆中的对象地址的相对地址。

类的初始化步骤:

1)如果这个类还没有被加载和链接,那先进行加载和链接

2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)

3)如果类中存在static标识的块,那就依次执行这些初始化语句。

类加载器的工作原理

委托机制

当一个类加载和初始化的时候,类仅在有需要加载的时候被加载。假设你有一个应用需要的类叫作Abc.class,首先加载这个类的请求由Application类加载器委托给它的父类加载器Extension类加载器,然后再委托给Bootstrap类加载器。Bootstrap类加载器会先看看rt.jar中有没有这个类,因为并没有这个类,所以这个请求由回到Extension类加载器,它会查看jre/lib/ext目录下有没有这个类,如果这个类被Extension类加载器找到了,那么它将被加载,而Application类加载器不会加载这个类;而如果这个类没有被Extension类加载器找到,那么再由Application类加载器从classpath中寻找。记住classpath定义的是类文件的加载目录,而PATH是定义的是可执行程序如javac,java等的执行路径。

可见性机制

根据可见性机制,子类加载器可以看到父类加载器加载的类,而反之则不行。所以下面的例子中,当Abc.class已经被Application类加载器加载过了,然后如果想要使用Extension类加载器加载这个类,将会抛出java.lang.ClassNotFoundException异常。

单一性机制

根据这个机制,父加载器加载过的类不能被子加载器加载第二次。虽然重写违反委托和单一性机制的类加载器是可能的,但这样做并不可取。你写自己的类加载器的时候应该严格遵守这三条机制。

java.lang.ClassLoader类介绍

java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java 类,即 java.lang.Class类的一个实例。

ClassLoader提供了一系列的方法,比较重要的方法如:

这里写图片描述

自定义类加载器

public class MyClassLoader extends ClassLoader{

    private String rootPath;

    public MyClassLoader(String rootPath){
        this.rootPath = rootPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //check if the class have been loaded
        Class<?> c = findLoadedClass(name);        
        if(c!=null){
            return c;
        }
        //load the class
        byte[] classData = getClassData(name);
        if(classData==null){
            throw new ClassNotFoundException();
        }
        else{
            c = defineClass(name,classData, 0, classData.length);
            return c;
        }    
    }

    private byte[] getClassData(String className){
        String path = rootPath+"/"+className.replace('.', '/')+".class";

        InputStream is = null;
        ByteArrayOutputStream bos = null;
        try {
            is = new FileInputStream(path);
            bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int temp = 0;
            while((temp = is.read(buffer))!=-1){
                bos.write(buffer,0,temp);
            }
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }            
        }

        return null;        
    }    

}

测试自定义的类加载器

创建一个测试类HelloWorld

package testOthers;

public class HelloWorld {

}

在D盘根目录创建一个testOthers文件夹,编译HelloWorld.java,将得到的class文件放到testOthers文件夹下。

利用如下代码进行测试

public class testMyClassLoader {
    @Test
    public void test() throws Exception{
        MyClassLoader loader = new MyClassLoader("D:");
        Class<?> c = loader.loadClass("testOthers.HelloWorld");
        System.out.println(c.getClassLoader());
    }
}

说明HelloWorld类是被我们的自定义类加载器MyClassLoader加载的

参考:
http://www.importnew.com/6581.html
http://www.cnblogs.com/sunniest/p/4574080.html

本文转载自:http://blog.csdn.net/lpjishu/article/details/52664507

共有 人打赏支持
fengsehng
粉丝 4
博文 284
码字总数 214494
作品 0
朝阳
程序员
Android--面试中遇到的问题总结(三)

《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。这份指南包含了大部分Android开发的基础、进阶知识,不仅可以帮助准备...

sealin
2017/02/22
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
Java虚拟机必学之四大知识要点你掌握了吗?

作为一位 Java 程序员,在尽情享受 Java 虚拟机带来好处的同时,我们还应该去了解和思考“这些技术特性是如何实现的”,去了解最底层的原理。只有熟悉 JVM,你才能在遇到 OutOfMemory 等异常...

Java干货分享
前天
0
0
面试中关于Java虚拟机(jvm)的问题看这篇就够了

最近看书的过程中整理了一些面试题,面试题以及答案都在我的文章中有所提到,希望你能在以问题为导向的过程中掌握虚拟机的核心知识。面试毕竟是面试,核心知识我们还是要掌握的,加油~~~ 下面...

snailclimb
05/12
0
0
Java面试中,遇到这类面试题最吃亏!

从你接触 Java开发到现在,你对 Java最直观的印象是什么呢?是它宣传的 “Compile once, run anywhere”,还是目前看已经有些过于形式主义的语法呢?你对于 Java平台到底了解到什么程度?请你...

Java大蜗牛
07/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Python爬虫:Scrapy框架的安装和基本使用

大家好,本篇文章我们来看一下强大的Python爬虫框架Scrapy。Scrapy是一个使用简单,功能强大的异步爬虫框架,我们先来看看他的安装。 Scrapy的安装 Scrapy的安装是很麻烦的,对于一些想使用S...

糖宝lsh
18分钟前
1
0
Android Dialog几种对话框

1,普通对话框 2,单选对话框 3,多选对话框 4,日期选择对话框 5,时间选择对话框 6,日期选择对话框 7,进度条对话框 普通对话框 AlertDialog.Builder builder = new AlertDialog.Builde...

lanyu96
25分钟前
0
0
awk命令

-F选项的作用是指定分隔符。如果不加-F选项,则以空格或者tab为分隔符。print为打印操作,用来打印某个字段。$1为第1个字段,$2为第2个字段,以此类推。但是$0比较特殊,它表示整行: [root@cen...

野雪球
32分钟前
0
0
一切都靠大数据:滴滴已封禁4.3万人员、车辆

这段时间以来,滴滴出行相继出炉了各种整改措施,包括自身安全建设和外部社会共建,昨日就刚刚宣布正在筹备建立安全监督顾问委员会。 据媒体最新报道,9月30日,上海市交通委员会执法总队、上...

linuxCool
51分钟前
4
0
awk命令用法介绍

10月18日任务 9.6/9.7 awk 1.awk(上)(下) 1.awk 分段操作功能 指定分隔符,并把第一段打印出来,不会改动文件内容 将所有内容打印出来 awk 没有指定分隔符号,则会默认用空格或者空白字符...

hhpuppy
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部