Java的构造函数(constructor)是一种特殊方法,实例化Java类时被调用来完成初始化的工作。它的定义和使用有一些特性和要求,这里是我总结的构造函数的特别之处。
- 构造函数的名称与其所属类的类名一致,方法名的首字母是大写的。我理解JVM创建类实例时,需要一个标识来确定类的构造函数。如果没有标识,对具体要创建实例的类,也许我们知道它的构造函数是那个,指定JVM去调用。可对这个类的父类,我们就没法指定了,JVM也就不知道该调用谁了。而使用类名作为方法名可以方便的把构造函数和其他方法区分开来,这就是一种特殊的标识,JVM通过这一特征就能找到要调用的构造函数了。
- 构造函数可以设置访问控制字段(private, public, protected, none)和注释(annotation),但不能使用这些描述符修饰:abstract, native, static, final, synchronized, stricfp 。
- 不能使用abstract是因为构造函数不是类成员,不能被继承,声明成abstact就永远无法被实现了。
- 不能使用native是基于实现复杂性的考虑,JVM可以方便的确认父类的构造函数正确的被执行了。
- 构造函数是在JVM分配类实例空间后初始化时调用的,也就是它的作用对象是类实例。static方法运行时类实例还不存在,这就有问题了。
- 因为构造函数不能被继承修改,所以使用final就没有必要了。或是说,构造函数默认就是final的。
- synchronized不能使用是因为它也不是必要的。类实例只有在初始化完成后才能被使用,我认为可以理解它已经是一个原子性的操作,不需要再用锁来避免冲突了。
- 不使用stricfp修饰构造函数是一种语言实现的考虑。更有效判断构造函数是否为FP-strict可以通过判断类本身是否为FP-strict。
- 构造函数不能定义返回类型。与之相比,普通方法必须定义返回类型,当不返回具体值或对象时,需定义为void。不能定义返回类型,我想可以理解成构造函数是有确定返回类型的,也就是对象的引用。注意,构造函数没有返回类型,但方法体内可以使用不含任何操作数的return语句。
- 如果一个Java类没有定义任何构造函数,编译器会自动创建一个默认的无参的构造函数。如果已有构造函数,编译器则不再会创建。注意,当构造函数所属类为非private的成员内部类时,编译器创建的默认构造函数有一个隐式的参数,也就是对包含该类的对象的引用。
- 构造函数可以被重载(Overloading), 并且在一个构造函数里可以调用另外一个。但是,这种调用必须是在方法的第一句,通过关键字this进行。
- 除调用类其他的构造函数,在构造函数里还可以使用super关键字调用直接父类的构造函数,这也必须是在方法的第一句进行。如果某一构造函数没有调用任何其他构造函数或直接父类的构造函数, 编译器会自动为其添加对父类无参构造函数的调用:super()。上面提到的编译器自动添加的构造函数其实就包含一句。注意,因为这里自动调用的是一个无参的构造函数,如果父类里没有,编译器会报错。
- "Thinking In Java"里指出,在构造函数里不要调用所属类的类成员, 因为调用时这些成员会造成不可预料的问题。例如:
public
class
ConstructorCallMethod {
private
String
value
;
public
void
test(){
System.
out
.println(
"In supper, string"
+
value
);
}
public
ConstructorCallMethod() {
System.
out
.println(
"Start super"
);
test();
System.
out
.println(
"End supper"
);
}
/**
* Expected output:
* Start super
* In extended, string:null
* End supper
* created
*
@param
args
*/
public
static
void
main(String[]
args
) {
ConstructorCallMethod
t
=
new
ConstructorCallMethod_Extension();
}
}
class
ConstructorCallMethod_Extension
extends
ConstructorCallMethod{
private
String
value
;
public
void
test(){
System.
out
.println(
"In extended, string:"
+
value
);
}
public
ConstructorCallMethod_Extension() {
value
=
"created"
;
System.
out
.println(
value
);
}
}