java类加载器理解

硅谷探秘者 5071 0 0

类加载器

Java类加载器(英语:Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。学习类加载器时,掌握Java的委派概念很重要。

1.类的加载过程:

QQ截图20190101225723.png

2.类的生命周期:

classLoder.png

加载:"加载"是"类加载"过程的一个阶段,此阶段完成的功能是:

  通过类的全限定名来获取定义此类的二进制字节流

  将此二进制字节流所代表的静态存储结构转化成方法区的运行时数据结构

  在内存中生成代表此类的java.lang.Class对象,作为该类访问入口.

验证:连接阶段第一步.验证的目的是确保Class文件的字节流中信息符合虚拟机的要求,不会危害虚拟机安全,使得虚拟机免受恶意         代码的攻击.大致完成以下四个校验动作:

  文件格式验证

  源数据验证

  字节码验证

  符号引用验证

准备:连接阶段第二步,正式为类变量分配内存并设置变量的初始值.(仅包含类变量,不包含实例变量).  

解析:连接阶段第三步,虚拟机将常量池中的符号引用替换为直接引用,解析动作主要针对类或接口,字段,类方法,方法类型等等..

初始化:类的初始化是类加载过程的最后一步,在此,才真正意义上的开始执行类中定义的java程序代码.该阶段会执行类构造器.

使用:使用该类所提供的功能.

卸载:从内存中释放.


具体再看一下-类是如何加载的 :http://www.jiajiajia.club/weblog/blog/artical/78

3.类加载器的划分以及继承关系:

QQ截图20190101225956.png

注意,BootStrap类加载器是由c语言实现。

QQ截图20190102000514.png

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
		}
	}
}

结果:

20190102001013.png

如果我们把加密后的类的类的名字改为 TestEntity,然后用AppClassLoader去加载的话,一定会报错的,因为类已经被加密了。


评论区
请写下您的评论...
暂无评论...
猜你喜欢
java虚拟机(jvm) 4942 虚拟机设计团队把阶段中的“通过一个的全限定名来获取描述此的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的。实现这个动作的代码
java虚拟机(jvm) 4029 首先要知道的过程包括了、验证、准备、析、初始化五个阶段。java虚拟机class的过程虚拟机把Class文件到内存,然后进行校验,析和初始化,最终形成java型,这就是虚拟
java基础 4886 java中byte型相超出范围变负数析例: @Test publicvoidt(){ byteb=127; b+=127; System.out.println(b); }最终运行得出的
java基础 1437 java并发编程-CAS算法1.什么是cas算法?CAS:CompareandSwap,即比较再交换。jdk5增了并发包java.util.concurrent.*,其下面的使用CAS算法实
java基础 3338 在用各种框架的时候,注是几乎都会遇到的,那么他的原是怎么样的呢!来探究一下1.我们用过很多注比如下面的jdk自带的注@Override表示覆盖或重写父的方法;@Deprecated表示该方
official 1049 编码在网络应用中需要实现某种编,将原始字节数据与自定义的消息对象进行互相转换。网络中都是以字节码的数据形式来传输数据的,服务编码数据后发送到客户端,客户端需要对数据进行
框架 6268 javaspringboot动态添或移除定时(定时任务)工具importjava.lang.reflect.Field;importjava.lang.reflect.Method
spring/springmvc 1284 (A.class):此注的bean会在A之后 @ConditionalOnMissingBean(A.class):当ioc容中含有A型的对象时,那么ioc则会忽略@Component注
归档
2018-11  12 2018-12  33 2019-01  28 2019-02  28 2019-03  32 2019-04  27 2019-05  33 2019-06  6 2019-07  12 2019-08  12 2019-09  21 2019-10  8 2019-11  15 2019-12  25 2020-01  9 2020-02  5 2020-03  16 2020-04  4 2020-06  1 2020-07  7 2020-08  13 2020-09  9 2020-10  5 2020-12  3 2021-01  1 2021-02  5 2021-03  7 2021-04  4 2021-05  4 2021-06  1 2021-07  7 2021-08  2 2021-09  8 2021-10  9 2021-11  16 2021-12  14 2022-01  7 2022-05  1 2022-08  3 2022-09  2 2022-10  2 2022-12  5 2023-01  3 2023-02  1 2023-03  4 2023-04  2 2023-06  3 2023-07  4 2023-08  1 2023-10  1 2024-02  1 2024-03  1
标签
算法基础 linux 前端 c++ 数据结构 框架 数据库 计算机基础 储备知识 java基础 ASM 其他 深入理解java虚拟机 nginx git 消息中间件 搜索 maven redis docker dubbo vue 导入导出 软件使用 idea插件 协议 无聊的知识 jenkins springboot mqtt协议 keepalived minio mysql ensp 网络基础 xxl-job rabbitmq haproxy srs 音视频 webrtc javascript
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。