zoukankan      html  css  js  c++  java
  • Java中ClassLoader浅析.

    一、问题

    请在Eclipse中新建如下类,并运行它:

    复制代码
    1 package java.lang;
    2 
    3 public class Long {
    4     public static void main(String[] args) {
    5         System.out.println("Hi, i am here");
    6     }
    7 }
    复制代码

    你能猜到它的运行如果吗? 不错,就是如下这个样子!

    错误: 在类 java.lang.Long 中找不到 main 方法, 请将 main 方法定义为:
    public static void main(String[] args)
    否则 JavaFX 应用程序类必须扩展javafx.application.Application





    为什么呢,明明我在Long方法类中定义了main方法,为什么说main方法没有定义呢?

    本文将解决以上问题出现的原因。

    二、ClassLoader的作用

    我们都知道java程序写好以后是以.java(文本文件)的文件存在磁盘上,然后,我们通过(bin/javac.exe)编译命令把.java文件编译成.class文件(字节码文件),并存在磁盘上。但是程序要运行,首先一定要把.class文件加载到JVM内存中才能使用的,我们所讲的classLoader,就是负责把磁盘上的.class文件加载到JVM内存中,如下图所示:

    你可以认为每一个Class对象拥有磁盘上的那个.class字节码内容,每一个class对象都有一个getClassLoader()方法,得到是谁把我从.class文件加载到内存中变成Class对象的。

    三、ClassLoader层次结构

    请执行如下程序:

    复制代码
     1 public class Test {
     2     public static void main(String[] args) {
     3         ClassLoader classLoader = Test.class.getClassLoader();
     4         System.out.println(classLoader);
     5         
     6         ClassLoader classLoader1 = classLoader.getParent();
     7         System.out.println(classLoader1);
     8         
     9         ClassLoader classLoader2 = classLoader1.getParent();
    10         System.out.println(classLoader2); 
    11     }
    12 }
    复制代码

    它的输出是:

    sun.misc.Launcher$AppClassLoader@2a139a55
    sun.misc.Launcher$ExtClassLoader@7852e922
    null

    得到了 classLoader2就是null值了。这里其实有三个类加载器:

    (1): 根类加载器(null)

    它是由本地代码(c/c++)实现的,你根本拿不到他的引用,但是他实际存在,并且加载一些重要的类,它加载(%JAVA_HOME%jrelib),如rt.jar(runtime)、i18n.jar等,这些是Java的核心类。

    (2): 扩展类加载器(ExtClassLoader)

    虽说能拿到,但是我们在实践中很少用到它,它主要加载扩展目录下的jar包, %JAVA_HOME%libext

    (3): 应用类加载器(AppClassLoader)

    它主要加载我们应用程序中的类,如Test,或者用到的第三方包,如jdbc驱动包等。

    这里的父类加载器与类中继承概念要区分,它们在class定义上是没有父子关系的。

    四、Class加载时调用类加载器的顺序

    当一个类要被加载时,有一个启动类加载器和实际类加载器的概念,这个概念请看如下分析

    如上面的Test.class要进行加载时,它将会启动应用类加载器进行加载Test类,但是这个应用类加载器不会真正去加载他,而是会调用看是否有父加载器,结果有,是扩展类加载器,扩展类加载器也不会直接去加载,它看自己是否有父加载器没,结果它还是有的,是根类加载器。

    所以这个时候根类加载器就去加载这个类,可在%JAVA_HOME%jrelib下,它找不到com.wangmeng.Test这个类,所以他告诉他的子类加载器,我找不到,你去加载吧,子类扩展类加载器去%JAVA_HOME%libext去找,也找不着,它告诉它的子类加载器 AppClassLoader,我找不到这个类,你去加载吧,结果AppClassLoader找到了,就加到内存中,并生成Class对象。
    这个时间时候启动类加载器(应用类加载器)和实际类加载器(应用类加载器)是同一个.

    这就是Java中著名的委托加载机制,看如下图:

    我们再来看一下 java.lang.Long的加载,按上面分析,应该是由根类加载器加载得到的,此时启动类加载器是应用类加载器,但实际类加载器是根类加载器。

    所以回到我们最开始那个问题,没有main方法是因为执行的根本不是我们自己写的类,执行的是java核心中的那个Long类,当然没有main方法了。 这样就防止我们应用中写的类覆盖掉java核心类。

  • 相关阅读:
    此查询使用的不是 ANSI 外部联接运算符
    centos重启命令
    updatePanel 加载完成后回调JS
    建站推荐十个免费的CMS内容管理系统(Php+mysql)
    [转]最值得拥有的免费Bootstrap后台管理模板
    Got a packet bigger than 'max_allowed_packet' bytes”
    ECshop商城程序常见的96个小问题汇总
    linux 命令
    mysql 存储过程
    千万级记录的Discuz论坛导致MySQL CPU 100%的优化笔记
  • 原文地址:https://www.cnblogs.com/kexianting/p/8526998.html
Copyright © 2011-2022 走看看