zoukankan      html  css  js  c++  java
  • 浅谈JVM-类加载器结构与双亲委派机制

    一、类加载器结构

      

    1、引导类加载器(bootstrap class loader)

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

      加载扩展类和应用程序类加载器。并制定指定的父类加载器。

    2、扩展类加载器(extensions class loader)

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

    3、应用程序类(application class loader)

      它根据Java应用程序的类路径(classpath,java.class.path)路径类。一般来说,系统(应用程序)类都是由它来加载。

    4、自定义类

      继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

    二、类加载作用与API

    三、双亲委派机制

      双亲委派机制实际上就是使用代理模式(交给其它类加载器完成)。

      某个特定的类加载器在接到加载器请求时,首先将加载任务委托给父加载器,一直高层次加载器委托。若加载器可完成其加载任务,就成功返回;只有父加载器无法完成此加载任务,才自己加载。

      作用:保证java核心库的类型安全。

      思考:如何保证安全?假设我们自定义了java.lang.String类,这属于系统类是不允许被加载的。首先Application Class Loader向上抛,直到抛到Bootstrap Class Loader类,然后Bootstrap Class Loader一看,咦,我核心库里面有java.lang.String类,就直接把rt.jar的代码加载进去。我们自定义的java.lang.String就孤单的被抛弃了。

    四、自定义类加载器

     1 public class FileSystemClassLoader extends ClassLoader {
     2 
     3     //HelloWorld   --> f:/myjava/  HelloWorld.class      
     4     private String rootDir;
     5 
     6     public FileSystemClassLoader(String rootDir){
     7         this.rootDir = rootDir;
     8     }
     9 
    10     @Override
    11     protected Class<?> findClass(String name) throws ClassNotFoundException {
    12 
    13         Class<?> c = findLoadedClass(name);
    14 
    15         //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
    16         if(c!=null){
    17             return c;
    18         }else{
    19             ClassLoader parent = this.getParent();
    20             try {
    21                 c = parent.loadClass(name);       //委派给父类加载
    22             } catch (Exception e) {
    23 //                e.printStackTrace();
    24             }
    25 
    26             if(c!=null){
    27                 return c;
    28             }else{
    29                 byte[] classData = getClassData(name);
    30                 if(classData==null){
    31                     throw new ClassNotFoundException();
    32                 }else{
    33                     c = defineClass(name, classData, 0,classData.length);
    34                 }
    35             }
    36 
    37         }
    38 
    39         return c;
    40 
    41     }
    42 
    43     private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
    44         String path = rootDir +"/"+ classname.replace('.', '/')+".class";
    45 
    46 //        IOUtils,可以使用它将流中的数据转成字节数组
    47         InputStream is = null;
    48         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    49         try{
    50             is  = new FileInputStream(path);
    51 
    52             byte[] buffer = new byte[1024];
    53             int temp=0;
    54             while((temp=is.read(buffer))!=-1){
    55                 baos.write(buffer, 0, temp);
    56             }
    57 
    58             return baos.toByteArray();
    59         }catch(Exception e){
    60             e.printStackTrace();
    61             return null;
    62         }finally{
    63             try {
    64                 if(is!=null){
    65                     is.close();
    66                 }
    67             } catch (IOException e) {
    68                 e.printStackTrace();
    69             }
    70             try {
    71                 if(baos!=null){
    72                     baos.close();
    73                 }
    74             } catch (IOException e) {
    75                 e.printStackTrace();
    76             }
    77         }
    78 
    79     }
    80 }

     测试类:当同一个类被不同加载器加载的时候,JVM认为它们不是同一个类。

     1 package com.aaron.classloader;
     2 
     3 public class Test {
     4     public static void main(String[] args) throws Exception{
     5         FileSystemClassLoader loader = new FileSystemClassLoader("f:/myjava");
     6         FileSystemClassLoader loader2 = new FileSystemClassLoader("f:/myjava");
     7         
     8         Class<?> c = loader.loadClass("HelloWorld");
     9         Class<?> c2 = loader.loadClass("HelloWorld");
    10         Class<?> c3 = loader2.loadClass("HelloWorld");
    11 
    12         Class<?> c4 = loader2.loadClass("java.lang.String");
    13         Class<?> c5 = loader2.loadClass("com.aaron.classloader.HelloWorld");
    14         
    15         
    16         System.out.println(c.hashCode());
    17         System.out.println(c2.hashCode());
    18         System.out.println(c3.hashCode());    //同一个类,被不同的加载器加载,JVM认为也是不相同的类
    19         System.out.println(c4.hashCode());
    20         System.out.println(c4.getClassLoader());//引导类加载器
    21         System.out.println(c3.getClassLoader());    //自定义的类加载器
    22         System.out.println(c5.getClassLoader());    //系统默认的类加载器
    23         
    24     }
    25 }

     测试结果

    深入学习参考:http://blog.csdn.net/zhoudaxia/article/details/35824249

  • 相关阅读:
    五小步让VS Code支持AngularJS智能提示
    AngularJS----服务,表单,模块
    AJAX 动态加载后台数据 绑定select
    连接mysql 报错 Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
    MAC中向阿里云服务器上传文件
    使用Navicat连接阿里云ECS服务器上的MySQL数据库
    mysql面试题:字段中@之前字符相同且大于等于2条的所有记录
    2018 最新手机号正则(最新最全)
    php同一个用户同时只能登陆一个, 后登陆者踢掉前登陆者(排他登陆)
    php 单冒号 、双冒号的用法
  • 原文地址:https://www.cnblogs.com/qiuyong/p/6407900.html
Copyright © 2011-2022 走看看