类加载机制:
程序启动时,根据入口函数调用相关功能,功能在不同类中即在不同的class文件中,jvm根据类加载机制来动态加载class文件到内存中,只有被加载后才能被调用,否则引发异常
1、装载:查找和导入Class文件
2、链接:其中解析步骤是可以选择的
(a)检查:检查载入的class文件数据的正确性
(b)准备:给类的静态变量分配存储空间
(c)解析:将符号引用转成直接引用
3、初始化:对静态变量,静态代码块执行初始化工作
应用加载一个类,首先委托给父家在第,然后从顶层的bootstrap classloader试图加载,如果没加载成功,则往下传递,直到最下层加载器去到指定文件系统或网络URL中去加载,都没有就ClassNotFoundException,否则将这个找到的类生成一个定义,并加载到内存,返回这个类在内存中的class实例对象
避免重复加载(确保只有一个加载),安全(不能自己写一个String加进去)
类相同 = 类名相同 + 加载该类的类加载器相同
BootStrap ClassLoader JDK核心类库加载(rt.jar/resource.jar/charset.jar等)C++编写,嵌入JVM内核,启动JVM时随之启动,负责加载核心类库并构造下面两种类加载器 "sun.boot.class.path"
Extension ClassLoader 加载扩展类(jre/lib/ext下的jar) "java.ext.dirs"
App ClassLoader 系统类加载器(应用下的jar和class) "java.class.path"
java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm()); } System.out.println(System.getProperty("sun.boot.class.path"));
ClassLoader loader = TestClassLoaderIssue.class.getClassLoader(); while(loader != null) { System.out.println(loader); loader = loader.getParent(); } System.out.println(loader);
sun.misc.Launcher$AppClassLoader@21ff3fcf sun.misc.Launcher$ExtClassLoader@7cb64078 null
为什么要创建自己的类加载器管理类加载:
1. 因为 java提供的只能加载指定目录下的jar和class,其他位置或者网络无法方便加载
2. Class文件需要加密,先将classa进行加密,然后按照规则编写自定义的ClassLoader进行解密,这样我们就可以在程序中加载特定了类,并且这个类只能被我们自定义的加载器进行加载,提高了程序的安全性
3. 可以多版本共存,灵活
继承ClassLoader重写findClass,将class二进制流读入
1. 创建一个类, 编译后将class文件放在d:\com\timer\下
package com.timer; /** * Created by itworker365 on 5/16/2017. */ public class LoadedByClassFileClass { public LoadedByClassFileClass(){ System.out.println("LoadedByClassFileClass loaded111 by class loader"); } }
2. 创建一个类加载器,测试类加载
package com.timer; import java.io.*; /** * Created by itworker365 on 5/16/2017. */ public class TestClassLoaderIssue extends ClassLoader{ private String path = "d://"; private final String fileType = ".class"; public TestClassLoaderIssue() { super(); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = this.loadClassData(name); return this.defineClass(name, data, 0, data.length); } private byte[] loadClassData(String name) { InputStream in = null; byte[] data = null; ByteArrayOutputStream baos = null; try { name = name.replace(".", "\\"); in = new BufferedInputStream(new FileInputStream(new File(path + name + fileType))); baos = new ByteArrayOutputStream(); int ch = 0; while (-1 != (ch = in.read())) { baos.write(ch); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return data; } public static void main(String[] args) { TestClassLoaderIssue loader = new TestClassLoaderIssue(); Class<?> clazz = null; Object object; try { clazz = loader.loadClass("com.timer.LoadedByClassFileClass"); object = clazz.newInstance(); System.out.println(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
加入jvm参数 -XX:+TraceClassLoading,可以看到类加载的具体情况
[Loaded sun.security.action.GetBooleanAction from C:\Program Files\Java\jdk1.7.0_04\jre\lib\rt.jar]
[Loaded com.timer.LoadedByClassFileClass from __JVM_DefineClass__]
[Loaded java.net.InetAddress$Cache from C:\Program Files\Java\jdk1.7.0_04\jre\lib\rt.jar]
查看jar中包含的类:jar -tvf slf4j-api-1.7.5.jar