Java Initializer Java 初始化程序
程序中的初始化是指对类中域field(就是属性property)和局部变量(local variable)赋初值。在Java中初始化分为显式初始化(Explicitly)和隐式初始化(Implicitly)。
域的隐式初始化(Field Implicit Initialization)
Java虚拟机负责对域进行隐式初始化;隐式初始化总是在任何代码之前,因为虚拟机要保证数据的正确性,这成全了粗心的coder不必为初始化担心。
实例域(instance field)的隐式初始化:
其原理是当实例化一个类时(不论以何种方式遇到new关键字时), 虚拟机就会在相应的堆内存上为该类对象开辟一块空间,内存空间大小在编译阶就道,因为类中属性为原始数据类型和引用类型,引用类型存放的地址占内存 字节数是确定的(究竟多少我也不清楚)原始数据类型所占用的内存大小是固定的。当虚拟机为对象分配完内存空间后,接着会对此块内存清零,清零的结果导致所 有的对象属性将获得一个初始值,至此,实例域的隐式初始化就完成了,初始值也可以获得。
静态域(static field)的隐式初始化
当类中定义了静态域之后,事情开始变得复杂了;这可以与类中的静态代码块(static{…})一起来说;当主程序中第一次使用虚拟机的类池中不存在的类时(不管是实例化此类的对象,还是调用类中静态方法),类加载器ClassLoader(其实是应用类加载器的一个实例)就会将此类在类池中引入,其做法就是为此类创建一个Class类对象;这样也就是在堆内存中开辟了一个空间,存放Class的一个实例,此实例中保存的是刚才ClassLoader加载的类信息;静态域作为类信息,按照上面讲的“实例域的隐式初始化”过程来初始化,接着执行静态代码块。为什么先初始化静态域,然后再执行静态代码块,可以请读者先想一想,将在此文后头进行讲解。
域的显式初始化(Field Explicit Initialization)
实例域的显式初始化
实 例域的显式初始化可以在类中定义该域时进行,也是就为该域显式地为该域赋值;也可以在类构造体中为其初始化;类构造体包括构造方法和公共构造体(很多人不 知道公共构造体的概念,其实就是在类定义体中,加上一对大括号,然后在括号中编写代码,公共构造体的作用是将构造方法中相同部分提取出来(factor out),放在一个不同的构造方法中供重用,调用不同的构造方法时,都会先执行公共构造体中的代码);那么域的显式初始化顺序可以获知了:先执行定义时初始化,再执行构造体中的初始化。
静态域的显式初始化
静态域的显式初始化是在ClassLoader将类载入虚拟机类池的时候进行的,我们也可以在静态域定义处为该域赋初值,或者在static区内为静态域赋值;当ClassLoader在堆内存区为该类创建Class对象时,对静态域首先执行隐式初始化,接着对静态域进行显式初始化,次序是先执行定义处赋值,再执行static区内赋值。
由此我们可以得出结论:其实类域的初始化是在必要时才进行的,我们可以把静态域的初始化也理解成实例域的初始化,静态域就是某个Class对象的实例属性,而static代码块是虚拟机为创建Class类对象时所执行的构造体,所以也按照1.开辟内存空间给域并清零;2.执行域定义处的赋值;3.执行构造体内的赋值;对于后两项,只有在coder写出相应的代码时才会执行。
可以查看下面一段代码来验证以上的赋值顺序,但是对于第一步,则无法与后两步结合验证。
package initializationDemo;
class Item{
public Item(){
System.out.println("Item()");
}
public Item(int id){
System.out.println("Item("+id+")");
}
}
class ItemLocker{
static Item item1=new Item();
Item item2=new Item();
static{
item1=new Item(1);
}
{
System.out.println("this is the instance field block");
}
public ItemLocker(){
item2=new Item(2);
}
}
publicclass MainClass {
publicstaticvoid main(String[] args){
try {
Class.forName("ItemLocker");
} catch (ClassNotFoundException e) {
}
ItemLocker il=new ItemLocker();
}
}
运行结果为:
Item()
Item(1)
Item()
this is the instance field block
Item(2)
最后,局部变量只支持显式初始化。局部变量在声明定义之后可以进行显式的初始化(explicitly initialized),也可以在使用前进行赋值,但是注意java虚拟机需要保证局部变量在被使用之前必须经过初始化了,这在《Thinking in java》第四版中明确说明过,这么做是为了提醒程序员可能出现的漏洞,是一种代码安全机制。