Java基本概念之ClassLoader
in Java with 1 comment
Read: 1,162

Java基本概念之ClassLoader

in Java with 1 comment

记录在Java学习的笔记

ClassLoader的基本概念

对于C和C++,它们在编译完成后是直接提供一个可执行文件,这样的话我们可以通过可执行文件去执行程序,而对于java来说,它在由很多的类组成

我们知道,在java的编译过程分为:源文件-->字节码文件--->目标文件

但是这个解释比较笼统,在学习期间,查阅了下我们从编写.java到最后的运行都经历了些什么


分割线的内容来自:JAVA的一次编译,到处运行,你知道多少?
原文地址:传送门

一次编译,到处执行

一次编译是宏观的一次编译

首先我们来看下java的编译过程

分解过程

第一步:Java源代码到.CLASS文件字节码,是java的第一次编译。生成的这个.class文件就是可以到处运行的文件。

下面是第一步执行的过程:

第二步:Java字节码——到目标机器代码;执行是由JVM执行引擎来完成,JAVA的第二次编译。
下面是第二步执行的过程:

到处运行的隐含条件——这里的“到处”的前提是“装有JVM”

看完编译过程在这里说“到处运行”就毫不费力了。因为第二次编译就是在JVM中执行的,也就是在任何一个装有“JVM”的操作系统中完成的。JAVA提供了各种不同平台上的虚拟机制,所以可以实现“到处”。

注意:java并不是编译机制,而是解释机制。

看过了JAVA的两次编译,不要以为JAVA是编译机制,在java中有第一次编译,还有第二次解释来适应不同平台;根据不同的平台进行解释执行。在JVM中的第二次编译过程中(从字节码向机器码转换过程中)真正采用的是“解释”机制。即翻译一句,执行一句,不产生整个的机器代码程序。翻译过程如果不出现错误,就一直进行到完毕,否则将在错误处停止执行。

同一个程序,如果是解释执行的,那么它的运行速度通常比编译为可执行的机器代码的运行速度慢一些。但是,对Java来说,二者的差别不太大。


上文是为我们的正文做了下铺垫,在这里我们注意到了,当我们生成了.classloader后,我们要进行解释程序,解释程序的第一步就是类装载器,即ClassLoader

我们知道我们的java程序由很多的.java组成,当我们执行编译后,第一步会执行的生成.class,但是我们使用的时候是通过解释程序然后进行装入到内存进行使用,但是如果这些都装入有点浪费,所以java会根据需要依次装入内存

ClassLoader是JVM实现的重要的一部分,ClassLoader包括bootstrap class loader ,ClassLoader在JVM运行的时候加载java核心的API,以满足java程序的最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义,是指通过Java程序实现的两个ClassLoader:一个是ExtClassLoader,它的作用是用来夹在Java扩展的API,也就是/lib/ext中的类;第二个是AppClassLoader,它是用来加载用户机器上的CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader情况下,程序员自定义类就是由该ClassLoader进行加载

ClassLoader的加载流程

当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后通过AppClassLoader加载CLASSPATH目录下定义的Class

下面我们来看下Class loadClass代码:

 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;
        }

//JDK 1.8

从上面代码中可以看出:一个类加载的过程使用了父类委托模式,那么为什么要使用这种模式呢?

ClassLoader的方法

Class类中有个静态的方法forName,这个方法和ClassLoader中的loadClass方法目的是一样的,都是用来加载class的,但是这两者还是有区别的

Class clazz = Class.forName("something");
// 等同于:Class.forName("something",true , CALLCLASS.class.getClassLoader())

或者

ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("something");

Class.forName()调用Class.forName(name , initialize , loader) ;

第二个参数:initialize,用于设置加载类的时候是否连接该类

关于连接:JVM在加载类的时候,需要经过三个步骤:装载、连接、初始化

LoadClass方法定义是protected,该方法包含两个参数,第二个默认为false

相关文章

Responses
  1. 基础好才是真的好。

    Reply