zoukankan      html  css  js  c++  java
  • 类装载器ClassLoader

    1、类装载器的工作机制

      类装载器就是类的字节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,需要以下步骤:

      (1)装载:查找和导入Class文件

      (2)链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的。

        校验:检查载入Class文件数据的正确性。

        准备:给类的静态变量分配存储空间。

        解析:将符号引用转换成直接引用。

      (3)初始化:对类的静态变量、静态代码块执行初始化工作。

      类装载工作由ClassLoader及其子类负责。ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。JVM在运行时会产生3个ClassLoader:根装载器、ExtClassLoader(扩展类装载器)和AppClassLoader(应用类装载器)。其中根装载器不是ClassLoader的子类,它使用C++语言编写,因而在Java中看不到它,根装载器负责装载JRE的核心类库,如JRE目标下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子类,其中ExtClassLoader负责装载JRE扩展目录ext中的JAR类包;AppClassLoader负责装载Classpath路径下的类包。

      这三个类装载器之间存在父子层级关系,即根装载器是ExtClassLoader的父装载器,ExtClassLoader是AppClassLoader的父装载器。在默认情况下,使用AppClassLoader装载应用程序的类。

      实验代码:

      

    public static void main(String[] args) {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            System.out.println("current loader:" + loader);
            System.out.println("parent loader:" + loader.getParent());
            System.out.println("grandparent loader:" + loader.getParent().getParent());
        }
    View Code

      输出:  

    current loader:sun.misc.Launcher$AppClassLoader@18b4aac2
    parent loader:sun.misc.Launcher$ExtClassLoader@66d3c617
    grandparent loader:null
    View Code

      说明:

      根装载器在Java中访问不到,所以返回null

      扩展:

      JVM装载类时使用“全盘负责委托机制

      全盘负责:是指当一个ClassLoader装载一个类是,除非显式地使用另一个ClassLoader,该类所依赖的及引用的类也由这个ClassLoader载入;

      委托机制:是指先委托父装载器寻找目标类,只有在找不到的情况下才能从自己的类路径下查找并装载目标类。

      这样的设计是从安全角度考虑的,试想,如果有人编写了一个恶意的基础类(如java.lang.String)并装载到JVM中,将会引起多么可怕的后果?

    实战经验:

      在此我提供一个工具类,通过改类可以方便的查看JVM从哪个类包中加载指定类:

      访问方式:http://localhost:8080/srcAdd.jsp?className=java.net.URL

      工具类页面:

    <%@page contentType="text/html; charset=GBK" %>
    <%@page import="java.security.*,java.net.*,java.io.*" %>
    <%!
        public static URL getClassLocation(final Class cls) {
            if (cls == null) throw new IllegalArgumentException("null input: cls");
            URL result = null;
            final String clsAsResource = cls.getName().replace('.', '/').concat(".class");
            final ProtectionDomain pd = cls.getProtectionDomain();
            // java.lang.Class contract does not specify if 'pd' can ever be null;
            // it is not the case for Sun's implementations, but guard against null
            // just in case:
            if (pd != null) {
                final CodeSource cs = pd.getCodeSource();
                // 'cs' can be null depending on the classloader behavior:
                if (cs != null) result = cs.getLocation();
                if (result != null) {
                    // Convert a code source location into a full class file location
                    // for some common cases:
                    if ("file".equals(result.getProtocol())) {
                        try {
                            if (result.toExternalForm().endsWith(".jar") ||
                                    result.toExternalForm().endsWith(".zip"))
                                result = new URL("jar:".concat(result.toExternalForm())
                                                       .concat("!/").concat(clsAsResource));
                            else if (new File(result.getFile()).isDirectory())
                                result = new URL(result, clsAsResource);
                        } catch (MalformedURLException ignore) {
                        }
                    }
                }
            }
            if (result == null) {
                // Try to find 'cls' definition as a resource; this is not
                // document.d to be legal, but Sun's implementations seem to         //allow this:
                final ClassLoader clsLoader = cls.getClassLoader();
                result = clsLoader != null ?
                        clsLoader.getResource(clsAsResource) :
                        ClassLoader.getSystemResource(clsAsResource);
            }
            return result;
        }
    %>
    <html>
    <head>
        <title>srcAdd.jar</title>
    </head>
    <body bgcolor="#ffffff">
    使用方法,className参数为类的全名,不需要.class后缀,如
    srcAdd.jsp?className=java.net.URL
    <%
        try {
            String classLocation = null;
            String error = null;
            String className = request.getParameter("className");
    
            classLocation = "" + getClassLocation(Class.forName(className));
            if (error == null) {
                out.print("类" + className + "实例的物理文件位于:");
                out.print("<hr>");
                out.print(classLocation);
            } else {
                out.print("类" + className + "没有对应的物理文件。<br>");
                out.print("错误:" + error);
            }
        } catch (Exception e) {
            out.print("异常。" + e.getMessage());
        }
    %>
    </body>
    </html>
    View Code

      工具类:

      在IDEA断点调试的时候,按Alt+F8,弹出Evluate Expression对话框,在Expression处输入:ClassLocationUtils.where(<类名>.class) 即可获知当前类是从哪个jar包中加载的。

      

    /**
     * tools to find which jar does the class come from
     *
     * @author : chenxh,ascend
     */
    public class ClassLocationUtils {
    
        /**
         * find the location of the class come from
         *
         * @param cls Class
         * @return String
         */
        public static String where(final Class cls) {
            if (cls == null) throw new IllegalArgumentException("null input: cls");
            URL result = null;
            final String clsAsResource = cls.getName().replace('.', '/').concat(".class");
            final ProtectionDomain pd = cls.getProtectionDomain();
            if (pd != null) {
                final CodeSource cs = pd.getCodeSource();
                if (cs != null) result = cs.getLocation();
                if (result != null && "file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") ||
                                result.toExternalForm().endsWith(".zip"))
                            result = new URL("jar:".concat(result.toExternalForm())
                                                   .concat("!/").concat(clsAsResource));
                        else if (new File(result.getFile()).isDirectory())
                            result = new URL(result, clsAsResource);
                    } catch (MalformedURLException ignore) {
                    }
    
                }
            }
            if (result == null) {
                final ClassLoader clsLoader = cls.getClassLoader();
                result = clsLoader != null ?
                        clsLoader.getResource(clsAsResource) :
                        ClassLoader.getSystemResource(clsAsResource);
            }
            return result.toString();
        }
    
    }
    View Code

    码字不易,尊重原创:http://www.cnblogs.com/adeng/p/7596818.html

      

  • 相关阅读:
    SharedPreferences数据、openFileOutput文件、SQLite数据库文件存储位置
    Android Activity 生命周期的透彻理解
    Android中半透明Activity效果另法
    android开发3:四大基本组件的介绍与生命周期
    Activity生命周期的学习以及Logcat的使用
    Android 之 Window、WindowManager 与窗口管理
    Android相关类关系
    android应用开发之Window,View和WindowManager .
    Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析
    Android应用程序窗口(Activity)的窗口对象(Window) 的创建过程分析
  • 原文地址:https://www.cnblogs.com/adeng/p/7596818.html
Copyright © 2011-2022 走看看