java类加载器理解
类加载器
Java类加载器(英语:Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。学习类加载器时,掌握Java的委派概念很重要。
1.类的加载过程:
2.类的生命周期:
加载:"加载"是"类加载"过程的一个阶段,此阶段完成的功能是:
通过类的全限定名来获取定义此类的二进制字节流
将此二进制字节流所代表的静态存储结构转化成方法区的运行时数据结构
在内存中生成代表此类的java.lang.Class对象,作为该类访问入口.
验证:连接阶段第一步.验证的目的是确保Class文件的字节流中信息符合虚拟机的要求,不会危害虚拟机安全,使得虚拟机免受恶意 代码的攻击.大致完成以下四个校验动作:
文件格式验证
源数据验证
字节码验证
符号引用验证
准备:连接阶段第二步,正式为类变量分配内存并设置变量的初始值.(仅包含类变量,不包含实例变量).
解析:连接阶段第三步,虚拟机将常量池中的符号引用替换为直接引用,解析动作主要针对类或接口,字段,类方法,方法类型等等..
初始化:类的初始化是类加载过程的最后一步,在此,才真正意义上的开始执行类中定义的java程序代码.该阶段会执行类构造器.
使用:使用该类所提供的功能.
卸载:从内存中释放.
具体再看一下-类是如何加载的 :http://www.jiajiajia.club/weblog/blog/artical/78
3.类加载器的划分以及继承关系:
注意,BootStrap类加载器是由c语言实现。
4.双亲委派模型:
双亲委派模型是一种组织类加载器之间关系的一种规范,他的工作原理是:如果一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,这样层层递进,最终所有的加载请求都被传到最顶层的启动类加载器中,只有当父类加载器无法完成这个加载请求(它的搜索范围内没有找到所需的类)时,才会交给子类加载器去尝试加载.
这样的好处是:java类随着它的类加载器一起具备了带有优先级的层次关系.这是十分必要的,比如java.langObject,它存放在\jre\lib\rt.jar中,它是所有java类的父类,因此无论哪个类加载都要加载这个类,最终所有的加载请求都汇总到顶层的启动类加载器中,因此Object类会由启动类加载器来加载,所以加载的都是同一个类,如果不使用双亲委派模型,由各个类加载器自行去加载的话,系统中就会出现不止一个Object类,应用程序就会全乱了.
5.自定义类加载器完成类的加密与解密:
先自定义一个类
package classLoader;
import java.util.Date;
public class TestEntity extends Date{
private static final long serialVersionUID = 1L;
private String name="123";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "TestEntity [name=" + name + "]";
}
}
定义一个类加载器:
package classLoader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 自定义classLoader
* @author LENOVO
*
*/
public class MyClassLoader extends ClassLoader{
private String classDir;
/**
* 解密类
* @param ips
* @param ops
* @throws Exception
*/
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);///1->0 0->1
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName = classDir + "\\" + name;
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
byte[] bytes = bos.toByteArray();
System.out.println("解密类完成");
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public MyClassLoader() {
super();
// TODO Auto-generated constructor stub
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
测试:
package classLoader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
/**
* 测试
* @author LENOVO
*
*/
public class TestMain {
public static void main(String[] args) {
String path =System.getProperty("user.dir")+"\\bin\\classLoader";
try {
//首先加密一个类,这类类名改为TestEntityS
FileInputStream fis = new FileInputStream(path+"\\TestEntity.class");
String destPath = path + "\\" + "TestEntityS.class";
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
//加载等自定义类,并获取类对象
Class clazz = new MyClassLoader(path).loadClass("TestEntityS.class");
Date t = (Date)clazz.newInstance();
System.out.println(t);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 加密类
* @param ips
* @param ops
* @throws Exception
*/
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);///1->0 0->1
}
}
}
结果:
如果我们把加密后的类的类的名字改为 TestEntity,然后用AppClassLoader去加载的话,一定会报错的,因为类已经被加密了。