本文共 8187 字,大约阅读时间需要 27 分钟。
相信大多数java程序员都知道classLoader的存在,但大家想过java为什么要设计java 类加载体系吗? java类加载体系的是如何工作的?
我带着这些问题,翻阅了相关资料,写下了本篇博客。
我们先看看有哪些场景会使用到java 类加载 体系:
1, java applet
这个东西估计很多人都没用过,比较古老. 但我们看看它的原理介绍就明白它是干什么的了.
它的原理如下:
含有Applet的网页的HTML文件代码中部带有<applet> 和</applet>这样一对标记,当支持Java的网络浏览器遇到这对标记时,就小应用程序代码并在本地计算机上执行该Applet.
也就是说,把java下载到本地,让本地的jvm来执行这段代码。这里面就涉及到类动态加载的相关接口。
2, tomcat,jboss等web服务器
因为tomcat.jboss本身就是java程序编写的web服务器,那么他们在加载相应的war包程序时,也涉及到类动态加载的相关接口.
还有一些像osgi等也需要涉及到 类动态加载的接口.
这些场景都有下面2个特点:
1, 程序在运行期间才决定是否需要加载某类,而不是在程序启动时决定.
2, 程序在运行期间可能会替换或者移除某些类.
java的类加载体系满足上面的所有的需求,但还有一些更重要的原因促使java必须要有自己的类加载体系, 安全.这个原因我们后面了解了它的原理后会有更深刻的体会.
首先我们需要了解,java类加载体系是什么.
看下面的代码:
public class ClassLoaderExample { public static void main(String[] args) { ClassLoader a = ClassLoaderExample.class.getClassLoader(); while(a != null){ System.out.println(a.toString()); a = a.getParent(); } }}上面的代码输出:
sun.misc.Launcher$AppClassLoader@47415dbf
sun.misc.Launcher$ExtClassLoader@1471cb25就是说:
ClassLoaderExample 这个类是AppClassLoader这个类加载器加载的,AppClassLoader的父加载器是ExtClassLoader,ExtClassLoader的父加载器为null.
public class A { public static void main(String[] args) throws ClassNotFoundException { B b = new B(); }}class B { static { System.out.println("static B"); }}
public class A { public static void main(String[] args) throws ClassNotFoundException { //B b = new B(); Class.forName("B"); System.out.println("hello world."); }}class B { static { System.out.println("static B"); }}
public class A { public static void main(String[] args) throws ClassNotFoundException { //B b = new B();// Class.forName("B"); A.class.getClassLoader().loadClass("B"); System.out.println("hello world."); }}class B { static { System.out.println("static B"); }}编译运行,输出如下:
public static void main(String[] args) throws SQLException, ClassNotFoundException {// B b = new B(); classForName(); System.out.println("hello world."); } public static void classForName() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.jdbc.Driver"); String url = "xxxx"; String pwd = "xxx"; String userNmae = "xxx"; DriverManager.getConnection(url,userNmae,pwd); }
com.mysql.jdbc.Driver这段代码很熟悉吧. 刚学编程时,基本上所有的同学都会接触这段代码.这句Class.forName("com.mysql.jdbc.Driver"); 到底是干什么的?
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can\'t register driver!"); } }
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}重要的代码是这段:
public class A { public static void main(String[] args) throws ClassNotFoundException { Class.forName("C"); }}class B { static { System.out.println("static B"); }}编译运行上面的代码:
public class A { public static void main(String[] args) throws ClassNotFoundException { B b = new B(); }}class B{ C c = new C(); static{ System.out.println("static B"); }}class C{}
经过上面的讨论,总结如下:
1, 类的加载是双亲委派模式.
2,每个类加载器只加载一次同一个类.
3,类的加载是延迟的,按需加载的.
4,Class.forName 和 classloader.loadclass,new 都可以触发类的加载. new 是强依赖,Class.forName比较灵活,事先并不知道要加载的类是谁.