重温Java的类加载机制
原文地址:http://blog.csdn.net/hitxueliang/article/details/19992851
首先简要的说一下类加载器
我们知道,虚拟机的指令存储在以.class为扩展名的类文件中。那么虚拟机如何调用这些.class文件呢。先看一下虚拟机执行的步骤:
1. 虚拟机有一个有一个用于类加载的机制,用于从数据源中读取类文件,这个源可以是磁盘文件或者web上的文件,这里假设要加载的雷文佳是TestLoader。
2. 如果TestLoader这个类拥有类型为其他类的实例或者是某个类的子类,也就是说它和其他类产生关联或者依赖,那么相关的类也会被加载
3. 虚拟机执行TestLoader中的main方法
4. 如果main方法中有对其他类的依赖,那么加载相应的类。
可见联系虚拟机和我们的程序的“中间人”就是类加载器,用什么就加载什么。类加载器是有其自身的体系的。每一个java程序至少拥有三个类加载器:
引导类加载器:
引导类加载器负责加载系统类,通常从rt.jar中加载,引导类加载器是隶属于虚拟机体系的一部分,而且实现通常是C语言实现的。引导类加载器没有对应的ClassLoader对象,例如:
System.out.println("----------测试引导类加载器-------------");
System.out.println("Class的加载器:"+Class.class.getClassLoader().getSystemClassLoader());
System.out.println("TestLoader的加载器:"+TestLoader.class.getClassLoader());
System.out.println("Stirng的类加载器:"+String.class.getClassLoader());
System.out.println("ArrayList的类加载器:"+ArrayList.class.getClassLoader());
System.out.println("----------测试引导类加载器-------------\n");
那么对应的结果如下:
----------测试引导类加载器-------------
Class的加载器:sun.misc.Launcher$AppClassLoader@1e87719
TestLoader的加载器:sun.misc.Launcher$AppClassLoader@1e87719
Stirng的类加载器:null
ArrayList的类加载器:null
----------测试引导类加载器-------------
显然我们知道ArrayList,String以及基本类型的封装类是属于rt.jar的,这与结果符合。
扩展类加载器:
扩展类加载器用于从jre/lib/ext目录加载扩展类。可以将你的jar放到这个目录下,这样就没有任何路径问题了,但有个问题要注意:如果将你的jar放到了jre/lib/ext下,如果你的类中调用了系统类或者其他扩展类,那么你就摊上事了,因为扩展类加载器并不使用类路径,这就导致,你是找不到的,所以在使用扩展路径来解决类文件冲突前,要考虑规避这个问题。
系统类加载器:
系统类加载器用于加载应用类,可以找到classpath里面指定的jar中的类。
扩展类加载器和系统类加载器都是采用java实现的,都是URLClassLoader的实例,关系图呢,可以看下面的图所示,在这里可以看到AppClassLoader就是我们上面打印出来的系统类加载器,而他正是URLClassLoader的子类
类加载器的层次结构
类加载器有一个等级结构,可以认为默认的是上层优先的原则。除了引导类加载器外其他的类加载器都有一个父类加载器。根据规定,每个类加载器都会先为其父类加载器提供工作机会,如果父类不能完成此项任务,他才会自己去做。例如你要系统类加载器加载一个String类型时,首先系统类加载器,会让他的父亲即扩展类加载器去尝试加载,那么扩展类加载器也会让他的父亲——引导类加载器去尝试加载,引导类加载器是顶层的了,他一看这个任务是我的菜,那么他就会完成此项任务。
类的加载器层次结构如下如所示
虽然大多数的时候你不必担心类加载的层次结构。通常类是由其他的类的需要而被加载的,而这个过程对于大多数人是透明的。但如果你想干涉这个过程,也是可以的。下面介绍如何自己去指定加载器。
这里就在一个main函数中说明。每一个线程都有一个对类的加载器的引用,称为上下文类加载器。
Thread t=Thread.currentThread();
System.out.println("当前主线程的类加载器是:"+t.getContextClassLoader());
t.setContextClassLoader(new MyLoader("Hello world"));
ClassLoader loader=t.getContextClassLoader();
System.out.println("设置加载器,采用自己的类加载器:"+t.getContextClassLoader());
这里先获取当前线程的类加载器,之后再指定为自己的类加载器。
这里说明一下,主线程的加载器是系统类加载器。当新建一个线程时,他的上下文加载器会被设定为创建者的上下文类加载器。因此如果你不去干预,那么依旧是系统类加载器。
如何实现自己的类加载器?
实现自己的类加载器实际很简单,只需要继承ClassLoader类即可,然后覆盖findClass方法即可。当调用我们的类加载器时,首先他会将这个工作交给父亲去做,父类中的LoadClass方法将去做,发现这个类没有加载过或者不能加载时,才去调用findClass方法。
FindClass方法要实现的任务是:
1. 加载字节码
2. 调用父类的definClass方法,向虚拟机提供字节码。
我的加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MyLoader extends ClassLoader{
private String key;
public MyLoader(String key){
this.key=key;
System.out.println(key+" ,自己的类加载器被实例化了");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classbytes=null;
classbytes=loadClassBytes(name);
Class<?> cl=defineClass(null, classbytes, 0, classbytes.length);
if(cl==null){
System.out.println("失败");
return null;
}
return cl;
}
//读取文件,返回字节数组
private byte[] loadClassBytes(String name){
FileInputStream in=null;
try {
in=new FileInputStream(name);
ByteArrayOutputStream buffer=new ByteArrayOutputStream();
int ch;
while((ch=in.read())!=-1){
byte b=(byte) ch;
buffer.write(b);
}
in.close();
return buffer.toByteArray();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
被加载的类
public class ToBeLoad {
public static void main(String[] args) {
System.out.println("被加载的类main方法执行了");
System.out.println(args[0]);
}
}
测试类:
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class TestLoader {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
System.out.println("----------测试引导类加载器-------------");
System.out.println("Class的加载器:"+Class.class.getClassLoader().getSystemClassLoader());
System.out.println("TestLoader的加载器:"+TestLoader.class.getClassLoader());
System.out.println("Stirng的类加载器:"+String.class.getClassLoader());
System.out.println("ArrayList的类加载器:"+ArrayList.class.getClassLoader());
System.out.println("----------测试引导类加载器-------------\n");
File f=new File("E://WorkSpace//JavaSE//MyTest//bin//ToBeLoad.class");
Thread t=Thread.currentThread();
System.out.println("当前主线程的类加载器是:"+t.getContextClassLoader());
t.setContextClassLoader(new MyLoader("Hello world"));
ClassLoader loader=t.getContextClassLoader();
System.out.println("设置加载器,采用自己的类加载器:"+t.getContextClassLoader());
Class<?> c=loader.loadClass(f.getAbsolutePath());
Method[] m=c.getMethods();
m[0].invoke(null, (Object)new String[]{"1"});
}
}
重温Java的类加载机制的更多相关文章
- Java虚拟机类加载机制——案例分析
转载: Java虚拟机类加载机制--案例分析 在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬 ...
- JAVA 初识类加载机制 第13节
JAVA 初识类加载机制 第13节 从这章开始,我们就进入虚拟机类加载机制的学习了.那么什么是类加载呢?当我们写完一个Java类的时候,并不是直接就可以运行的,它还要编译成.class文件,再由虚拟机 ...
- Java 的类加载机制
Java 的类加载机制 来源 https://www.cnblogs.com/xiaoxi/p/6959615.html 一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内 ...
- Java基础-类加载机制与自定义类Java类加载器(ClassLoader)
Java基础-类加载机制与自定义类Java类加载器(ClassLoader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于类加载器的概念和分类我就不再废话了,因为我在之前的笔 ...
- 深入理解Java虚拟机---类加载机制(简略版)
类加载机制 谈起类加载机制,在这里说个题外话,当初本人在学了两三个月的Java后,只了解了一些皮毛知识,就屁颠屁颠得去附近学校的招聘会去蹭蹭面试经验,和HR聊了一会后开始了技术面试,前抛出了两个简单的 ...
- 我竟然不再抗拒 Java 的类加载机制了
很长一段时间里,我对 Java 的类加载机制都非常的抗拒,因为我觉得太难理解了.但为了成为一名优秀的 Java 工程师,我决定硬着头皮研究一下. 01.字节码 在聊 Java 类加载机制之前,需要先了 ...
- 面试官,不要再问我“Java虚拟机类加载机制”了
关于Java虚拟机类加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断程 ...
- [转]Java虚拟机类加载机制
原文地址:http://blog.csdn.net/u013256816/article/details/50829596 看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎 ...
- java虚拟机类加载机制和双亲委派模型
java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸 ...
随机推荐
- CI框架源代码阅读笔记5 基准測试 BenchMark.php
上一篇博客(CI框架源代码阅读笔记4 引导文件CodeIgniter.php)中.我们已经看到:CI中核心流程的核心功能都是由不同的组件来完毕的.这些组件类似于一个一个单独的模块,不同的模块完毕不同的 ...
- Android 4.2 通知通过PendingIntent启动Activity失败的问题
今天突然发现在Android 4.2手机上点击通知消息无法打开Activity的问题,具体Logcat信息如下: 01-09 11:37:43.733: WARN/ActivityManager(92 ...
- shell 判断文件、目录是否存在
shell判断文件是否存在 1. shell判断文件,目录是否存在或者具有权限 2. #!/bin/sh 3. 4. myPath="/var/log/httpd/" 5. m ...
- [转] JSON for java入门总结
一.JSON介绍 JSON(JavaScript Object Notation),类似于XML,是一种数据交换格式,比如JAVA产生了一个数据想要给JavaScript,则除了利用XML外,还可以利 ...
- PV和并发
几个概念 网站流量是指网站的访问量,用来描述访问网站的用户数量以及用户所浏览的网页数量等指标,常用的统计指标包括网站的独立用户数量.总用户数量(含重复访问者).网页浏览数量.每个用户的页面浏览数量.用 ...
- Java多线程——多线程的基本概念和使用
一.进程和线程的基础知识 1.进程和线程的概念 进程:运行中的应用程序称为进程,拥有系统资源(cpu.内存) 线程:进程中的一段代码,一个进程中可以有多段代码.本身不拥有资源(共享所在进程的资源) 在 ...
- codevs 1269 匈牙利游戏
/*暴力+乱搞 55分(似乎只有暴力得分了)*/ #include<iostream> #include<cstdio> #include<cstring> #in ...
- MVC验证码的编写
主要是相互学习一下mvc,希望各位大神指导 /// <summary> /// 生成随机数字 /// </summary> /// <returns>随机数字< ...
- Android NDK编程,引入第三方.so库
android自带的编译工具NDK进行编译时(非单纯的调用第三方.so而是进行ndk编程),armeabi以及armeabi-v7a文件夹下的第三方so文件将会被删除,只会产生编译后的so文件,其他的 ...
- 通过安全策略限制局域网部分IP访问我的电脑
一旦电脑连上局域网,那么别人就容易进入自己的电脑,造成隐私被泄漏,这是我们最不愿发生的情况.因此,如果你的电脑并不需要向局域网其他用户共享资料,那么就建议采用策略,禁止局域网电脑访问自己的电脑,以保证 ...