初始化
1. 使用构造器
在Java中通过使用构造器,类的设计者可以确保每个对象都正确得到初始化。运行时刻可以调用方法和执行某些动作来确定初值。创建对象时,如果类具有构造器,Java会在用户具有使用对象的能力之前自动的调用相应构造器,确保初始化的进行。
“创建”和“初始化”是两个独立的概念,但在Java中创建和初始化捆绑在一起不能分离。
class Type{
int i;
Type(int i){
i=10;
f(i);
System.out.print(i); }
void f(int i){}
}
public class Main{
public static void main(String[] args){
Type t = new Type(10); // 构造器没有任何返回的东西,但new表达式返回了一个对象的引用
}
}
但是,无法阻止自动个初始化的进行,它将在构造器被调用之前发生。例如上述Type类,i的值先初始化为0,然后是10。初始化的顺序决定于变量被定义的顺序。
静态数据的初始化
无论创建多少个对象,静态数据都只占用一份存储空间。
class Bowl{
Bowl(int marker){
System.out.println("Bowl("+marker+")");
}
void f1(int marker){
System.out.println("f1("+marker+")");
}
}
class Table{
static Bowl bowl1 = new Bowl(1);
Table(){
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker){
System.out.println("f2("+marker+")");
}
static Bowl bowl2 = new Bowl(2);
}
class Cupboard{
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard(){
System.out.println("Cupboard()");
bowl4.f1(2);
}
void f3(int marker){
System.out.println("f3("+marker+")");
}
static Bowl bowl5 = new Bowl(5);
}
public class JavaStaticInit {
public static void main(String[] args) {
System.out.println("Creating new Cupboard() in main");
new Cupboard();
System.out.println("Creating new Cupboard() in main");
new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
输出为:
Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
从上面的输出可以看出,静态数据的初始化在必要时刻才进行,而且只初始化一次,以后不会再初始化。
当 拥有main方法的类JavaStaticInit被加载时,类里面的静态成员table 和cupboard 就会被初始化,new Table()导致Table类被加载,在Table类中又有bowl1和bowl2两个静态成员,会执行初始化这两个静态数据,这又导致Bowl类会被 加载,Bowl类中没有静态数据。然后调用Bowl类的构造方法创建对象Table.bowl1和Table.bowl2,所以先输出“Bowl(1)” 和“Bowl(2)”,然后Tbale类构造器被执行创建JavaStaticInit.table对象,打印“Table()”,构造器中 bowl2.f1(1)的调用打印“f1(1)”,此时JavaStaticInit.table对象初始化完毕,接着初始化 JavaStaticInit.cupboard对象,导致Cupboard类会被加载,Cupboard类在两个静态数据前面先定义了一个非静态数据 bowl3,但还是会在加载类的时候先初始化静态数据bowl4、bowl5,所以先打印“Bowl(4)”和“Bowl(5)”,接着会初始化 bowl3,打印“Bowl(3)”,然后是构造器被执行,打印“Cupboard()”“f1(2)”,cupboard对象初始化完毕。
然后执行main方法......
对象的创建过程(以Table为例)实际上可以总结为:
(1)构造器给出了类名,即使没有显示的使用关键字static,构造器实际上也是静态方法。因此当首次创建Type类的对象或者Type类的静态方法/静态域被访问时,Java解释器查找类路径定位Type.class文件。
(2)载入Type.class,所有的静态这时都会执行初始化,并且静态数据只在类首次加载时初始化一次,以后不会再执行初始化。
(3)当用new Type()创建对象时,为Type对象分配足够的存储空间。
(4)执行自动的清零动作,例如,int类型被初始化为0,引用类型被初始化为null。
(5)执行所有非静态数据成员的初始化。
(6)执行构造器初始化(会导致基类的构造器的初始化)。
显示静态初始化
Java可以把多个静态初始化动作组织成一个特殊的“静态块”:static{ //init... }
示例:
class Clazz{
static String s1;
static String s2 = "string2";
// 静态字句
static{
System.out.println("static-block:"+s1);
System.out.println("static-block:"+s2);
s1="static_string1";
s2 = "static_string2";
}
static void printString(){
System.out.println("s1: "+s1);
System.out.println("s2: "+s2);
}
}
public class JavaStaticInitBlock {
public static void main(String[] args) {
Clazz.printString();
}
}
输出:
static-block:null
static-block:string2
s1: static_string1
s2: static_string2
静态语句块也只执行一次。
看下一个例子:
public class JavaStaticInitBlock2 {
static{
System.out.println("Static Block");
}
static String s1;
static String s2=f(2);
static {
System.out.println("static-block:"+s1);
System.out.println("static-block:"+s2);
s1="static_string1";
s2="static_string2";
}
static String f(int i){
System.out.println("static method f()");
return "string"+i;
}
public static void main(String[] args) {
System.out.println(JavaStaticInitBlock2.s1);
System.out.println(JavaStaticInitBlock2.s2);
}
}
输出:
Static Block
static method f()
static-block:null
static-block:string2
static_string1
static_string2
输出看出,static语句块和static域的初始化是按定义顺序进行。
非静态实例初始化
Java中有一种叫做实例初始化的类似语法,用来初始化每一个对象的非静态变量。语法格式为:要做的工作放在一对花括号中{ // init... }。
例如:
class Mug {
Mug(int maker){
System.out.println("Mug("+maker+")");
}
void f(int maker){
System.out.println("f("+maker+")");
}
}
class Mugs{
Mug mug1;
Mug mug2;
{
mug1 = new Mug(1);
mug2 = new Mug(2);
System.out.println("mug1 & mug2 initialized");
}
Mugs(){
System.out.println("Mugs()");
}
Mugs(int i){
System.out.println("Mugs(int)");
}
}
public class JavaInstantiationBlock {
public static void main(String[] args) {
System.out.println("main started...");
new Mugs();
System.out.println("new Mugs() completed");
new Mugs(1);
System.out.println("new Mugs(1) completed");
}
}
输出:
main started...
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs()
new Mugs() completed
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs(int)
new Mugs(1) completed
这种语法对于支持匿名内部类是必须的,它也使你可以保证无论调用哪一个显示构造器,某些操作都会发生。输出可以看出实例初始化语句块在构造器之前执行。