`

ClassLoader 学习(1)

 
阅读更多

 

部分内容转自:http://www.cnblogs.com/realviv/articles/1906110.html

 

  1. classloader就是把类文件加载到jvm中供虚拟机使用,先一个例子:  
  2.    
  3. 首先,我定义一个ClassLoaderTest包,然后在这个包内定义一个接口:  
  4. public interface IService {  
  5.  void service();  
  6. }  
  7.    
  8. 然后使用这个接口定义一个实现类:  
  9. public class Service extends IService{  
  10.  @Override  
  11. publicvoidservice(){  
  12. System.out.println("service implements IService");  
  13. }  
  14. }  
  15.    
  16. 然后把这个类定义删掉,把这个类生成的class文件放到d:/ClassLoaderTest文件夹下.  
  17.    
  18. 再做一个实现类:  
  19. public classMain {  
  20.  public static voidmain(String[] args) throwsException {  
  21. URLu = newURL("file:d:/");  
  22. URLClassLoaderucl = newURLClassLoader(newURL[] { u });  
  23. Classc = ucl.loadClass("ClassLoaderTest.Service");  
  24. IServiceobj = (IService) c.newInstance();  
  25. obj.service();  
  26. }  
  27. }  
  28.    
  29. 可以看到,我们通过URL方式,远程加载了类文件.  
  30.    
  31. 一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。在Java中,一个类用其完全匹配类名(fullyqualified classname)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识.  
  32.   
  33. Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。System Class Loader是一个特殊的用户自定义类装载器,由JVM的实现者提供,在编程者不特别指定装载器的情况下默认装载用户类. 系统类装载器(System Class Loader)可以通过ClassLoader.getSystemClassLoader() 方法得到。

 

java的几种ClassLoader

java中,我们可以取得这么以下三个ClassLoader类:

一. ClassLoader基本概念

 

1ClassLoader分类

 

类装载器是用来把类(class)装载进JVM的。

 

JVM规范定义了两种类型的类装载器:启动内装载器(bootstrap)和用户自定义装载器(user-definedclass loader)

JVM在运行时会产生三个ClassLoader:BootstrapClassLoaderExtensionClassLoaderAppClassLoaderBootstrap是用C++编写的,我们在Java中看不到它,Null,JVM自带的类装载器,用来装载核心类库,如java.lang.*等。

AppClassLoaderParentExtClassLoader,而ExtClassLoaderParentBootstrapClassLoader

 

Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。系统类装载器可以通过ClassLoader.getSystemClassLoader()方法得到。

 

系统为什么要分别指定这么多的ClassLoader类呢?

答案在于因为java是动态加载类的,这样的话,可以节省内存,用到什么加载什么,就是这个道理,然而系统在运行的时候并不知道我们这个应用与需要加载些什么类,那么,就采用这种逐级加载的方式

(1)首先加载核心API,让系统最基本的运行起来

(2)加载扩展类

(3)加载用户自定义的类

 

publicstatic void main(String[] args) {

System.out.println(System.getProperty("sun.boot.class.path"));
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
}
 

 

程序结果为:

E:\Myeclipse6.0\jre\lib\rt.jar;E:\Myeclipse 6.0\jre\lib\i18n.jar;E:\Myeclipse6.0\jre\lib\sunrsasign.jar;E:\MyEclipse6.0\jre\lib\JSse.jar;E:\MyEclipse 6.0\jre\lib\jce.jar;E:\MyEclipse6.0\jre\lib\charsets.jar;E:\MyEclipse 6.0\jre\classes

E:\MyEclipse6.0\jre\lib\ext

E:\workspace\ClassLoaderDemo\bin

 

在上面的结果中,你可以清晰看见三个ClassLoader分别加载类的路径;也知道为什么我们在编写程序的时候,要把用到的jar包放在工程的classpath下面啦,也知道我们为什么可以不加载java.lang.*包啦!其中java.lang.*就在rt.jar包中;

 

ClassLoader的加载机制

现在我们设计这种一下Demo:

packagejava.net;

publicclass URL {

privateString path;

publicURL(String path) {

this.path= path;

}

publicString toString() {

returnthis.path + " new Path";

}

}

packagejava.net;

importjava.net.*;

publicclass TheSameClsDemo {

publicstatic void main(String[] args) {

URLurl = new URL("http://www.baidu.com");

System.out.println(url.toString());

}

}

 

在这种情况下,系统会提示我们出现异常,因为我们有两个相同的类,一个是真正的URL,一个是我在上面实现的伪类;出现异常是正常的,因为你想想,如果我们在执行一个applet的时候,程序自己实现了一个String的类覆盖了我们虚拟机上面的真正的String类,那么在这个String里面,不怀好意的人可以任意的实现一些功能;这就造成极不安全的隐患;所以java采用了一种名为“双亲委托”的加载模式;

 

以下是jdk源代码:

 

protectedsynchronized Class<?> loadClass(String name, boolean resolve)

throwsClassNotFoundException

{

//First, check if the class has already been loaded

Classc = findLoadedClass(name);

if(c == null) {

try{

if(parent != null) {

c= parent.loadClass(name, false);

}else {

c= findBootstrapClass0(name);

}

}catch (ClassNotFoundException e) {

//If still not found, then invoke findClass in order to find theclass.

c= findClass(name);

}

}

if(resolve) {

resolveClass(c);

}

returnc;

}

 

 

在上面的代码中,我们可以清晰的看见,我们调用一个ClassLoader加载程序的时候,这个ClassLoader会先调用设置好的parentClassLoader来加载这个类,如果parentnull的话,则默认为BootClassLoader类,只有在parent没有找的情况下,自己才会加载,这就避免我们重写一些系统类,来破坏系统的安全;

 

类与它所依赖的类的classloader机制:

如果一个类是由某个classloader加载,那么这个类依赖的类(非显式的通过某个classloader加载)必须也由该classloader或其父classloader加载,无视子classloader

 

通过thread.getContextClassloader:

thread.getContextClassloader默认返回AppClassLoader,除非你显式setContextClassloader

 

来看一下jdbc中如何使用classloader:

一般我们写一个jdbc程序都会这样:

Class.forName("com.mysql.jdbc.Driver");

Stringurl ="jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=utf-8";

Stringuser = "root";

Stringpsw = "yanyan";

Connectioncon = DriverManager.getConnection(url,user, psw);

为什么需要第一句话?

其实第一句话可以用下面这句话替代:

com.mysql.jdbc.Driverdriver = new com.mysql.jdbc.Driver();

其他都不用变化,有人会问,driver对象从来没有用到.,它的效果就是在调用DriverManagergetConnection方法之前,保证相应的Driver类已经被加载到jvm中,并且完成了类的初始化工作就行了.注意了,如果我们进行如下操作,程序是不能正常运行的,因为这样仅仅使Driver类被装载到jvm中,却没有进行相应的初始化工作。

com.mysql.jdbc.Driverdriver = null;

//or

ClassLoadercl = new ClassLoader();

cl.loadClass("com.mysql.jdbc.Driver");

 

我们都知道JDBC是使用Bridge模式进行设计的,DriverManager就是其中的Abstractionjava.sql.DriverImplementorcom.mysql.jdbc.DriverImplementor的一个具体实现(请参考GOFBridge模式的描述)。大家注意了,前一个Driver是一个接口,后者却是一个类,它实现了前面的Driver接口。

Bridge模式中,AbstractionDriverManager)是要拥有一个ImplementorDriver)的引用的,但是我们在使用过程中,并没有将Driver对象注册到DriverManager中去啊,这是怎么回事呢?jdk文档对Driver的描述中有这么一句:

Whena Driver class is loaded, it should create an instance of itself andregister it with the DriverManager

哦,原来是com.mysql.jdbc.Driver在装载完后自动帮我们完成了这一步骤。源代码是这样的:

packagecom.mysql.jdbc

 

publicclass Driver extends NonRegisteringDriver implements java.sql.Driver{

static{

try{

java.sql.DriverManager.registerDriver(newDriver());

}catch (SQLException E) {

thrownew RuntimeException("Can't register driver!");

}

}

 

publicDriver() throws SQLException {

//Required for Class.forName().newInstance()

}

}

 

再看一下DriverManager.getConnection(url,user, psw);方法:

ClassLoadercallerCL = DriverManager.getCallerClassLoader();

getConnection(url,info, callerCL)

==>

if(callerCL== null){

 callerCL= Thread.currentThread().getContextClassLoader();

}

上面的意思是:代码意思是如果DriverManager类的类加载器为空的话,就使用当前线程的类加载器。仔细想想,DriverManagerrt.jar包中,它是由JDK的启动类加载器加载的,而启动类加载器是C编写的,所以取得的都是空,再者,使用当前线程类加载器的话,那么交由程序编写者来保证能够加载驱动类。而不至于驱动器类无法加载。非常高明的手段~! 

 

 

Class的这种设计引入了一个有趣的模式:

某个框架制定某个API,而这些api的实现是有其他供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)

通过thread.getContextClassloader是来传递classloader(有时候需要thread.setContextClassloader设置好api实现的classloader,用此classloader.getResources找出所有的api实现的具体类名,再用classloader加载之,此时框架都不需要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。

分享到:
评论

相关推荐

    Java ClassLoader学习总结

    主要内容包括 Java类加载机制及加载流程,以及如何定义自己的类加载器,如何实现类的热替换。

    Java中ClassLoader类加载学习总结

    本篇文章主要给大家讲述了Java中ClassLoader类加载的原理以及用法总结,一起学习下。

    Java类加载器ClassLoader用法解析

    主要介绍了Java类加载器ClassLoader用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    实用java学习过程

    其实JDK的学习没有那么简单,关于JDK有两个问题是很容易一直困扰Java程序员的地方:一个是CLASSPATH的问题,其实从原理上来说,是要搞清楚JRE的ClassLoader是如何加载Class的;另一个问题是package和import问题,...

    深入Java虚拟机JVM类加载学习笔记

    深入Java虚拟机JVM类加载学习笔记:jvm java classloader 垃圾回收 gc

    javajdk源码学习-JavaSourceLearn:JDK源码学习

    java jdk源码学习 ...ClassLoader 4 Compiler 4 System 4 Package 4 Void 4 java.util AbstractList 1 AbstractMap 1 AbstractSet 1 ArrayList 1 LinkedList 1 HashMap 1 Hashtable 1 HashSet 1 LinkedHashMa

    Java运行时环境之ClassLoader类加载机制详解

    主要给大家介绍了关于Java运行时环境之ClassLoader类加载机制的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    java面试题宝典

    一、Core Java: 1 — 95 题1 — 24 页 基础及语法: 1 — 61 题1 — 13 页 异常: 62 — 69 题13 — 15 页 集合: 70 — 80 题15 — 18 页 线程: 81 — 90 题18 — 21 页 IO & Socket: 91 — 95 题21 — 24 页 二...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    15.2.2 建立classloader实例 518 15.3 重点复习 520 15.4 课后练习 521 chapter16 自定义泛型、枚举与标注 523 16.1 自定义泛型 524 16.1.1 定义泛型方法 524 16.1.2 使用extends与? 525 16.1.3 使用...

    小白的20天Java学习打卡day2

    1、打开DOS命令窗口,执行java HelloWorld,执行原理 java.exe命令会启动JVM JVM启动之后会启动类加载器ClassLoader ClassLoader会在硬盘上的某个位置搜索HelloWorld.class字节码文件 找到该文件则执行 找不到该文件...

    Java类加载器加载类顺序

    java ClassLoader的学习  java是一门解释执行的语言,由开发人员编写好的java源文件先编译成字节码文件.class形式,然后由java虚拟机(JVM)解释执行,.class字节码文件本身是平台无关的,但是jvm却不是,为了实现...

    Java高并发编程详解:多线程与架构设计 (Java核心技术系列)

    部分主要阐述Thread的基础知识,详细介绍线程的API使用、线程安全、线程间数据通信,以及如何保护共享资源等内容,它是深入学习多线程内容的基础。 第二部分引入了ClassLoader,这是因为ClassLoader与线程不无关系,...

    2015-11-ClassLoaders-Selajev.pdf

    针对Java ClassLoaders的学习文章,让你全面掌握classloader的特性

    AndroidComponentPlugin:Android上简单实现四大组件的插件化,供学习使用

    插件化代码的编写,涉及到的知识点主要有java中的反射,动态代理,静态代理以及android中的AIDL跨进程通信,binder机制,ClassLoader加载机制,四大组件的运行原理等等。本项目代码主要参考和。在此基础上做了大量的...

    JSP开发实用技术整理

    这是本人在开发和学习中长期整理出来的,个人认为比较实用,一共11份word文档,含目录方便查看。 这是第一份word文档目录 ===================================== 1.错误页404或500等显示页面配置 1 2.过滤器配置 1 ...

    Android动态加载之DexClassLoader学习

    DexClassLoader学习源码,使用时,请删除Lib.Java文件,否则会出现loadClass错误。

    java深度历险——王森

    关于JDK有两个问题是很容易一直困扰Java程序员的地方:一个是CLASSPATH的问题,其实从原理上来说,是要搞清楚JRE的ClassLoader是如何加载Class的;另一个问题是package和import问题,如何来寻找类的路径问题。把这两...

    jvm-core-learning-example:关于Java虚拟机核心知识点学习积累的例子,是初学者及虚拟机核心知识巩固的最佳实践

    作者与学习乐园原始码地址:我的作者:项目结构src目录Java虚拟机核心知识点学习代码并测试案例test目录(测试包名与src目录保持一致)对应学习代码包的详细测试案例包目录: ├── org.jvmcore.classloader// 类...

    Java学习笔记

    1.跨平台(平台=OS) 可移植性 在不同的操作系统之上可以不用做任何代码的修 改 直接使用 a) 字节码文件:字节码文件不包括任何内存布 局信息 与操作系统和硬件毫无关系 (Java 的内存分布是在运行的时候才动态分配的...

Global site tag (gtag.js) - Google Analytics