JAVA REVIEW_JVM类的加载、连接、初始化

时间:2021-09-04 14:39:33

    当程序主动使用某个类时,如果该类还未被加载到内存中,系统通过加载,连接,初始化来对该类进行初始化。这三个步骤可以统称为类加载或类初始化。

JVM和类

同一个JVM的所有线程,所有变量都处于同一个进程里,它们都使用该JVM进程的内存区。JVM进程结束,该进程在内存中的状态也将会丢失

class A{
public static int a=6;
}
class ATest1{

public static void main(String[] args) {
A a=new A();
a.a++;
System.out.println(a.a);
}
}

此程序中第一次加载时7,第二次加载还是7,第一次执行结束后JVM进程结束,内存中的所有的修改将丢失。所以第二次运行将再次初始化A。

当系统出现以下几种情况时,JVM进程将终止

程序运行到最后正常结束

程序运行到使用sysytem.exit或Runtime.getRuntiem().exit()代码处结束程序

程序执行过程中遇到未捕获的异常或错误而结束

程序所在的平台强制结束了JVM进程

类的加载

类加载时把类的class文件读入内存,并为之创建一个java.lang.class对象。


JVM提供的这些类加载器是系统类加载器,开发者可以通过继承classLoader基类创建自己的类加载器


被加载类的来源:

从本地文件系统加载class文件,这是前面绝大部分实例程序的类加载方式

从jar包加载class文件,这种方式也是常见的,加载jdbc驱动

网络加载class文件

java源文件动态编译,并执行加载

类加载器可以预先加载某些类。


类的连接

当类被加载以后,系统为之生成一个对象的class对象,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。连接阶段又分三个阶段

验证,验证阶段永远检验被加载类是否有正确的内部结构,并和其他类协调一致

准备,类准备阶段则负责为类的类变量分配内存,并设置默认初始值

解析,将类的二进制数据中的符号引进替换成直接引用


类的初始化

此阶段是对类变量进行初始化,对类指定初始值有两种方式:

1.声明变量时指定初始值

2.使用静态初始化块为类变量指定初始化值

此代码中ab赋值为5和6.cd没有指定初始值,采用默认初始值0

 

class Test{
static int a =5;
static int b;
static int c;
int d;
static{
b=6;
}
}

class TestMain{
public static void main(String[] args) {
System.out.println(Test.a);
System.out.println(Test.b);
System.out.println(Test.c);
Test t=new Test();
System.out.println(t.d);
}
}

变量指定初始值,静态初始化块是类的初始化语句。JVM会按这些语句在程序中排列顺序依次执行他们。


class Test1{
static{
b=6;
System.out.println("-------------");
}
static int a=5;
static int b=9;
static int c;

public static void main(String[] args) {
System.out.println(Test1.b);
}
}

最后输出是:

-------------

9

JVM初始化一个类包含如下几个步骤

1.类没有加载和连接,先加载并连接

2.直接父类没有初始化,先要初始化父类

3.没有初始化语句,系统依次执行初始化语句

类的初始化时机

系统初始化类或接口的6种方式

创建实例。new

调用某个类或类方法(静态方法)

访问某个类或接口的类变量,为该类变量赋值

使用反射方式来强制创建某个类或接口

初始化某个类的子类。初始化子类是,该子类的所有父类都被初始化

使用java.exe命令来运行某个主类。运行主类时,程序会先初始化该类。


对于一个final型的类变量,如果该类变量的值在编译时可以确定下来,那么这个类变量相当于宏变量。直接将出现的类变量的地方替换掉,不会导致该类的初始化(不会允许执行static代码块)。

class MyTest{
static{
System.out.println("静态初始化块");
}
static final String compileConstant="疯狂java讲义";
}
class TestMain1{
public static void main(String[] args) {
System.out.println(MyTest.compileConstant);
}
}
输出:疯狂java讲义


final修饰的类变量,如果类变量在编译时不能确定下来,则必须等到运行时才能确定下来该变量的值,会导致类被初始化。

class MyTest1{
static{
System.out.println("静态初始化块");
}
static final String compileConstant1=System.currentTimeMillis()+"";
}
class TestMain2{
public static void main(String[] args) {
System.out.println(MyTest1.compileConstant1);
}
}


输出:静态初始化块

              1461748382581

 

当使用ClassLoader类的loadClass方法类加载某个类时,该方法只是加载该类,不会出初始化。使用class的forName方法后才能初始化该类。

 

class Tester1{
static{
System.out.println("Tester类静态初始化块");
}

}
public class ClassLoaderTest{
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader cl=ClassLoader.getSystemClassLoader();
cl.loadClass("Tester1");//加载该类
System.out.println("系统加载Tester类");
Class.forName("Tester1"); //这句话才会初始化该类。
}
}

总之类的加载过程是由加载,连接,初始化来对该类进行初始化。