Java SE 学习笔记 第十七记

1、java.lang.ClassLoader抽象类中的方法若使用String类型作为参数表示一个类的话,需要使用类的二进制名称。每个Class对象都包含一个定义它的ClassLoader的引用。


2、java.lang.ClassLoader抽象类的方法loadClass(String name)接收一个类的二进制名字,用于加载这个类,并由Java虚拟机创建这个类的Class对象作为结果返回(如果没有找到这个类的话就会抛出ClassNotFoundException异常)。需要注意的是,使用loadClass方法加载一个类并不是对这个类的主动使用,因此加载这个类后并不会对这个类进行初始化,必须等到程序对这个类首次主动使用时才会初始化。


3、java.lang.ClassLoader抽象类的很多方法不能直接使用,但是ClassLoader类中提供了静态方法getSystemClassLoader()可以直接使用,返回结果是委托的System ClassLoader加载器对象,系统加载器通常是用来启动应用程序的类加载器。


4、java.lang.ClassLoader类的方法defineClass(String name,byte[] b,int off,int len):该方法可以将b二进制数据转换为一个Class实例,b数组一般从类的class中读取获得。


5、Java虚拟机自带三个类加载器详解:
1)根类加载器(Bootstrap):该类加载器没有父加载器,它负责加载虚拟机的核心类库,如java.lang.*等。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。根类加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,没有继承java.lang.ClassLoader类,由C++编写。
2)扩展类加载器(Extension):它的父加载器为根类加载器,从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jrelibext子目录(扩展目录)下加载类库,如果用户把自己创建JAR文件放在这个目录先,也会自动使用扩展类加载器加载。扩展类加载器由纯java编写,是java.lang.ClassLoader类的子类。
3)系统类加载器(System):也称为应用类加载器,它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载了,他是用户自定义类加载器的默认父加载器。系统类加载器是纯java类,是java.lang.ClassLoader类的子类。


6、用户自定义类加载器:用户自定义的类加载器都必须继承java.lang.ClassLoader类。ClassLoader的构造方法ClassLoader(String name)可以构造一个以系统类加载器为父加载器的类加载器;ClassLoader(ClassLoader loader,String name)可以构造一个以loader加载器为父加载器的类加载器。ClassLoader类是一个抽象类,但是类中却没有抽象方法,所以继承后不重写任何方法也没有问题,但是从逻辑上都必须重写findClass(String name)。findClass方法会在loadClass方法中被调用,用于加载一个类(需要自己写加载一个类的代码)。默认的findClass方法永远抛出一个ClassNotFoundException异常,将会导致使用该自定义类加载器永远无法加载一个类。


7、类的加载器用来把类加载到Java虚拟机中,从JDK1.2版本开始,类的加载器过程采用父亲委托机制以更好的保证Java平台的安全。父亲委托机制中,各个加载器按照父子关系形成了属性结构,除了根类加载器之外,其它的类加载器都有且只有一个父加载器。


8、父亲委托机制的过程:一个类加载器加载一个类是使用该类加载器的loadClass方法。在loadClass方法中会首先调用findLoadedClass(name)方法检测要加载的类是否已经加载,如果没有加载就会判断是否存在父加载器,要是存在父加载器就会调用parent.loadClass(name,false)执行父加载器的loadClass方法,父类加载器中的loadClass执行同样的逻辑检查,也就是递归。它一直追寻到没有父加载器的类加载器,也就是根类加载器,之后就会执行根类加载器的findbootstrapClass0(name)方法加载这个类。要是父类加载器无法加载这个类,就会抛出ClassNotFoundException异常被catch捕获,执行findClass方法。在findClass方法中若仍然无法加载,就会继续往回抛出这个异常,由子类加载器的findClass方法处理,一直到有一个类加载器能够加载为止。如果所有的类加载器都无法加载就只能抛出ClassNotFoundException异常了。简单来说,父亲委托机制的过程就是从该类加载器开始,一直往上找父加载器,直到往上没有父加载器为止,之后就开始使用最顶层的父加载器尝试加载这个类,无法加载则返回尝试使用子类加载器加载,直到父加载器中加载了这个类或者最终自己加载或者抛出异常都无法加载。


9、如果一个类加载器成功加载了一个类,那么这个类加载器就被称为定义类加载器,而定义类加载器以下的子加载器都能够获得定义类加载器返回的Class对象,它们和定义类加载器都被称为初始类加载器。


10、父子加载器之间的关系实际上是包装关系,子加载器组合了一个父加载器,所以父子加载器之间不一定是继承关系(但也可以使继承关系,只要子加载器组合了一个父加载器就可以)。


11、父亲委托机制的优点:能够提高软件系统的安全性,在这个机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意代码代替由父加载器加载的可靠代码。


12、加载器的命名空间:每个类加载器都有自己的命名空间,命名空间由该加载器以其所有父加载器所加载的类组成(但是父加载器的命名空间不能访问到由子加载器加载的类)。在同一个命名空间中,不会出现类的完整名字完全相同的两个类;在不同的命名空间中有可能会出现类的完整名字相同的两个类。


13、运行时包:由同一个类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类的加载器是否相同,只有属于同一运行时包的类才能互相访问和包可见的类和类成员。这样做的目的是为了防止用于自己定义的类冒充核心类库的类去访问核心类库的包可见成员。


14、一个类被一个加载器加载,那么这个类中使用到的其它类也会使用这个加载器去加载,当然,这个加载过程也要符合父亲委托机制。


15、如果两个加载器之间没有直接或者间接的父子关系,那么它们各自加载类相互不可见。


16、一个类被加载、连接和初始化之后就开始了它的生命周期,当代表这个类的Class对象不再被引用(即不可触及)时,Class对象就会结束声明周期,Class对象对应的类在方法区内的数据也会被卸载,从而结束了加载类的声明周期。也就是说,一个类结束生命周期取决于代表它的Class对象是否结束了生命周期。


17、只有用于自定义的类加载器所加载的类可以被卸载,而由Java虚拟机自带的三个类加载器加载的类,在虚拟机的生命周期中都不会被卸载,一旦被Java虚拟机自带的的类加载器加载,它的Class对象将始终可触及。


18、卸载一个类的方法就是取消所有对这个类Class对象的引用,Java虚拟机会根据一定的回收机制给这个Class对象进行标识,并在合适的时间进行回收和卸载。引用Class对象的地方主要有三个:
1)类加载器。类加载器每加载一个类就可以返回被加载类的Class对象,并且不会对这个类进行重复加载,说明在类加载器对象中肯定存在一个集合用户存放所有被它加载过的类的Class对象引用。另外,每一个Class对象总会引用它的类加载器(加载它需要加载的其它类等),说明每个Class对象也拥有一个加载它的ClassLoader对象的引用,使用Class的getClassLoader() 可以获得加载它的类加载器对象。
2)类对象。一个类构造出的所有对象都只有一个共同的Class对象,并且可以通过getClass方法获得这个Class对象,说明所有的类对象都拥有对它的Class对象的引用。
3)直接的Class引用变量引用。

你可能感兴趣的:(java,虚拟机,ClassLoader,Class,类加载器)