Java类加载器

原创 2015年07月09日 16:52:42
                                     **类的加载器**

1、类的加载器的功能:负责将java类编译完的.class文件加载到java虚拟机中,并生成Class类的实例,进而可以通过newInstance方法生成该类的实例对象。

2、JDK自带的类加载器:引导类加载器,扩展类加载器,系统类加载器。
其中,系统类加载器继承扩展类加载器,扩展类加载器继承引导类加载器。
系统类加载器:加载classpath中记载的类(eclipse环境下),或者是加载当前目录下的.class文件(cmd环境下)。
扩展类加载器:加载jre/lib/ext目录下的.class文件
系统类加载器:加载jdk核心库中的.class文件
可以通过以下代码得到相应的加载器类,测试类ArrayType:

    public class ArrayType {

         public static void main(String[] args) {

            // 当前类的加载器 
            ClassLoader classLoader =  ArrayType.class.getClassLoader();
            while(classLoader!=null){
                System.out.println(classLoader.toString());

                // 当前类的加载器的父类加载器 
                classLoader = classLoader.getParent();
            }
         } 
    }          

注意:该例子只能返回系统类加载器和扩展类加载器,引导类加载器返回的是null,原因是引导类加载器是加载JDK核心库的, JDK的保护机制,如果是引导类加载器,则返回null。
3、自定义类加载器
JDK自带的三类加载器已经满足大部分需求,但是这三类加载器对.class文件的位置都是有要求的,如果我们动态生成的.class 文件(通过java的动态代理或者从网络上下载)不在jdk核心库或jre的扩展库或者classpath中 的时候,jdk自带的加载器无法找到.class 文件,从而程序运行时抛出ClassNotFoundException异常。
自定义类加载器的方法:
继承ClassLoader类
覆写ClassLoader类中的findClass方法
findClass方法需要调用defineClass方法将字节码(.class文件的内容)读入jvm中。
例如如下自定义类加载器:

    import java.io.*;

    public class FileSystemClassLoader extends ClassLoader {
        private String rootDir; 

        public FileSystemClassLoader(String rootDir) { 
              this.rootDir = rootDir; 
         } 

        protected Class<?> findClass(String name) throws ClassNotFoundException { 
              byte[] classData = getClassData(name); 
              if (classData == null) { 
                  throw new ClassNotFoundException(); 
              }  else { 
                  return defineClass(name, classData, 0, classData.length); 
              } 
        } 

        private byte[] getClassData(String className) { 
              String path = classNameToPath(className); 
              try { 
                     InputStream ins = new FileInputStream(path); 
                     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
                     int bufferSize = 4096; 
                     byte[] buffer = new byte[bufferSize]; 
                     int bytesNumRead = 0; 
                     while ((bytesNumRead = ins.read(buffer)) != -1) { 
                             baos.write(buffer, 0, bytesNumRead); 
                     } 
                    return baos.toByteArray(); 
                 } catch (IOException e) { 
                     e.printStackTrace(); 
             } 
             return null; 
        } 

         private String classNameToPath(String className) { 
             return rootDir + File.separatorChar 
                             + className.replace('.', File.separatorChar) + ".class"; 
         }
     } 

注意:ClassLoader中有一个方法叫loadClass,该方法属于jdk自带的代理模式,我们在自定义自己的类加载器的时候,不要覆盖该 方法,jdk的代理模式是指 类加载器在加载类的时候,会首先将其代理给父类加载器让其加载,父类加载器会查看该类是否已 经被加载过,如果没有被加载过就加载该类,如果加载不了(不在所要求的路径中),就掉用findClass方法加载(java多态性调 用子类的findClass方法)。所以自定义加载器只需要覆写findClass方法就可以了。

4、利用自定义加载器来判断Java类是否相同。
判断两个java类是否相同,不仅仅这两个类的名字以及所在的路径相同就可以了,还必须判断加载这两个类的类加载器是否相同。 我们来做如下演示:
对于同一个类,我们采用不同的加载器来加载它,看看它加载到内存中是否一样。

(一)需要验证的类:Sample.java

     public class Sample {
        private Sample instance;
        public void setSample(Object instance) {
            this.instance = (Sample)instance;
        }
     }

(二)自定义类加载器:上述的FileSystemClassLoader.java
(三)测试类:Main.java

    import java.lang.reflect.Method;

    public class Main {

        public static void main(String[] args) {
            String bootPath = "D:\\src";
            FileSystemClassLoader fscl1 = new FileSystemClassLoader(bootPath);
            FileSystemClassLoader fscl2 = new FileSystemClassLoader(bootPath);
            String className = "Sample";
            try{
                Class<?> sample1 = fscl1.loadClass(className);
                Class<?> sample2 = fscl2.loadClass(className);
                Object obj1 = sample1.newInstance();
                Object obj2 = sample2.newInstance();
                Method setSampleMethod = sample1.getMethod("setSample",java.lang.Object.class);
                setSampleMethod.invoke(obj1, obj2);
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    }

结果:java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main.main(Main.java:27)
Caused by: java.lang.ClassCastException: Sample cannot be cast to Sample
at Sample.setSample(Sample.java:7)
… 5 more
总结:我们采用两个不同的加载器fscl1 和fscl2来加载同一个类Sample.class,在调用Sample中的setSample方法时出现异常,原因是fscl1和fscl2将Sample加载到JVM后,产生了两个不同的Class类实例,不同的类之间是不能强制转换的,所以抛出异常,就好比同样的粮食,通过面包厂加工出来的就是面包,但通过大家的肚子加工出来的那又是什么呢,模子不一样,出来的东西就是不一样。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

类加载器(java)

  • 2014-02-26 18:01
  • 26KB
  • 下载

java类加载器

  • 2015-11-09 11:23
  • 76KB
  • 下载

Java类加载器之管理资源和配置文件

先创建一个config.properties,建立一个k-v: className=java.util.HashSet import java.io.FileInputStream; import ...

Java类加载器的详解

  • 2009-09-16 07:48
  • 201KB
  • 下载

Java类加载器原理

  • 2012-05-26 12:03
  • 21KB
  • 下载

深入探讨 Java 类加载器

类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Ja...

自定义Java类加载器

java类加载器<2>

一、自定义类加载器的一般步骤   Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制。一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)