欢迎使用CSDN-markdown编辑器
来源:互联网 发布:淘宝上买dota2饰品条件 编辑:程序博客网 时间:2024/06/18 17:47
众所周知,JVM有4层类加载器,他们是:
分别加载自己负责的那个区域内的class文件的加载。
加载的类型由 包名+类型名称和加载他的那个加载器的名称共同确定。
类加载器加载过程:
第一步:判断他的父类是否已经被加载,如果没有,就先加载他的父类。第二步:加载完成后进行连接动作:(验证、准备、解析)。第三步:视情况需要判断是否进行类的初始化。类的初始化由5个动作触发,并且类的生命周期内只会初始化一次,当多个线程共同来进行初始化动作时,JVM会保证初始化动作的线程安全,即只有一个线程可以进行初始化动作,其他线程开始阻塞,当初始化动作完成之后其他线程跳过初始化阶段,直接进行下边的工作,这是由JVM保证的(单例模式的懒加载实现方式就是由于这个原因)。
加载完成之后的结果是:在JVM的堆中形成了一个Class的实例对象。
在方法区中形成了描述类的相关类型信息的数据结构,包括:类型全名,访问修饰符,父类名称,实现的接口列表,类字段列表、方法的字节码信息,一个指向加载这个类的类加载器的引用,一个指向堆中class对象的引用。
双亲委派模型指的是:当一个类加载器的loadClass方法被调用时,他的第一选择是去把这个类型交给他的父类加载器去加载,当他的父类不存在或者无法顺利加载时,才会调用自己的findClass方法进行加载工作,findClass会找到class字节码文件,将其转化为字节流,然后调用defineClass方法(这是一个native方法)他它会自己将流数据转化为JVM中的数据结构。
基本思路就是继承ClassLoader类,或者继承URLClassLoader类,重写他的findClass方法。
目录结构:
public class MyClassLoader extends URLClassLoader { public MyClassLoader(URL[] urls, String baseDir) { super(urls); this.baseDir = baseDir; } public MyClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } private String baseDir = ""; @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] bs = null; try { bs = findClassByte(name); } catch (IOException e) { e.printStackTrace(); } Class c = defineClass(name,bs,0,bs.length); if (c == null) throw new ClassFormatError(); return c; } private byte[] findClassByte(String name) throws IOException { InputStream in = ClassLoader.getSystemResourceAsStream(getFileName(name)); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int length = -1; while((length = in.read(data))>0){ bos.write(data,0,length); } in.close(); bos.close(); return bos.toByteArray(); } /** * 做class的路径转换工作 * @param name * @return */ /* private String getFileName(String name){ StringBuilder sb = new StringBuilder(baseDir); name = name.replace(".", File.separator)+".class"; return sb.append(File.separator+name).toString(); }*/ private String getFileName(String name){ StringBuilder sb = new StringBuilder("");// name = name.replace(".", File.separator)+".class"; //处理最后一个斜杠 String begin = name.substring(0,name.lastIndexOf('.')); String end = name.substring(name.lastIndexOf('.')); end = end.replace(".",File.separator); sb.append(begin); sb.append(end); sb.append(".class"); System.out.println("name:"+sb.toString()); return sb.toString(); }}
需要注意的地方在于:
在IDEA中,使用getResourceAsStream();方法获取项目中的资源的时候路径是这样的:
InputStream in =ClassLoader.getSystemResourceAsStream("com.zf.jvm/User.class");
什么要这样写,本人水平有限,请高手来解答。
总之要在实现的ClassLoader中加载资源的时候必须进行名称的转换。
下边是测试代码:
@Test public void test3() throws Exception { URL urls[] = new URL[10]; MyClassLoader myClassLoader = new MyClassLoader(urls,""); Class c = myClassLoader.loadClass("com.zf.jvm.User");// System.out.println(c == com.zf.jvm.User.class); System.out.println(c.getClassLoader());// com.zf.jvm.User user = new com.zf.jvm.User();// System.out.println(user.getClass() == c); }
最终成功打印出加载他的那个类。可以根据Class类利用反射机制生成对象,也是很有用的,先实现类的动态加载,并且类加载器是根据需要进行动态加载的,并不会把路径上所有的Class文件都进行加载而是根据需要动态加载,Class.forName()方法会导致类加载后进行了初始化工作,但是字面量User.class并不会导致初始化过程。
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- 欢迎使用CSDN-markdown编辑器
- Banner的简单使用
- 递归方式实现打印一个整数的每一位
- Python List 列表
- Spring学习总结
- ubuntu无法打开terminal
- 欢迎使用CSDN-markdown编辑器
- OpenCV 2.4.9在Ubuntu下的配置与安装
- AngluarJS增删
- Java EE JSP编程基础
- LeetCode-029 Divide Two Integers
- Banner轮播
- webview的基本使用
- 树莓派Raspbian系统安装tesseract-ocr实现OCR
- 添加替换删除