成员初始化
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()行创建了一个新的对象,并调用其默认构造器(尽管没有明确定义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的类:
1、 当首次创建类型Dog的对象时,或者Dog的静态方法被访问时,Java解释器必须查找类Dog.class。
2、 然后载入Dog.class,有关静态初始化的所有动作都会执行。静态初始化只在这个时间进行一次。
3、 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4、 这块存储空间会被清零,这就自动将Dog对象中的所有基本类型数据都设置成了默认值,而引用则被置成了null。
5、 执行所有出现于字段定义处的初始化动作。
6、 执行构造器。