学习一下Java中的类加载器,这个是比较底层的东西,好好学习、理解一下。

 一、类加载器的介绍

1、类加载器:就是加载类的工具,在java程序中用到一个类,java虚拟机首先要把这个类的字节码加载到内存中来,但是通常这个类的原始信息是放在硬盘上的classpath指定的路径下,将.class文件的内容加载到硬盘上来,再对它进行一些处理,处理完的结果就是字节码,这些工作就是类加载器在做

扩展一下一个java程序的生命周期:(这个总是记不住,无语了。。。)

以Java为例:

电脑是不能直接执行Java程序的,一个.java程序要想被执行,首先需要编译器将高级的.java程序文件编译成.class字节码片段,字节码经过JVM(解释器)的处理后生成电脑可以直接执

行的机器码,至此java程序才能得以正确运行。

2、Java虚拟机中可以安装多个类加载器,系统中默认的有三个类加载器:Bootstrap、ExtClassLoader、AppClassLoader,每个类加载器加载特定位置的类

3、类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有一个类加载器不是java类,这个加载器就是Bootstrap类加载器。

注意:Bootstrap类加载不是java类,它是嵌套在java虚拟机内核中的,java虚拟机内核一启动,它就已经在那里了,它是用C++语言写的一段二进制代码,它可以去加载别的类以及其他的类加载器。

         // sun.misc.Launcher$AppClassLoader
String classLoaderName1 = ClassLoaderTest.class.getClassLoader().getClass().getName();
System.out.println(classLoaderName1); // 为 null 说明System类是由Bootstrap类加载器加载的
System.out.println(System.class.getClassLoader()); // 循环理解 类加载器中的父子关系
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);

4、java虚拟机中的所有类加载器采用具有父子关系的属性结构进行组织,在实例化每个类加载器对象时候,需要为其指定一个父级类加载器对象,或者默认采用系统类加载器为其父级类加载器

5、类加载器的委托机制

当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

1)首先,当前线程的类加载器去加载线程中的第一个类

2)如果类A中引用了类B,java虚拟机将使用类A的类加载器来加载类B

3)还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器来加载某个类

每个类加载器加载类时,又先委托给其上级类加载器

1)当所有祖宗类加载器没有加载到类时,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是去找发起者类加载器的儿子,因为没有getChild()方法,就算是有,那有多个儿子,应该交给哪一个呢?

 二、编写自己的类加载器

 1、自定义类加载器的原理

从jdk文档中找到的一个案例:(主要就是重写findClass()方法,加载二进制class文件,转换成字节码)

 //网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。示例实现如下: 

      class NetworkClassLoader extends ClassLoader {
String host;
int port; public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
} private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}

2、自己定义classLoader

这个写起来真的费劲啊,听着张老师的视频讲解都感觉很费劲,这个要是自己能写出来,那真的要对class的加载机制很熟悉,看着张老师解决问题的能力,感觉自己真的还有很多东西需要学习,自己的解决问题的能力几乎是0,但是这背后肯定离不开强大的知识体系的支撑,自己的基础知识不够扎实,如何去谈解决问题,可能你都发现不了问题。

代码拿出来,不过还有很多难点,希望以后自己能够完全理解(这个重写ClassLoader应该不难,但是如何让你写的ClassLoader来使用起来,我觉得这个是比较难的,你要知道在java中的类加载机制,树状的类加载关系,只有当父类加载不到类的时候,才会轮到你自己重写的ClassLoader来加载所需要加载的类,问题是如何让父类加载不到,而让你自己的ClassLoader加载到呢?这个是问题的所在,也是解决问题的切入点

 public class MyClassLoader extends ClassLoader{

     private String classDir;

     public static void main(String[] args) throws Exception{
String srcPath = args[0];
String destDir = args[1]; String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destPath = destDir + "\\" + destFileName; FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis, fos);
fis.close();
fos.close(); } private static void cypher(InputStream ips, OutputStream ops) throws Exception{
int b = -1; // 读字节流读到最后为 -1
while((b = ips.read()) != -1 ){
// 进行异或运算 就是把二进制中的0编程1 1变成0 进行简单的加密
ops.write(b ^ 0xff);
}
} @Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 截取包名中最后的类的名称
String classFileName = classDir + "\\" + name.substring(name.lastIndexOf(".")+1) + ".class";
System.out.println("MyClassLoader中加载的类的名称:" + classFileName);
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis, bos);
fis.close(); byte[] bytes = bos.toByteArray();
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
} return super.findClass(name);
} public MyClassLoader(){ } public MyClassLoader(String classDir){
this.classDir = classDir;
}
}

写一下测试代码:

         //父类加载的是全路径加包名的类
Class clazz = new MyClassLoader("itcastlib").loadClass("com.ssc.classLoader.ClassLoaderAttachment");
// 子类继承父类的加载的是相对路径的类 只有当父类没有找到类的时候 子类才进行加载
//Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
Date d1 = (Date) clazz.newInstance();
System.out.println(d1);

这里的让重写的ClassLoader加载指定的类的解决办法是:我把编译好的指定的类的class文件放在 itcastlib文件夹中,这个其实是上面加密后的class文件,也就是MyClassLoader类中main函数执行之后生成在  itcastlib 文件夹中的,但是父类会去E:\20181124\workspace\Test\bin\com\ssc\classLoader\ClassLoaderAttachment.class 路径下找那个类的class文件,而当我用重写的ClassLoader加载时,会从itcastlib 文件夹下去加载该类的class文件,并且我自己的ClassLoader会执行解密方法,对class文件的字节码进行解密。否则父类加载加密的class文件的时候,会报错的,因为父类中的findClass()方法并没有我的解密方法。

下图中的样子:

父类加载报错:

总结:

这样写出来,感觉自己稍微理清楚一点了。嘻嘻嘻

其实,在好多的框架中都会自己设计所需要的类加载器,但是我之前好像并不知道,看了一下Tomcat中的servlet的加载问题,看到了Tomcat中使用了WebappClassLoader、StandardClassLoader来加载servlet以及Tomcat内部实现的HttpServlet,这个以后可以往深里仔细去看一下

Java中的类加载器--Class loader的更多相关文章

  1. Java中的类加载器

    转载:http://blog.csdn.net/zhangjg_blog/article/details/16102131 从java的动态性到类加载机制   我们知道,Java是一种动态语言.那么怎 ...

  2. Java中的类加载器以及Tomcat的类加载机制

    在加载阶段,虚拟机需要完成以下三件事情: 1.通过一个类的全限定名来获取其定义的二进制字节流. 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构. 3.在Java堆中生成一个代表这个类 ...

  3. [读书笔记]java中的类加载器

    以下内容大多来自周志明的<深入理解Java虚拟机>. 类加载器是java的一项创新,也是java流行的重要原因之一,它最初是为了满足java applet的需求而开发出来. 什么是appl ...

  4. java中的类加载器ClassLoader和类初始化

    每个类编译后产生一个Class对象,存储在.class文件中,JVM使用类加载器(Class Loader)来加载类的字节码文件(.class),类加载器实质上是一条类加载器链,一般的,我们只会用到一 ...

  5. 关于java中的类加载器

    什么是类加载器? 类加载器是专门负责加载类的命令或者说工具 ClassLoader java中的3个类加载器 JDK中自带了3个类加载器 启动类加载器 扩展类加载器 应用类加载器 假设有这样一段代码 ...

  6. Java中的类加载器----ClassLoader

    1.简单的讲类加载器就是加载类. 在一个类要被执行时,首先会被从硬盘中加载到内存中,这个任务就是由类加载器来完成,如果加载不成功时,类是无法被执行的.类加载器执行的都是字节码二进制文件.   帮助文档 ...

  7. JAVA基础_类加载器

    什么是类加载器 类加载器是Java语言在1.0版本就引入的.最初是为了满足JavaApplet需要.现在类加载器在Web容器和OSGI中得到了广泛的应用,一般来说,Java应用的开发人员不需要直接同类 ...

  8. 黑马程序员——【Java高新技术】——类加载器

    ---------- android培训.java培训.期待与您交流! ---------- 一.概述 (一)类加载器(class loader) 用来动态加载Java类的工具,它本身也是Java类. ...

  9. 黑马程序员:Java基础总结----类加载器

    黑马程序员:Java基础总结 类加载器   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 类加载器 Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个 ...

随机推荐

  1. CodeForces 718A Efim and Strange Grade (贪心)

    题意:给定一个浮点数,让你在时间 t 内,变成一个最大的数,操作只有把某个小数位进行四舍五入,每秒可进行一次. 析:贪心策略就是从小数点开始找第一个大于等于5的,然后进行四舍五入,完成后再看看是不是还 ...

  2. XCode10 swift4.2 适配遇到的坑

    以下是2018年10月23日更新 经过大约一个月的时间的适配,项目正式使用XCode10(以下简称为10 or XC10)大部分库都升级为Swift4.2(以下简称为 4.2 or S4.2),下面是 ...

  3. ARM汇编中ldr伪指令和ldr指令(转载)

    转自:http://blog.csdn.net/ce123_zhouwei/article/details/7182756 ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成, ...

  4. 如何正确访问Redis中的海量数据?服务才不会挂掉!

    一.前言 有时候我们需要知道线上的Redis的使用情况,尤其需要知道一些前缀的key值,让我们怎么去查看呢?并且通常情况下Redis里的数据都是海量的,那么我们访问Redis中的海量数据?如何避免事故 ...

  5. 【EXCEL终极总结分享】基于NPOI扩展封装的简易操作工具类库(简单灵活易用,支持导出、导入、上传等常见操作)

    对于EXCEL的导入.导出,我之前已分享过多次,比如: 第一种方案:<我写的一个ExcelHelper通用类,可用于读取或生成数据>这个主要是利用把EXCEL当成一个DB来进行获取数据,导 ...

  6. Luogu P1262 间谍网络 【强连通分量/缩点】By cellur925

    题目传送门 真是一道好题呀~~~~qwq 知道这题是tarjan,但是想了很久怎么用上强连通分量.因为样例们...它显然并不是一个强联通分量! (被样例迷惑的最好例子) 然后...就没有然后了...感 ...

  7. hdu2489 Minimal Ratio Tree dfs枚举组合情况+最小生成树

    #include <stdio.h> #include <set> #include <string.h> #include <algorithm> u ...

  8. C++11 多线程相关的头文件

    C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable& ...

  9. Python入门小练习 002 批量下载网页链接中的图片

    我们常常需要下载网页上很多喜欢的图片,但是面对几十甚至上百张的图片,一个一个去另存为肯定是个很差的体验. 我们可以用urllib包获取html的源码,再以正则表达式把匹配的图片链接放入一个list中, ...

  10. [LuoGu]P2664 树上游戏

    Portal 这题真的好. 看到树上路径, 脑子里就要点分治 这一题对于每个点都要计算一遍, 如果暴算实在不好算, 这样我们就可以考虑算贡献. 直接计算每种颜色的贡献. 因为一条过重心的路径中, 可能 ...