java基础类加载器_Java基础之类加载器
1. 什么是类加载器? 加载类的工具.
2. Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:
BootStrap,ExtClassLoader,AppClassLoader
3. 类加载器也是Java类,因为其是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap,它是嵌套在虚拟机内核里面的,虚拟机内核一启动它就存在,它是用cpp语言写的一段二进制代码。
4. Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载
类加载器之间的父子关系和管辖范围图
5.所有的类加载器要组织成一个树状结构
6. 类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出哪儿类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类(Thread类有getContextClassLoader()方法);
如果类A中引用了或者继承了类B,Java虚拟机将使用加载类A的类装载器来加载类B;
还可以直接调用ClassLoader(自己写的类加载器).loadClass()方法来指定某个类加载器去加载某个类;
7. 每个类加载器加载类时,又先委托给其他上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不到,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法。
8. 对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar后运行结果为:sun.misc.Launcher$ExtClassLoader的原因
8. 委托机制有什么好处?集中管理,如果我们写了几个类加载器,都去加载某个类,那么内存中就有多份这个类的字节码
9. 能不能自己写一个类叫java.lang.System? 为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用java系统提供的System类。除非自己写个类加载器。
10. 实现自己的类加载器要继承ClassLoader这个抽象类,但不能覆盖其loadClass()方法,这里面有找父类加载器的流程,爸爸返回之后再接着调用findClass(),只需要覆盖findClass()方法即可,不覆盖loadClass()是想保留委托机制,而其中的defineClass()方法是将从磁盘找到的二进制数据转换为字节码(也就是Class对象)的方法,这些概念都可以在jdk文档中找到,并且可以找到详尽的示例程序。
11. 模板方法设计模式
父类-->loadClass/findClass/得到class文件的二进制数据转换成字节码-->defineClass
子类1中的LoadClass在完成公共事件后再调用(findClass)来做自己的事情
子类2中的LoadClass在完成公共事件后再调用(findClass)来做自己的事情
自己的事件代码不一样,总体的流程在父类里面已经规定好了
只需要覆盖findClass()和defineClass()即可,相当简单,你以为多复杂,你以为啊!
public class ClassLoaderTest {
public static void main(String[] args) {
// sun.misc.Launcher$AppClassLoader
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
// null
System.out.println(System.class.getClassLoader());
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while (loader != null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
// 理解有误,将ClassLoaderTest导出的itcast.jar放到ext目录时,怎么修改ClassLoaderTest.java的代码都
// 已经
// 没用作用了,因为加载的始终是ext目录下的ClassLoaderTest的main方法
// eclise->show view > problems
}
}
12. 编写自己的类加载器
public class MyClassLoader extends ClassLoader {
public static void main(String[] args) throws Exception {
String srcPath = args[0];
String destDir = args[1];
String destFileName = srcPath.substring(srcPath.lastIndexOf(File.separator) + 1);
String destPath = destDir + File.separator + destFileName;
System.out.println("srcPath:" + srcPath);
System.out.println("destFileName:" + destFileName);
System.out.println("destPath:" + destPath);
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;
while ((b = ips.read()) != -1) {
// 这个地方加密时对取出的每一个字节进行与0xff异或操作,即将0变成1,1变成0的过程。
ops.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + File.separator + name + ".class";
System.out.println(classFileName);
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis, bos);
fis.close();
byte[] bytes = bos.toByteArray();
return defineClass(null, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader() {
}
public MyClassLoader(String classDir) {
this.classDir = classDir;
}
}
@SuppressWarnings("serial")
public class ClassLoaderAttachment extends Date {
@Override
public String toString() {
return "hello, itcast!";
}
}
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
// sun.misc.Launcher$AppClassLoader
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
// null
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(new ClassLoaderAttachment().toString());*
// 实验成功的关键是原来无包名classpath下的ClassLoaderAttatchment.class清除,否则它会被
// AppClassLoader先加载,错误,因父类不能加载无包名的类,如果loadClass不加包名,则父类加载不到
// ,因为没有包路径名,要想让父类先加载并且出错,则加上包名,如果此时classes下的.class文件被删除,则父类
// 加载不到才轮到自定义加载器去加载,两个地方修改下:
// ClassLoaderAttachment -> cn.itcast.day2.ClassLoaderAttachment
// name -> name.substring(name.lastIndexOf('.')+1)
// 实验过程如下:
// 1.在工程的根目录下创建itcast目录
// 2.运行MyClassLoader,运行时传入ClassLoaderAttachment.class的全路径名和itcast
// 3.运行*查看正常结果,把加密过后的类覆盖掉classpath下好的ClassLoaderAttachment.class
// 4.此时*已经不能运行了,用MyClassLoader解密后加载即可
Class clazz = new MyClassLoader("itcast").loadClass("ClassLoaderAttachment");
// 此处不能在使用ClassLoaderAttachment因为一旦用了之后,
// 系统的类加载器就会去加载,导致失败,所以该类就继承了Date类了.
Date d = (Date) clazz.newInstance();
System.out.println(d);
}
}
一个类加载器的高级问题分析
·编写一个能打印出自己的类加载器名称和当前加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassLoader。
·把MyServlet.calss文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
·父级类加载器加载的类无法引用只能被子级类加载器加载的类,原理如下图:
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader = this.getClass().getClassLoader();
while (loader != null) {
out.write(loader.getClass().getName() + "
");
loader = loader.getParent();
}
out.write(String.valueOf(loader));
out.flush();
out.close();
}
}
要配置web.xml里的servelet和servlet-mapping标签
解决方法servlet-api.jar也放到/ext/目录下,都 由ExtClassLoader加载得了,此问题之所以出现,是因为ExtClassLoader在加载 MyServlet时,因为MyServlet继承了HttpServlet,按照类加载器的原则,HttpServlet还将由ExtClassLoader加载,但ExtClassLoader的加载目录中并没有HttpServetl类。
java基础类加载器_Java基础之类加载器相关推荐
- java 扩展类加载器_java实现自定义类加载器
各类加载器虽然以父子相称,但是没有继承关系 (视频教程推荐:java课程) 点入ClassLoader的源码查看样例:* * class NetworkClassLoader extends Clas ...
- java new 新对象_java基础(五)-----new一个对象的具体过程
在创建对象之前,首先要判断类有没有被加载,例如创建对象或调用类的static方法变量时,会触发类加载,如下: Dog dog = new Dog(); 首次访问某个类的静态方法或者静态字段时: Dog ...
- java io流 教程_Java基础教程:IO流与文件基础
Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:指的是从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列 ...
- java边界布局东南西北_JAVA swing布局管理器实例解析
组件在容器(比如Jframe)中的位置和大小是由布局管理器来决定的.所有的容器都会使用一个布局管理器,通过它来自动进行组件的布局管理. 种类 java共提供了五种布局管理器,只介绍三种是最常见的布局管 ...
- was修改类加载模式_java基础——单例(Singleton)模式介绍
基本概括 详解 一.单例模式定义: 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 二.单例模式特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3 ...
- java rmi 安全管理器_Java:没有安全管理器:RMI类加载器被禁用
嗨,我有RMI应用程序,现在我尝试从我的客户端在服务器上调用一些方法.我有以下代码: public static void main(final String[] args) { try { //Se ...
- java时间日期格式器_JAVA基础类库(二)-----日期、时间类和格式器
Date类 public classDateTest{public static voidmain(String[] args){ Date d1= newDate();//获取当前时间之后100ms ...
- JAva入门 活着_java基础回顾
最近在回顾java基础知识,收货颇多,在此做个总结 1.List和Set的区别 ---List -----可以允许重复的对象 -----可以插入null元素 -----是一个有序容器,保持着每个元素的 ...
- java package报错_Java基础知识总结 - 超详细篇(上)
1,JDK:Java Development Kit,java的开发和运行环境,java的开发工具和jre. 2,JRE:Java Runtime Environment,java程序的运行环境,ja ...
最新文章
- CVPR 2021 | 自适应激活函数ACON: 统一ReLU和Swish的新范式
- 梯度下降(BGD)、随机梯度下降(SGD)、Mini-batch Gradient Descent、带Mini-batch的SGD
- 47.2. 配置 Tomcat 服务器
- android 多点触控缩放,Android多点触控(图片的缩放Demo)
- Java——多线程学习
- VMware 怎么进入BIOS
- 平衡二叉树(AVL树)-详解平衡调整
- Qt4工作笔记-Linux中Qt4.8.6在GBK和UTF-8编码转换
- Unity Shader:Waveform波形(2)-基本波形:正弦,三角,锯齿,直角以及其变种的实现方式
- linux脚本加标题,bash-从shell脚本设置屏幕标题
- LA 5842 Equipment (状态压缩+dp)
- Leetcode 236.二叉树的最近公共祖先
- css3的一些知识点
- 回忆NWT开工,还要吾亲自布网线
- 清理注册表 php,cad注册表怎么删干净
- Ricequant 平台入门--回测第一个量化交易策略
- python——爬虫豆瓣250
- php 输入经纬度查询位置,php 根据实际地址获取对应的经纬度
- 麻雀虽小五脏俱全--一个小项目的总结
- 51单片机 8x8LED点阵屏循环显示数字0~9
热门文章
- NFC 音乐墙 (不限手机)[web 接口服务实现-折腾记录]
- 【shell】shell脚本的文本替换工具-tr
- 程序员各种学习网站,持续更新
- php连接远程mysql_MySQL如何开启远程连接?
- Qt QLineEdit 信号editingFinished()
- 百度云说 | ABC三位一体,全面赋能
- IntelliJ IDEA插件安装最全详解
- Docker,Kubernetes(K8S)是时候系统的学习下了!
- 1.定义一个Father和Child类,并进行测试。 要求如下: 1)Father类为外部类,类中定义一个私有的String类型的属性name,name的值为“zhangjun”。 2)Child
- C语言中time函数的定义及用法示例