当程序主动使用某个类时,如果该类还未被加载到内存中,系统通过加载,连接,初始化来对该类进行初始化。这三个步骤可以统称为类加载或类初始化。
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"); //这句话才会初始化该类。
}
}
总之类的加载过程是由加载,连接,初始化来对该类进行初始化。