java铲车_JAVA类型的装载机制解析

我们知道在静态块中在类加载过程中就执行了,那我们今天就讲下类型(类和接口)的装载过程

在此之前,我们先来了解下JVMJVM,是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM是java的核心和基础,在java编译器和os平台之间的虚拟处理器。可以在上面执行java的字节码程序。其原理是:java编译器将源文件编译成JVM可理解的代码或者字节码文件,JVM在执行时把字节码解释成具体平台上的机器指令执行 。

正因为如此,Java有一个非常重要的特点就是与平台的无关性,即能够“一次编译,到处运行”

好,现在我们来看下类装载机制的概念虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

一个类的生命周期(从它进入JVM开始,到最终退出)包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking),为了我们本次的学习,我们先看一下加载、连接和初始化的简要说明 。

加载就是把二进制形式的JAVA类型读入JVM中;

连接就是把这种已读入JVM的二进制形式的类型数据合并到JVM的运行时状态中去。其中,“验证”是验证JAVA类型数据格式是否正确且适合JVM使用。“准备”是负责为这种类型分配所需内存,比如为它的类变量分配内存。“解析”则是把常量池中的符号引用转换为直接引用,JVM的实现可以推迟解析这一步,因为它可以在运行程序过程中真正使用某个符号时再去解析(所以,加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定)

初始化容易理解,就是为类变量赋予合适的初始值。而且,任何一个类初始化前都要求它的超类已经初始化了,以此类推,一个类初始化前,其所有祖先类都初始化了。

装载阶段的三个基本动作:通过 该类型的完全限定名,产生一个代表该类型的二进制数据流

解析这个二进制数据流为方法区内的内部数据结构

创建一个表示该类型的java.lang.Class类的实例

这个二进制数据流可能遵守java lang 文件格式,也有可能是遵守其他格式,其获取也不止从Class文件,比如它还可以从Jar包中获取、从网络中获取(最典型的应用便是Applet)、由其他文件生成(JSP应用)等。

JAVA类型要么由启动类装载器装载,或者由用户自定义装载器来装载,这里我们了解下类装载器

类的装载器ClassLoader:

我们知道,Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中

ClassLoader是一个抽象类,ClassLoader的实例将读入java字节码将类装载入jvm,可以定制符合不同字节码流获取方式(可以从网络或文件加载),在装载负责加载阶段。

ClassLoader比较重要方法:loadClass()(根据类名去装载,返回类的信息),defineClass()(定义一个类,在形参传入类的二进制字节码),findClass()(loadClass回调方法,自定义装载器做法),findLoad()(查看是否是已装载类)

ClassLoader拥有四种:BootStrap ClassLoader(加载系统类 rt.jar),它负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

Extension ClassLoader(加载扩展类 lib/ext/*.jar)该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

App ClassLoader(加载应用类)该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

Custom ClassLoader(自定义加载器)

除了第一个加载都有个Parent作为父亲,按照上面的排序进行 自底向上检查类是否加载(寻找需要加载的类是否在对应装载器的目录下,找不到会报ClassNotFoundException),自顶向下加载类(自上而下加载对应装载器路径下的类),在这里可以去深入了解一下双亲委派模型。

类的装载方式

1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到JVM中

2.显式装载, 通过class.forname()等方法,显式加载需要的类

new是硬编码,然而在大部分情况下,new这种硬编码就够了,大家用得很多。

Class.forName(String)可以动态加载,这增强了java的动态性。也就是说你可以在运行时选择加载A类或者B类

if (condition_a) {

Class cls = Class.forName("A");

else if (condition_b) {

Class cls = Class.forName("B");

} else {

Class cls = Class.forName("Other");

}

测试

相信学习完后一定多少能够理清了装载机制,现在来一起看下下面这段程序的运行结果是什么吧。

package com.zzq.test;

public class SSClass

{

static

{

System.out.println("SSClass");

}

public static int a = 20;

public static void main(String[] args)

{

System.out.println(SubClass.value);

// new SubClass();// System.out.println(SuperClass.value);//System.out.println(SubClass.b); }

}

class SuperClass extends SSClass

{

static

{

System.out.println("SuperClass init!");

}

public static int value = 123;

public SuperClass()

{

System.out.println("init SuperClass");

}

}

class SubClass extends SuperClass

{

static

{

System.out.println("SubClass init");

}

static int b=10;

public SubClass()

{

System.out.println("init SubClass");

}

}

运行结果:

SSClass

SuperClass init!

123

也许你还会疑惑为什么没有SubClass init,只有直接定义这个字段的类才会被初始化,该类中的静态块并不会被执行,其只是用了继承了父类SuperClass中的value,故SubClass并没有被装载。程序中main里被注释的其他几个命令可以依次分别运行下哦,有助于更好的理解!

还想提个问题:JAVA里自带的jar包里的类和我们设置的同包同名的类是怎么区分的呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值