Java对象初始化

引言

先做一道测试题:
class Foo {
	int i;
	Foo() {
		i = 1;
		int x = getValue();
		System.out.println(x);
	}
	protected int getValue() {
		return i;
	}
}

class Bar extends Foo {
	int j;
	<pre name="code" class="java">Bar() {
    super();
    j = 2;
}
 
 上面的代码输出结果是0,你做对了吗? 

Java对象的初始化

在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。对象初始化既可以通过调用构造函数的显示方式,也可以使用自动装箱机制等的隐式初始化方式。当一个对象被创建之后,系统会为其分配内存,主要用来存放对象的成员变量以及从父类继承而来的变量。系统在为这些变量分配内存的同时,也会给它们赋默认值。

1、构造函数

Java中的每一个类都至少有一个构造函数。如果我们没有显示地定义构造函数,编译器会自动为我们生成一个无参的构造函数。构造函数与其他的方法基本一样,只不过没有返回值,方法名与类名相同。在生成的字节码中,构造函数被命名成<init>方法,参数保持不变。Java还要求一个对象在初始化之前,其父类必须被初始化,这一点是在构造函数中保证的。因此在类的构造函数中,第一条语句必须是调用父类的构造函数。如果我们没有显示地调用父类的构造器,编译器会自动生成调用父类构造器的指令。
public class Test{

    public Test{
    // ...
    }
}
对于上面代码中定义的类,如果观察编译之后的字节码,我们会发现编译器为我们生成了一个构造函数:
aload_0
invokespecial	#8; //Method java/lang/Object."<init>":()V
return
上面代码的第二行就是调用Object对象的构造函数的指令。正因为如此,如果我们显式调用父类的构造函数,那么调用语句必须放在构造函数的最前面,这么做才可以保证一个对象在初始化之前其父类已经被初始化完成。 
如果我们在构造函数中调用另外一个构造函数,看下面的代码:
public class Examples {
	private int i;
	Examples() {
	    this(1);
	    ....
	}
	
	Examples(int i) {
	    ....
	    this.i = i;
	}
}
对于这种情况,Java只允许在Examples(int i)内出现调用父类的构造函数,也就是说,下面的代码编译是无法通过的:
public class Examples {
	private int i;
	
	Examples() {
	    super();
	    this(1);
	}
	
	Examples(int i) {
	    ....
	    this.i = i;
	}
}
Java对构造函数作出这种限制,目的是为了要保证一个类中的实例变量在被使用之前已经被正确地初始化,不会导致程序执行过程中的错误。
回到引言中的问题:在new Bar()的时候,编译器会首先调用super()方法。于是构造函数就变成了下面的结构:
Bar() {
    super();
    j = 2;
}
调用父类的构造器时,会调用getValue()方法。而Bar重写了父类的getValue()方法,因此会return j。此时j = 2还没有被执行,因此返回j的默认值0。

2、实例变量初始化器与实例初始化器

我们可以在定义实例变量的同时,对实例变量进行赋值,赋值语句就是实例变量初始化器,比如:
public class Instance {
	private int i = 1;
	private int j = i + 1;
}
我们还可以通过实例初始化器来执行对象的初始化操作,比如:
<pre name="code" class="java"><pre name="code" class="java">public class Instance {
	private int i = 1;
	private int j;
	{
	    j = 2;
	}
}
 
  
 
  上面代码中花括号内代码,在Java中就被称作实例初始化器。实例变量初始化器和实例初始化器在对象的初始化时,会紧跟对父类的初始化,先于其他代码被执行。而实例变量初始化器和实例初始化器之间,会根据顺序的先后执行。如果我们定义了实例变量初始化器与实例初始化器,那么编译器会将其中的代码放到类的构造函数中去,这些代码会被放在对父类构造函数的调用语句之后(Java要求构造函数的第一条语句必须是父类构造函数的调用语句),构造函数本身的代码之前。 
 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值