沛县网站定制,网站跟域名是什么关系,搜索推广是什么,杭州做网站吧Java源文件#xff08;.java文件#xff09;被编译器编译后变为字节码形式的类文件#xff08;.class文件#xff09;#xff0c;Java类加载的过程就是JVM加载.class的二进制文件并且放到内存中#xff0c;将数据放到方法区#xff0c;并且在堆区构造一个java.lang.clas…Java源文件.java文件被编译器编译后变为字节码形式的类文件.class文件Java类加载的过程就是JVM加载.class的二进制文件并且放到内存中将数据放到方法区并且在堆区构造一个java.lang.class对象并且完成类的初始化的过程。
//下述片段引用自 Java的类加载机制是什么
Java的类加载机制主要分为三个过程加载、连接和初始化。
1.加载机制
Java的类加载机制主要分为三个过程加载、连接和初始化。这三个过程的顺序是固定的但是每个过程中的细节却是不同的。下面我们来详细介绍一下这三个过程。
1.1 加载
Java的类加载器会根据类的全限定名来加载类当需要使用某个类时如果该类还未被加载进内存则需要执行一下步骤进行加载
1.1.1. 通过类的全限定名找到对应的class文件这里的class文件可以是.java文件经过编译之后生成的.class文件也可以是通过其他方式生成的.class文件。
1.1.2 将class文件中的二进制数据读取到内存中并将其转换为方法区的运行时数据结构。
1.1.3 创建由该类所属的java.lang.Class对象。该对象可以理解为是对类的各种数据如名称、访问修饰符、方法、成员变量等的封装。
在加载类时类加载器除了加载某个具体的类外还需要将这个类所依赖的类也加入到内存中。这种依赖性是多层级的也就是说被依赖的类又可能会去依赖其他类所以在加载一个类时通常需要将其类图中所有的类都加载进来。
1.2 连接
Java虚拟机在加载类之后需要对类进行连接连接分为三个步骤验证、准备和解析。
1.2.1. 验证在这个步骤中Java虚拟机主要确保所加载的类的正确性。验证过程主要包括文件格式验证、元数据验证、字节码验证和符号引用验证等。其目的在于确保目标.class文件的字节流中包含的信息符合当前虚拟机的要求并且不会危害虚拟机运行时环境安全。
1.2.2. 准备在准备阶段Java虚拟机为类的静态变量分配内存并设置变量的初始值。这里需要注意的是在这个阶段中分配的内存并不包含那些用户自定义的初始化值这些值在初始化阶段中进行设置。
1.2.3. 解析Java在这个阶段中将常量池中的符号引用转为直接引用。通过符号引用虚拟机得知该类访问其他的类或者类中的字段、方法等但在类初始化时需要缓存这些直接引用以便于直接调用。 1.3 初始化
在类的准备阶段Java虚拟机已经为静态变量分配了内存并设置了初值但是这些静态变量”赋初值“的动作并没有完成。初始化阶段会为静态变量设置用户自定义的初始化值并执行类构造器clinit()方法以执行初始化操作。
此时类的准备和初始化阶段已经执行结束Java的类加载机制总的过程也就结束了
//引用结束
注意静态代码的初始化分为两步连接的准备阶段包含初始化最后又有一个初始化步骤两者并不重叠前者是给静态成员变量分配内存并且设置类型的初始值后者是给静态成员变量设置用户指定的初始值。这段话有点拗口代码来说明更清晰。 1、父类 parent.java文件
public class Parent {static {System.out.println(Parent static block 1.);parentStaticIntVar 3;}static Integer parentStaticIntVar 2;static {System.out.println(Parent static block 2.);System.out.println(parentStaticIntVar parentStaticIntVar);parentStaticIntVar 4;}{System.out.println(Parent not static block 1.);parentIntVar 30;}Integer parentIntVar 20;public Parent(){ System.out.println(Parent construct method .);System.out.println(parentIntVar parentIntVar);}public void f(){System.out.println(parent f().);}{System.out.println(Parent not static block 2.);parentIntVar 40;}}2、子类 Sub.java文件
public class Sub extends Parent {static {System.out.println(Sub static block 1.);subStaticIntVar 3;}static Integer subStaticIntVar 2;static {System.out.println(Sub static block 2.);System.out.println(subStaticIntVar subStaticIntVar);subStaticIntVar 4;}{System.out.println(Sub not static block 1.);subIntVar 30;}Integer subIntVar 20;public Sub(){ System.out.println(Sub construct method .);System.out.println(subIntVar subIntVar);}public void f(){System.out.println(Sub f().);}{System.out.println(Sub not static block 2.);subIntVar 40;}}3、TestMain.java文件
public class TestMain {public static void main(String[] args) {System.out.println(-----class-----);System.out.println(subStaticIntVar Sub.subStaticIntVar);System.out.println(parentStaticIntVar Sub.parentStaticIntVar);System.out.println(-----instance-----);Sub s new Sub();s.f();}}
4、输出结果
-----class-----
Parent static block 1.
Parent static block 2.
parentStaticIntVar2
Sub static block 1.
Sub static block 2.
subStaticIntVar2
subStaticIntVar4
parentStaticIntVar4
-----instance-----
Parent not static block 1.
Parent not static block 2.
Parent construct method .
parentIntVar40
Sub not static block 1.
Sub not static block 2.
Sub construct method .
subIntVar40
Sub f().解读一下后可以知道静态代码块和静态变量遵循如下规则
1、静态代码块先于构造方法执行
2、静态代码块可以给静态成员变量赋值
3、静态代码块之间按照先后顺序执行
4、父类的静态代码块先于子类的静态代码块执行
5、静态代码块先于非静态代码块执行
6、静态代码块在第一次使用这个类的时候执行并且只执行一次
7、静态变量的显式赋值和静态代码块的按照先后顺序执行 实际上每个Java源文件由编辑器编译后会自动给类加载器追加一个类初始化方法clinit()一个类只有一个包含静态变量的显式赋值代码和静态代码块的代码在源文件中看起来是一个一个独立的代码块实际上编译后都放到一个这个类初始化方法中去了。 非静态的代码块和变量的规则和上述类似。 类加载的方法
1、Class.forName()
Class? c Class.forName(com.example.zhangzk.reflect.TestServiceImpl);
加载类并且完成初始化。
2、ClassLoader.loadClass() Class? c Thread.currentThread().getContextClassLoader().loadClass(com.example.zhangzk.reflect.TestServiceImpl);
加载类不初始化。 加载器有哪些
启动类加载器C编写的进入Java世界的大门负责加载存放在$JAVA_HOME\jre\lib下或被-Xbootclasspath参数指定的路径中的并且能被虚拟机识别的类库如rt.jar所有的java.*开头的类均被Bootstrap ClassLoader加载。
扩展类加载器Java编写的父加载器为启动类加载器该加载器由sun.misc.Launcher$ExtClassLoader实现它负责加载$JAVA_HOME\jre\lib\ext目录中或者由java.ext.dirs系统变量指定的路径中的所有类库如javax.*开头的类。
应用程序类加载器Java编写的父加载器为扩展类加载器该类加载器由sun.misc.Launcher$AppClassLoader来实现它负责加载用户类路径ClassPath所指定的类开发者可以直接使用该类加载器如果应用程序中没有自定义过自己的类加载器一般情况下这个就是程序中默认的类加载器。
自定义类加载器Java编写的父加载器为应用程序类加载器典型代表是Tomcat为了在一个TOMCAT进程下部署多个JAVA应用程序必须要自定义类加载器进行应用隔离。 双亲委派模型
每个类加载器需要加载类的时候先请求父类加载器来加载一直到启动类加载器启动类加载器是没有父类加载器的父类加载器找不到给类才由自己来加载。 要想搞清楚Spring Boot的启动流程必须要要知道上述区别只有充分利用好上述差异才能精准的控制加载和初始化的过程Spring中这些都用的出神入化了。