`

12、类初始化与对象实例化

 
阅读更多

在上章中讲解了类文件的生成和包含的信息,但是要加载到jvm中才能启动和运行;

#、一直以为   初始化和实例化   是一个意思,今天就来甑辩一下他们是否相同?

参看:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/

          http://blog.csdn.net/moreevan/article/details/6968718

开头说到 :类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要的两个环节,[这里说的是类初始化和对象的初始化,显然这是两个不同的概念,只是他们在大多数情况下是紧密执行的];

 

一个类的整个生命周期如下:


在装载(loading)阶段,类装载器(Bootstrap ClassLoader 或者自定义的ClassLoader) 把编译形成的class文件载入内存;

 

连接(lingking)阶段又可以分为三个子步骤:验证(verification)、准备(preparation)和解析(resolution)。

验证阶段    确保java文件数据格式 的正确性,并适于JVM使用。

准备阶段    JVM为静态变量/静态块分配内存空间;

解析过程    就是在class文件的常量池中寻找类、接口、字段和方法的符号引用,当程序运行时需要使用某个符号引用可以再去解析它。

 

初始化(initialization),初始化的5个启动点:

1.new关键字创建实例(对象实例化会启动初始化过程)

2.反射方法实例化对象(实例化)

3.调用子类时,先初始化父类(可能不会调用其构造方法)

4.初始化主类(主类一般不会实例化)

5.其他指令或句柄如:getstatic、putstatic、invokestatic、REF_getStatic、REF_putStatic、REF_invokeStatic。

注:初始化接口并不需要初始化它的父接口。

所以jvm的启动不会主动进行类初始化,除了主类

从前4条,可以理解,初始化和实例化是完全不同的概念,今后就不要再混淆了;

静态块  就是常说的 初始化方法:

public class Demos1 {
	static int a=45;
	static {
		System.out.println("i'm static block----"+a);
	}
	public static void main(String[] args) {
		System.out.println("i'm main funtion----"+a);
	}
}}
输出:
i'm static block----45
i'm main funtion----45

可以理解为,初始化就是为静态块、静态属性、静态方法分配空间的过程,从类的生命周期图看,他们是先于实例化(构造方法)执行的;

注:静态属性是要先于静态块执行的  储存在静态数据区,静态方法、方法也是在准备阶段存放在method area的;

所以当new一个对象实例的时候,先启动类的初始化过程(静态属性优先静态块执行),而不是先执行构造方法;

class Son{
	static{
		System.out.println("ok");
	}
	static int a = 34;
}
public class Demos1 {
	public static void main(String[] args) {
		System.out.println(Son.a);
	}
}
输出:
ok
34

在这段代码中,调用了Son的属性,这个过程会对Son进行初始化,所以就会先执行Son的初始化过程,后返回; 所以先打印ok而不是34;

注:Son.class不会执行初始化方法;

class Father{
	static {
		System.out.println("父类的初始化方法");
	}
	static int b = 35;
}
class Son extends Father{
	static int a = 34;
	static{
		System.out.println("ok");
	}
	
}
public class Demos1 {
	public static void main(String[] args) {
		System.out.println(Son.b);
	}
}
输出:
父类的初始化方法
35

上段代码,没有输出ok,由于先要对父类进行初始化,先执行b的赋值和打印"父类的初始化方法",完成之后发现b是父类的属性,就不再需要对子类Son初始化了,等以后需要初始化Son时再初始化,如: System.out.println(Son.a);执行Son的初始化方法;

 

jvm运行时,初始化方法只执行一次,静态属性赋值可以执行多次;

class Father{
	static {
		System.out.println("父类的初始化方法");
	}
	static  final int b = 35;
}
class Son extends Father{
	static int a = 34;
	static{
		System.out.println("ok");
	}
}
public class Demos1 {
	public static void main(String[] args) {
		System.out.println(Son.b);
	}
}
输出:
35

这段代码连父类的初始化方法都没有执行, 这又是为什么呢?

因为jvm将final声明的常量,放入到NonInitialization类的常量池中去了,它是属于NonInitialization的属性,与Father和Son活生生的脱离了关系,调用Son.b时指向了NonInitialization的这个属性,而不再执行类初始化方法;加上System.out.println(Son.a);可以得到验证:这一步才执行Father的初始化方法;

 

-XX:+TraceClassLoading

  • 大小: 126.4 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics