public class Book {
public static void main(String[] args) {
staticFunction();
}
static Book book = new Book();
static {
System.out.println("书的静态代码块");
}
{
System.out.println("书的普通代码块");
}
Book() {
System.out.println("书的构造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
public static void staticFunction() {
System.out.println("书的静态方法");
System.out.println(amount);
}
int price = 110;
static int amount = 112;
}
从上面一个例子的结果进行分析。
先看执行结果:
书的普通代码块
书的构造方法
price=110,amount=0
书的静态代码块
书的静态方法
112
两道面试题,带你解析Java类加载机机制这篇文章里写的很详细。
这里大体写一下过程。
Java类加载7个过程
Java类加载分7个过程。分别是,加载,验证,准备,解析,初始化,使用,卸载
。
- 加载
加载就是将class文件载入jvm中。 - 验证
主要校验载入的class是否符合jvm规范。比如魔数校验,版本号校验,逻辑验证。 - 准备
主要是为类变量分片内存并给类变量设置初始值。即只分配static修饰的值。例如:private static int var=12
;这行代码在准备阶段内存已经分配完成,此时的var变量为0. 那么什么时候赋给12呢? 在类初始化的时候。即执行<clinit>()时。
也有例外。private static final int var = 12
.此时的var变量为12. - 解析
这个阶段的主要任务是将其在常量池中的符号引用替换成直接其在内存中的直接引用。 - 初始化
一般来说当 JVM 遇到下面 5 种情况的时候会触发初始化:
☐ 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
☐ 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
☐ 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
☐ 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
☐ 当使用 JDK1.7 动态语言支持时,如果一个 java.lang.invoke.MethodHandle实例最后的解析结果 REF_getstatic,REF_putstatic,REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。
初始化主要是执行<clinit>()方法的过程。
<clinit>()方法过程如下:
(1)首先收集类变量,包括static修饰的变量和static修饰的代码块。顺序是源码中的顺序。
(2)如果子类初始化的时候,发现父类没有初始化,则先初始化父类。
其中,除了类初始化,还有对象初始化。即new出一个对象。其执行的<init>()方法。
VM 会按照收集成员变量的赋值语句、普通代码块,最后收集构造方法,将它们组成对象构造器,最终由 JVM 执行。
整个过程是先执行<clinit>(),再执行<init>().
按照上面的规则。我们来走读一下例子里的代码。
1、首先,main方法是入口,需要先初始化main方法所在的类。
类构造方法<clinit>()里执行的代码如下:
static Book book = new Book();
static {
System.out.println("书的静态代码块");
}
static int amount = 112;
其中new Book()
进行对象初始化<init>().
执行的代码如下:
{
System.out.println("书的普通代码块");
}
int price = 110;
Book() {
System.out.println("书的构造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
两段代码合起来的执行过程如下:
{
System.out.println("书的普通代码块");
}
int price = 110;
Book() {
System.out.println("书的构造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
static {
System.out.println("书的静态代码块");
}
static int amount = 112;
staticFunction();
从这里看到打印的结果:
书的普通代码块
书的构造方法
//此时amout还没有赋值,还是初始值。
price=110,amount=0
书的静态代码块
书的静态方法
112