java类加载

java类加载机制

一. java类加载过程

​ java类加载一共经历7个过程

1. 加载

​ 加载是类加载的第一过程,这个阶段完成三件事

  1. 通过类的全路径获取该类的二进制流。
  2. 将该二进制流中的静态存储结构转化为方法运行时数据结构。
  3. 在内存中生成该类的Class对象,作为该类的数据访问入口。
2. 验证

​ 验证确保Class文件的字节流中的信息正确,以及不会危害到虚拟机;主要有四种验证。

  1. 文件格式验证:验证字节流是否符合Class文件的规范,如:常量池中的常亮是否有不被支持的类型等;
  2. 元数据验证:对字节码信息进行语义分析,如:该类是否有父类,是否继承了不被继承的类等;
  3. 字节码验证:是整个验证过程中最复杂的一个阶段,确定语义是否正确,主要针对方法提的验证,如:方法中的类型转换是否正确,跳转指令是否真确等;
  4. 符号引用验证:在解析过程中发生,主要是为了确保解析动作能正确执行;
3. 准备

​ 准备阶段是为类的静态变量分配内存并将其初始化为默认值,内存在方法区(永久代)中分配,准备阶段不分配勒种实例变量的内存,实例变量会随对象分配在就java堆中;

//准备阶段value初始值为0,在初始化阶段才会变成123
public static int value = 123;
4. 解析

​ 该阶段主要完成符号的间接引用转化为直接引用,解析动作不一定在初始化之前,也有可能在初始化之后

5. 初始化

​ 初始化时类加载的最后一步,类加载过程中,加载阶段用户可以通过自定义类加载器参与,其余动作完全由虚拟机主导控制的。初始化阶段,才真正执行类中定义的java程序代码。

6. 使用

​ 程序员正常使用类,做一些操作

7. 卸载

由java虚拟机自带的类加载器所加载的类,在虚拟机生命周期中是不会被卸载的

由用户自己定义的类加载器加载的类是可以别卸载的

二. JVM加载Class文件的原理机制

​ Java 语言是一种具有动态性的解释型语言,类(Class)只有被加载到 JVM 后才能运行。当运行指定程序时,JVM 会将编译生成的 .class 文件按照需求和一定的规则加载到内存中,并组织成为一个完整的 Java 应用程序。 这个加载过程是由类加载器完成,具体来说,就是由 ClassLoader 和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。类的加载方式分为隐式加载和显示加载。

  1. 隐式加载指的是程序在使用 new 等方式创建对象时,会隐式地调用类的加载器把对应的类加载到 JVM 中。
  2. 显示加载指的是通过直接调用 class.forName() 方法来把所需的类加载到 JVM 中。

任何一个工程项目都是由许多类组成的,当程序启动时,只把需要的类加载到 JVM 中,其他类只有被使用到的时候才会被加载,采用这种方法一方面可以加快加载速度,另一方面可以节约程序运行时对内存的开销。此外,在 Java 语言中,每个类或接口都对应一个 .class 文件,这些文件可以被看成是一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。在 Java 语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到 JVM 中,至于其他类,则在需要的时候才加载。

类加载的主要步骤:

  1. 装载。根据查找路径找到相应的 class 文件,然后导入
  2. 链接。链接又可分为 3 个小步:
  3. 检查,检查待加载的 class 文件的正确性
  4. 准备,给类中的静态变量分配存储空间
  5. 解析,将符号引用转换为直接引用(这一步可选)
  6. 初始化。对静态变量和静态代码块执行初始化工
三. 什么是类加载器,类加载器又有那些

​ 通过类的全路径获取该类的二进制流的代码块叫做类加载器;

​ 类加载器主要分为三种:

  1. 启动类加载器(Bootstrap ClassLoader):用来加载java核心类库,无法被java程序直接引用。
  2. 扩展类加载器(Extensions ClassLoader):它用来加载java的扩展库;java虚拟机的实现会提供一个扩展目录,该类加载器再此目录里面查找并加载java类
  3. 系统类加载器(System ClassLoader):他根据java应用的类路径来加载java类,java应用的类都是由它来完成加载的,可以通过ClassLoader.getSystemClassLoader()来获取它
public static void main(String[] args) {
			System.out.println(ClassLoader.getSystemClassLoader());
	}

sun.misc.Launcher$AppClassLoader@73d16e93

除了这些,用户还可以自己定义类加载器,通过集成java.lang.ClassLoader类的方式实现

  1. 继承ClassLoader
  2. 重写findClass()方法
  3. 调用defineClass()方法
package com.fzz.java.demo2;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class ClassLoaderDemo extends ClassLoader {
	
	private String classPath;
	
	public ClassLoaderDemo() {
	}
	public ClassLoaderDemo(String classPath){
		this.classPath = classPath;
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] classByte = new byte[0];
		try {
			classByte = getByte(name);
		} catch (IOException e) {

			e.printStackTrace();
		}
		 // defineClass方法将字节码转化为类
		return super.defineClass(name,classByte,0,classByte.length);
	}
	
	//	返回类的字节码
    private byte[] getByte(String className) throws IOException{
        InputStream in = null;
        ByteArrayOutputStream out = null;
        String path=classPath + File.separatorChar +
                    className.replace('.',File.separatorChar)+".class";
        try {
            in=new FileInputStream(path);
            out=new ByteArrayOutputStream();
            byte[] buffer=new byte[2048];
            int len=0;
            while((len=in.read(buffer))!=-1){
                out.write(buffer,0,len);
            }
            return out.toByteArray();
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally{
            in.close();
            out.close();
        }
        return null;
    }

}
package com.fzz.java.demo2;

public class Demo2 {
	public void show(String str) {
		System.out.println(str + " Hello MyClassLoader ");
	}
}

package com.fzz.java.demo2;

import java.lang.reflect.Method;

public class Demo {
	public static void main(String[] args) throws Exception {
		//自定义类加载器的加载路径
        ClassLoaderDemo classLoaderDemo=new ClassLoaderDemo("D:\\eclipse_workspace_two\\JavaDemo");
        //包名+类名
        Class c=classLoaderDemo.loadClass("com.fzz.java.demo2.Demo2");
        
        if(c!=null){
            Object obj=c.newInstance();
            Method method=c.getMethod("show", new Class[] {String.class});
            method.invoke(obj, new String[] {"fzz"});
            System.out.println(c.getClassLoader().toString());
        }
	}
}

结果:

fzz Hello MyClassLoader 
sun.misc.Launcher$AppClassLoader@73d16e93
四. 类加载机制双亲委派

​ 当一个类请求加载时,不会先去自己加载这个类,而是将其委派给父类,有父类去加载,如果父类不能加载,再反馈给子类,由子类去完成类的加载。

避免了一些类的重复加载,有效防止核心类被恶意篡改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值