zoukankan      html  css  js  c++  java
  • JavaEE学习之类加载器

    类装载子系统

      在JAVA虚拟机中,负责查找并装载类型的那部分被称为类装载子系统。

      JAVA虚拟机有两种类装载器:启动类装载器和用户自定义类装载器。前者是JAVA虚拟机实现的一部分,后者则是Java程序的一部分。由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间中。

      类装载器子系统涉及Java虚拟机的其他几个组成部分,以及几个来自java.lang库的类。比如,用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,JAVA虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其他对象一样,用户自定义的类装载器以及Class类的实例都放在内存中的堆区,而装载的类型信息则都位于方法区。

      类装载器子系统除了要定位和导入二进制class文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存,以及帮助解析符号引用。这些动作必须严格按以下顺序进行:

      (1)装载——查找并装载类型的二进制数据。

      (2)连接——指向验证、准备、以及解析(可选)。

        ● 验证  确保被导入类型的正确性。

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

        ● 解析  把类型中的符号引用转换为直接引用。

      (3)初始化——把类变量初始化为正确初始值。

      每个JAVA虚拟机实现都必须有一个启动类装载器,它知道怎么装载受信任的类,每个类装载器都有自己的命名空间,其中维护着由它装载的类型,所以一个Java程序可以多次装载具有同一个全限定名的多个类型,这样一个类型的全限定名就不足以确定在一个Java虚拟机中的唯一性。因此,当多个类装载器都装载了同名的类型时,为了惟一地标识该类型,还要在类型名称前加上装载该类型(指出它所位于的命名空间)的类装载器标识。位于不同命名空间的相同类无法相互转换,下面程序演示了这一点:

      1 import java.io.ByteArrayInputStream;
      2 import java.io.ByteArrayOutputStream;
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.FileNotFoundException;
      6 import java.io.IOException;
      7 import java.io.InputStream;
      8 
      9 public class MyClass extends ClassLoader{
     10     
     11     private String name;//类加载器的名称
     12     
     13     private String path;//加载类的路径
     14     
     15     private final String extendName = ".class";//文件扩展名
     16 
     17     public String getName() {
     18         return name;
     19     }
     20 
     21     public void setName(String name) {
     22         this.name = name;
     23     }
     24 
     25     public String getPath() {
     26         return path;
     27     }
     28 
     29     public void setPath(String path) {
     30         this.path = path;
     31     }
     32 
     33     public String getExtendName() {
     34         return extendName;
     35     }
     36     
     37     public MyClass(String name){
     38         super();
     39         this.name = this.name;
     40     }
     41     
     42     public MyClass(ClassLoader parent,String name){
     43         super(parent);
     44         this.name=name;
     45     }
     46 
     47     @Override
     48     public String toString() {
     49         return this.name;
     50     }
     51 
     52     @Override
     53     protected Class<?> findClass(String name) throws ClassNotFoundException {
     54         byte[] bytes = this.loadDate(name);
     55         return this.defineClass(name, bytes, 0, bytes.length);
     56     }
     57     
     58     public byte[] loadDate(String name){
     59         String filename = name.replace(".", "\");
     60         String filepath = this.path+filename+this.extendName;
     61         File file = new File(filepath);
     62         InputStream in = null;
     63         ByteArrayOutputStream out = null;
     64         byte[] bytes = null;
     65         try {
     66              in = new FileInputStream(file);
     67             int len = 0;
     68             out = new ByteArrayOutputStream();
     69             while((len=in.read())!=-1){
     70                 out.write(len);
     71             }
     72             bytes=out.toByteArray();
     73             return bytes;
     74         } catch (FileNotFoundException e) {
     75             e.printStackTrace();
     76         } catch (IOException e) {
     77             e.printStackTrace();
     78         }finally{
     79             try {
     80                 in.close();
     81                 out.close();
     82             } catch (IOException e) {
     83                 e.printStackTrace();
     84             }
     85         }
     86         return null;
     87     }
     88     
     89     public static void main(String[] args) {
     90         MyClass loader1 = new MyClass("loader1");
     91         loader1.setPath("E:\test\loader1");
     92         MyClass loader2 = new MyClass(loader1,"loader2");
     93         loader2.setPath("E:\test\loader2");
     94         
     95         
     96         MyClass loader3 = new MyClass(null,"loader3");
     97         loader3.setPath("E:\test\loader3\");
     98         test(loader1);
     99         test(loader2);
    100         test(loader3);
    101         
    102         try{
    103             Class classzz = loader3.loadClass("Sample");
    104             Object object = classzz.newInstance();
    105             Sample sample = (Sample)object;
    106             System.out.println(sample.toString());
    107         }catch(Exception e){
    108             e.printStackTrace();
    109         }
    110     }
    111     
    112     public static void test(ClassLoader loader){
    113         try{
    114             Class classzz = loader.loadClass("Sample");
    115             Object object = classzz.newInstance();
    116         }catch(Exception e){
    117             e.printStackTrace();
    118         }
    119     }
    120 }
    1 public class Sample {
    2     public Sample(){
    3         System.out.println("i am Sample......,加载我的类加载器的名称是:"+this.getClass().getClassLoader().toString());    
    4         new Person();
    5     }
    6 }
    1 public class Person {
    2     public Person(){
    3         System.out.println("i am person......,加载我的类加载器的名称是:"+this.getClass().getClassLoader().toString());    
    4     }
    5 }

    上面程序通过继承ClassLoader,实现自定义类加载器,在主方法中,创建三个自定义类加载器,其中loader2的父类加载器为loader1,loader3的父类加载器为根类加载器,在E盘下创建三条路径,分别为E:\test\loader1,E:\test\loader2,E:\test\loader3,将三段程序的.class文件分别放置在三个文件夹内,通过DOS命令切换到该目录下,首先运行命令:java MyClass,结果如下:

    1 E:	estloader1>java MyClass
    2 i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    3 i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    4 i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    5 i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    6 i am sample......,加载我的类加载器的名称是:loader3
    7 i am person......,加载我的类加载器的名称是:loader3

        从结果可以看出,test1,test2均为系统类加载加载所需要类,对于test3,加载所需类的类加载器为自定义类加载器MyClass,可以肯出虚拟机在加载类的过程中使用父类委托机制,loader3的父类为根类加载器,在JDK中找不到自定义类Sample,所以只能靠自定义类加载器加载该类,对于该自定义类加载器加载的类则位于自己命名空间下,其他明明空间下无法调用,可以通过修改上述程序进行验证,

      1 //package com.swust.自定义类加载器;
      2 
      3 import java.io.ByteArrayInputStream;
      4 import java.io.ByteArrayOutputStream;
      5 import java.io.File;
      6 import java.io.FileInputStream;
      7 import java.io.FileNotFoundException;
      8 import java.io.IOException;
      9 import java.io.InputStream;
     10 
     11 public class MyClass extends ClassLoader{
     12     
     13     private String name;//类加载器的名称
     14     
     15     private String path;//加载类的路径
     16     
     17     private final String extendName = ".class";//文件扩展名
     18 
     19     public String getName() {
     20         return name;
     21     }
     22 
     23     public void setName(String name) {
     24         this.name = name;
     25     }
     26 
     27     public String getPath() {
     28         return path;
     29     }
     30 
     31     public void setPath(String path) {
     32         this.path = path;
     33     }
     34 
     35     public String getExtendName() {
     36         return extendName;
     37     }
     38     
     39     public MyClass(String name){
     40         super();
     41         this.name = this.name;
     42     }
     43     
     44     public MyClass(ClassLoader parent,String name){
     45         super(parent);
     46         this.name=name;
     47     }
     48 
     49     @Override
     50     public String toString() {
     51         return this.name;
     52     }
     53 
     54     @Override
     55     protected Class<?> findClass(String name) throws ClassNotFoundException {
     56         byte[] bytes = this.loadDate(name);
     57         return this.defineClass(name, bytes, 0, bytes.length);
     58     }
     59     
     60     public byte[] loadDate(String name){
     61         String filename = name.replace(".", "\");
     62         String filepath = this.path+filename+this.extendName;
     63         File file = new File(filepath);
     64         InputStream in = null;
     65         ByteArrayOutputStream out = null;
     66         byte[] bytes = null;
     67         try {
     68              in = new FileInputStream(file);
     69             int len = 0;
     70             out = new ByteArrayOutputStream();
     71             while((len=in.read())!=-1){
     72                 out.write(len);
     73             }
     74             bytes=out.toByteArray();
     75             return bytes;
     76         } catch (FileNotFoundException e) {
     77             e.printStackTrace();
     78         } catch (IOException e) {
     79             e.printStackTrace();
     80         }finally{
     81             try {
     82                 in.close();
     83                 out.close();
     84             } catch (IOException e) {
     85                 e.printStackTrace();
     86             }
     87         }
     88         return null;
     89     }
     90     
     91     public static void main(String[] args) {
     92         MyClass loader1 = new MyClass("loader1");
     93         loader1.setPath("E:\test\loader1\");
     94         MyClass loader2 = new MyClass(loader1,"loader2");
     95         loader2.setPath("E:\test\loader2\");
     96         
     97         
     98         MyClass loader3 = new MyClass(null,"loader3");
     99         loader3.setPath("E:\test\loader3\");
    100 //        test(loader1);
    101 //        test(loader2);
    102 //        test(loader3);
    103         
    104         try{
    105             Class classzz = loader3.loadClass("Sample");
    106             Class classess = loader1.loadClass("Sample");
    107             Object object = classzz.newInstance();
    108             Object object1 = classess.newInstance();
    109             System.out.println("由不同类加载器加载的类类型是否可以转换:"+(object==object1));
    110 //            Sample sample = (Sample)object;
    111 //            System.out.println(sample.toString());
    112         }catch(Exception e){
    113             e.printStackTrace();
    114         }
    115     }
    116     
    117     public static void test(ClassLoader loader){
    118         try{
    119             Class classzz = loader.loadClass("Sample");
    120             Object object = classzz.newInstance();
    121         }catch(Exception e){
    122             e.printStackTrace();
    123         }
    124     }
    125 }

    执行结果如下:

    1 E:	estloader1>java -cp .;E:	estloader3 MyClass
    2 i am sample......,加载我的类加载器的名称是:loader3
    3 i am person......,加载我的类加载器的名称是:loader3
    4 i am sample......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    5 i am person......,加载我的类加载器的名称是:sun.misc.Launcher$AppClassLoader@39443f
    6 由不同类加载器加载的类类型是否可以转换:false

    从结果可以看出,由不同类加载器加载的同一类无法相互引用,虽然都是相同的类Sample,但由于他们位于不同的命名空间中,但新建实例却不是同一对象,这样做也保证了类的唯一性

  • 相关阅读:
    英语初级学习系列-00-Name-介绍自己
    Solidworks实例学习
    数学——泰勒公式
    SolidWorks知识积累系列-01
    彻底弄懂HTTP缓存机制及原理
    基于 Pymsql 数据库连接池
    WEB框架之Flask
    Django使用消息提示简单的弹出个对话框
    代码的调试、运行
    微信公众号本地测试环境搭建(附带内网穿透工具使用)
  • 原文地址:https://www.cnblogs.com/sunfie/p/4780947.html
Copyright © 2011-2022 走看看