ClassLoader课程现场

来源:互联网 发布:淘宝客服主管都做什么 编辑:程序博客网 时间:2024/04/30 05:29

今天说的是讲ClassLoader,但今天讨论的目的不是要让大家明白ClassLoader究竟是怎么回事, 而是希望大家能够对java的底层机制有个了解。

这里说的底层机制是指jvm之上的java体系,jvm层面的内容我们不讨论。ClassLoader的内容实际上是比较广的,不可能通过一两次说教就能明白。

今天这个课程实际上是帮助大家把java的基本功打扎实一些,这样你在工作中,对应用各种框架有更深的理解,学习新的技术更快。
个人认为,java中的对象有4个对象比较特殊,这4个对象是:ObjectClassThreadClassLoader,你要把java的基本功打扎实,首先要理解这4个类。


更进一步的,要想把java弄的更精,你可以去学一下proxy类,Annotation方面的知识(这不是一个类),序列化方面的知识,socket方面的知识。

 

有一句话大家可能都听过:计算机领域里任何一门技术做到高处,都是相通的,这句话或许有点夸张,但基本上是这样的,要达到这个境界,基础非得很扎实不可。

Object是所有类的根,java是个单根体系结构,所有的对象都是从Object继承出来的。Object9个方法(3个参数不同的wait看做一个方法),如果后面我们有时间,我可以和大家讨论一下这9个方法中几个比较复杂的。
今天这里不是主要讲object的知识,所以不多说这方面的内容了,但是你应该问问自己,是否这些方法都很清晰的掌握了,例如:clone()notify()wait()等。如果没掌握,赶快抓紧时间学一下了。


Class
类更象是构造一个对象的模板,你可以粗浅的理解为一个Class类似乎代表了一个.class文件,并且jvm可以理解就可以了,当然事实上情况并非这么简单。

Class有很多方法,你大致理解newInstance()isAssignableFrom()getResource()forName()等这几个就差不多了。Class类一定是可以序列化的,否则java这个语言的体系就乱套了。

 

Thread类代表的是一个线程,这个类我们不在这里讨论了,相信这里绝大多数人对这个类的理解还差很多。例如同步,yielddeamon等。


前面说的这3个类,ObjectClassThread,这是一个提纲,大家回去后好好体悟一下这几个类。这几个类掌握了,你才大致上说算是进了java程序员的中级门槛了。 
从初级到中级,其实这个门槛我个人觉得就在这几个类的掌握上。
否则你可能会用一些数据库,一些框架,会写一些程序,但是你很难理解那些大师到底在做什么。
你在程序员的这个道路上总是很难有实质的进步。
多看看jdk吧。我的机器上,src.zip那个文件总是解压缩放在那里的。随时可以打开查。

ClassLoader是类加载器,所有类的加载都是通过这个类完成的。ClassLoader有很多方法,这里都不讲,只讲1个:findClass()loadClass这个方法将指定的类装载进来,那么它是如何找到你指定的类的呢,就是通过findClass方法。

protectedClass findClass(String name) 这是这个方法的定义,后面我们做练习的时候,只需要重新定义这个方法就可以了。
ClassLoader
是个很复杂的体系,很难用一根主线串引着就明白,现在我尽量用一根主线引导大家思路去理解这个东西。这个主线我整理的或许不是很好。

首先,你要明白,java中任何一个类,都有一个模板,jvm根据这个模板去构造你的类,有了这个类以后,才可以去实例化,去调用这个类做一些事情。

jvm如何根据这个模板去构造你的类,这就是ClassLoader的作用。
也就是说,ClassLoaderJVM查找和加载类的一种机制。
这点很容易理解的,jvm,也就是java虚拟机如何知道你要用某个类呢,也就是Classloader将其装载进来告诉jvm

目前,你可以暂时理解为这个模板就是你的.class文件。
ClassLoader
用你的.class文件,构造出一个类,这个类是jvm可以理解的东西。
现在继续第二点,第二点就开始复杂起来了。
第二,jvm中有一些内置的类装载器。
总共有3个内置的类装载器
1
BootStrapClassLoader
这个类装载器的作用是:负责加载Java的核心类,包括核心的Java Classjava.langjava.io等,核心的库如rt.jar等。
这句话表面看起来简单,但是理解起来可不是那么容易。
最重要的是:这意味着,你用rt.jar包里的类的时候,这些类都是用Bootstrap加载进来的,即你在这些类上调用.getClassLoader方法会得到这个Bootstrap,不过这里其实你得到的是一个null,这点大家不妨以后试试,这是为什么呢?
这是因为:这个加载器的是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的
也就是说,你不能用这个类加载器做任何事情。
SunJVM中,在执行java的命令中使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定附加的类这点你知道就可以了,用到的机会微乎其微。
一般不需要用Bootstrap来加载你的类,不过你如果想做点坏事的时候,可能会用到。
2
、好,接着说第二个类加载器:ExtensionClassLoader 
它负责加载JRE的扩展目录,也就是JAVA_HOME/jre/ lib/ext或者java.ext.dirs属性所指定的目录下的类。
先问一下,大家明白这里所说的系统属性,例如java.ext.dirs是什么意思吗?
例如运行一个java程序,其命令是:javamypackag.AA
如果要指定这些属性,就用命令:java-Djava.ext.dirs="my dir" AA
这样的属性是java虚拟机已经定义好的,故叫做java的系统属性,这样的属性有很多,我经常用的有java.endorsed.dir等。
运行程序的时候不是有2个参数吗,一个是你程序的参数,一个是jvm的参数。
2个属性做java底层的会经常用到。
因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的JAR类包对所有的JVMsystem classloader都是可见的。
Extension ClassLoader上调用方法:getParent会得到null,即它的parent是前面说的第一个类加载器。
你看你的ide的输出,一般都是:javaw-classpath="..."
classpath
中包含了你的jar

3、好,下面是第三个ClassLoader,这个才是最重要的,对于我们来说。
第三个类加载器叫做:System ClassLoader 
系统类加载器,它负责在JVM被启动时,加载来自在执行java命令中的-classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所

指定的JAR类包和类路径
获得系统类加载器的方法是:ClassLoader.getSystemClassLoader()


好,前面讲的这3个类加载器,就是jvm中内置的加载器了,其作用我们后面还要慢慢品味。
其实Extension ClassLoader你一般也用不到。
java.ext.dirs
java.class.path系统属性都是指定要加载的jar,有什么不同吗 ?
这个问题问的非常好,这也是我们后面课程主要要解决的问题,这个问题是那些大师们所想解决的事情。
在解决你这个问题之前,我先要把ClassLoader最常用的一个模型给大家介绍一下,否则讲不清楚了。
这就是我们这个课程的第三点:父代理机制。
这三个类加载器,JVM加载的时候谁先谁后,他们都分别加载哪些类,在程序起动时,他们都是类对象,
jvm
首先用Bootstrap加载核心类,如java.lang.*,之后用第二个加载ext目录下的类,然后用第三个加载classpath的类。
不过这是从静态的角度说的,事实上,运行的时候,前两步是顺序执行的,第三步往往不确定。
在介绍父代理模型之前,大家要按照这个观点来看待:ClassLoader只是一个类,也仅仅是一个类而已,大家用类的观念来看待Classloader
java
1.2版本开始,Java引入了双亲委托模型。这个模型主要是用来保证java平台的安全的。
其实最重要的安全是什么:就是保证类加载的是一个正确的类。 如果这点都保证不了,我们现在也不用搞java了。
这个模型说起来就一句话:当一个装载器被请求装载某个类时,它首先委托自己的parent去装载,若parent能装载,则返回这个类所对应的Class对象,若parent不能装载,则由parent的请求者去装载。
格外注意:这个parent不是类的父类,这个parentClassLoader的一个成员。 getParent来加载.不是MyClassLoadersuper来加载。
ClassLoader
有个特殊的成员变量,叫做parent

MyClassLoader在加载某个类的时候,首先调用MyClassLoader.getParent(),这也是得到了一个ClassLoader,先用这个来加载,跟谁加载的MyClassLoader无关。
MyClassLoader
也是一个类,这个类也有自己的初始化办法,在初始化的时候你把parent设置好,或者在用的时候设置好,这些都由你程序设计了。
tomcat
的类加载机制大致如下:
           ServerClassLoader
             /         /
  StandardClassLoader  FilterClassLoader
            /
       WarClassLoader.....
你的类往往是通过WarClassLoader来加载的
WarClassLoader
在加载的时候,先看看StandardCL能不能加载,如果能,就返回这个Class对象,如果不能才用自己去加载。
StandardCL在加载的时候,则先看看ServerCL能不能加载,.....
这里,WarClassLoader.getParent()=StandardCL    StandardCL.getParent()=ServerCL 
而事实上,WarClassLoader这个类,注意,这个类可能是由SystemClassLoader或其他ClassLoader加载进来的。
而且,WarCLStandardCLServerCL的父类可能都是URLClassLoader
系统只负责内置的类加载器,刚才说的那几个类加载器都不是内置的
举个例子来说,我有2个应用程序,都用到logging类。
log4j
有好多版本,类的名字都是相同的,这2个应用分别用了不同版本的类。这时如果没有一个明确的划分,就出问题了。

第四点:ClassLoader的特殊性质

1点:继承性。
我构造了一个类加载器:MyClassLoader,它有2个实例:
cl1=new MyClassLoader();
cl2=new MyClassLoader();
现在加载一个特定的类:cl1.loadClass(A1);
现在A1中有个语句:Class.forName("B2");那么这个B2是谁加载进来的? 这个B2也是用cl1来加载 当然,可能是由cl1的父类加载进来的。
cl1=new MyClassLoader();
cl2=new MyClassLoader();
cl1.loadClass("A1");
A1
中有个语句:B2 b=new B2(),这里的对象b的加载器是cl1(或者cl1parent)

2个特点:唯一性。
Class a1 = cl1.loadClass("A1");
Class a2 = cl1.loadClass("A1");
这里的结果是a1=a2; System.out.println(a1==a2);打印的是true

第三点:独立性
Class a1=cl1.loadClass("A1");
Class a2=cl2.loadClass("A2");
System.out.println(a1==a2)
打印的结果是false
这点要注意:
Object o = a2.newInstance();
System.out.println(o instanceof a1);
这个结果是false!!!!!
这个结果可能会出乎大家意料了。大家需要好好理解一下这点。
System.out.println(o instanceof a1);
--》false
System.out.println(o instanceof a2);
--》true
这里大小写是正确的,是a1,不是A1
A1
是你的类,而现在我是:
Class a1=cl1.loadClass("A1");
Class a2=cl2.loadClass("A1");
石头的意思是,通过不同加载器加载的同一个.class是不同的
A2 extends A1
:在这个前提下:
Class a1=cl1.loadClass("A1");
Class a2=cl2.loadClass("A2");
:a1.isAssignableFrom(a2)false!!!
换句话说:
A2 extends A1
:在这个前提下:
Class a1=cl1.loadClass("A1");
Class a2=cl2.loadClass("A2");
A2 o2=o2.newInstance();.
A1 o1=o2;-----
这句话会抛出一个类型无法转换的异常。
isAssignableFrom
:是判断2Class类是否可以赋值。 isAssignableFrom是判断两个Class对象,instanceof是判断一个对象是否是某个Class对象

4个特点:可交叉使用,刚才上面大家也看到了,不同类加载器加载的类,在同一段程序中都可以像正常情况下一样使用,除了类型判断的时候要注意外。
Class a1 = cl1.loadClass("A1");
a1.getClassLoader()
的结果不一点是cl1。这个要注意。
isAssignableFrom
是两个Class对象的判断,是个方法,instanceofjava语法中的一个关键字。
if(a1 instanceof A1) ....
可以说是操作符
A.class
这个类,但是是用2个不同的类加载器加载的,所以这2个类之间没有任何关系,(甚至这2个类只是包名相同,而根本不是一个东西)
Thread.curent.ctxCL
这个东西补充两句吧
Thread.currentThread().getContextClassLoader()
这个类加载器是个隐式的加载器,它有一些特殊的用法
前面那3个加载器你可以看做式静态的加载器,也就是说是放在哪里的,你要用它做什么,你自己去指挥就行了。
而这个当前线程上下文加载器是运行时才确定的。 知道这个就足够了,你可以当作它没有任何作用,要用的时候都是需要显式的调用。

原创粉丝点击