深入剖析java对象创建

来源:互联网 发布:淘宝网手机客户端 编辑:程序博客网 时间:2024/06/05 03:47

1. new表达式做了什么?

Java语言里,new表达式(new+构造器)负责两个动作:

第一,分配对象空间并对其做默认初始化。默认初始化会将对象的所有成员字段设到其类型对应的默认值(零值)。

第二,初始化对象。

其中构造器只负责第2点,第1点是包含在new表达式里的语义。

2. Java如何初始化对象?

2.1当一个对象被创建时,虚拟机会为其分配内存(主要用来存放对象的实例变量及其从超类继承过来的实例变量)(即使这些从超类继承过来的实例变量有可能被隐藏也会被分配空间)。在为这些实例变量分配内存的同时,会执行默认初始化,这些实例变量也会被赋予默认值(零值)。

public class InstanceInitializer {

private int j = getI();

private int i = 1;

public InstanceInitializer() {

i = 2;

}

private int getI() {

return i;

}

public static void main(String[] args) {

InstanceInitializer ii = new InstanceInitializer();

System.out.println(ii.j);

}

}

如果我们执行上面这段代码,那么会发现打印的结果是0。因此我们可以确信,变量j被赋予了i的默认值0,而不是经过实例变量初始化器和构造函数初始化之后的值。

 

关于实例变量隐藏的插曲:

先看方法的覆盖(override)

class Foo {

void f(){

System.out.println("Foo");

}

}

public class Bar extends Foo {

void f(){

System.out.println("Bar");

}

public static void main(String[] args) {

Foo foo = new Bar();

Bar bar = new Bar();

foo.f();

bar.f();

}

}

输出:

Bar

Bar

说明,不管引用的类型是什么,只要它引用的是子类对象,调用到的都是子类的方法。然而实例变量只会被隐藏,不会被覆盖,根据引用类型不同,所访问到的实例变量就不一样。

class Foo {

int i = 0;

}

public class Bar extends Foo {

int i = 1;

public static void main(String[] args) {

Foo foo = new Bar();

Bar bar = new Bar();

System.out.println(foo.i);

System.out.println(bar.i);

}

}

上面的代码中,Foo和Bar中都定义了变量i,在main方法中,虽然foo 和bar两个引用都是引用的Bar 对象,但是System.out.println(foo.i)打印0;而System.out.println(bar.i)打印1。说明父类的i在子类对象中依然存在。子类对象只是在父类对象外面又包了一层,加上了自己的变量(父类和子类可以有同名变量)。

实例变量隐藏的插曲完。


在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值(这是第一次赋值,此赋值不可避免)。

2.2在内存分配完成之后,java的虚拟机就会开始对新创建的对象执行初始化操作。Java中,有三种执行对象初始化的结构,分别是实例变量初始化器、实例初始化器以及构造函数。

我们可以在定义实例变量的同时,对实例变量进行赋值,赋值语句就实例变量初始化器了比如,

publicclassInstanceVariableInitializer {

privateinti = 1;

privateintj = i + 1;

}

我们还可以通过实例初始化器来执行对象的初始化操作,比如,

publicclassInstanceInitializer {

privateinti = 1;

privateintj;

{

j = 2;

}

}

上面代码中花括号内代码,在Java中就被称作实例初始化器

如果我们定义了实例变量初始化器与实例初始化器,那么编译器会将其中的代码放到类的构造函数中去实例变量初始化器与实例初始化器地位等同,执行先后顺序依从代码顺序),这些代码会被放在对超类构造函数的调用语句之后(还记得吗?Java要求构造函数的第一条语句必须是超类构造函数的调用语句),构造函数本身的代码之前。

 

3. 一个实例变量在对象初始化的过程中会被赋值几次?

3.1在本文的前面部分,我们提到过,JVM在为一个对象分配完内存之后,会给每一个实例变量赋予默认值,这个时候实例变量被第一次赋值,这个赋值过程是没有办法避免的。

3.2如果我们在实例变量初始化器中对某个实例x变量做了初始化操作,那么这个时候,这个实例变量就被第二次赋值了。

3.3如果我们在实例初始化器中,又对变量x做了初始化操作,那么这个时候,这个实例变量就被第三次赋值了。

3.4如果我们在类的构造函数中,也对变量x做了初始化操作,那么这个时候,变量x就被第四次赋值。

也就是说,一个实例变量,在Java的对象初始化过程中,最多可以被初始化4次

 


0 0