Java会尽量保证:所有变量在使用前都能得到恰当的初始化,对于方法的局部变量,Java以编译时错误的形式来保证。如果写成:
viod f(){ int i; i++;//Error }
就会得到报错信息,i可能没有进行初始化。要是类的数据成员是基本类型,情况就不一样了,类的每个基本类型的数据成员都会有一个初始值,比如:
public class InitialValues { boolean t; char c; byte b; short s; int i; long l; float f; double d; InitialValues reference; void printInitialValues() { System.out.println("Data type Initial value"); System.out.println("boolean " + t); System.out.println("char [" + c + "]"); System.out.println("byte " + b); System.out.println("short " + s); System.out.println("int " + i); System.out.println("long " + l); System.out.println("float " + f); System.out.println("double " + d); System.out.println("reference " + reference); } public static void main(String[] args) { InitialValues iv = new InitialValues(); iv.printInitialValues(); } } /* 输出: Data type Initial value boolean false char [] byte 0 short 0 int 0 long 0 float 0.0 double 0.0 reference null */
成员变量和局部变量的不同:
成员变量
构造器是一个在创建对象时被自动调用的特殊方法。在Java中使用构造器可以为对象进行一些必须要初始化的工作,也是Java的垃圾清理的基础。下面是一个构造器的例子。
class Rock { Rock() { System.out.println("Rock "); } } public class SimpleConstructor1 { public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Rock(); } } }
在创建对象时,new Rock();将会为对象分配空间,并调用相应的构造器Rock(),这就使得我们在操作操作对象之前,对象已经被初始化了。
构造器的名称必须与类名完全相同;构造器是没有返回值的。
默认构造器
不接受任何参数的构造器叫做默认构造器,它的作用是创建一个默认对象,如果你写的类中没有构造器,则编译器会自动帮你创建一个默认的构造器。例如:
class Bird{} public class DefaultConstructor { public static void main(String[] args) { Bird bird =new Bird(); } }
new Bird()行创建了一个新的对象,并调用其默认构造器--尽管没有明确定义这个构造器,因为没有它的话,就没有方法可调用,也就无法创建对象。但是如果已经定义了一个构造器(无论是否有参数),编译器就不会再自动创建默认构造器:
class Bird2 { Bird2(int i) { } Bird2(double d) { } } public class NoSynthesis { public static void main(String[] args) { // Bird2 b = new Bird2(); Bird2 b2 = new Bird2(1); Bird2 b3 = new Bird2(1.0); } }
上述代码中,要是这样写:new Bird2()编译器就会报错:Cannot resolve constructor Bird2()。这种情况是因为,要是你没有提供任何构造器,编译器会默认建一个,但是如果你已经写了一个构造器,编译器不确定你是刻意省略的,还是遗漏了,也就不再创建默认的构造器了。
也可以在构造器里进行初始化,但是这不影响数据成员的初始化,数据成员的初始化会在构造器被调用之前完成。假如使用下述代码:
class Counter { int i; Counter() { i = 7; } }
那么i会先被置为0,然后变成7。对于所有基本类型和对象引用,包括在定义时已经指定初值的变量,都是这样的顺序。因此编译器不会强制你一定要在构造器的某个地方或在使用它们之前对元素进行初始化-因为已经进行过初始化。
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
静态数据的初始化
无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于局部域。如果一个静态的基本类型成员,也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。
下面的例子可以了解静态成员是何时初始化的:
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 StaticInitialization { 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) *///:~
从上面的例子可以看出:
静态成员的初始化,只有在引用时才会发生,如果不创建Table对象,也不引用Table.b1或Table.b2,那么静态的Bowl b1和b2永远不会被创建。只有在第一个Table对象被创建的时候,它们才会初始化。此后,静态对象不会再次被初始化。
总结一下对象的创建过程,假设有个名为Dog的类: