为什么需要构造方法
上一页你创建了类,并用 new 创建了对象。构造方法就是在 new 调用期间真正运行的代码。它唯一的目的是交回一个可以直接使用的对象:所有必需的字段都已设置,没有初始化到一半的状态。
构造方法看起来像方法,但有两点不同:它与类同名,并且没有返回类型,连 void 都没有。
new Point(3, 4) 先分配对象,然后以 x = 3 和 y = 4 运行构造方法体。到 new 返回时,p 已经完全初始化好了。
this 关键字
在上面的构造方法中,参数名是 x 和 y,与字段同名。this.x 表示“这个对象的字段 x”,而单独的 x 指的是参数。没有 this,x = x 只会把参数赋给它自己,字段保持不变。
只有当参数遮蔽了字段时才必须写 this,但很多人为了清晰到处都写。常见的错误是名字冲突时忘了写它:代码能编译、能运行,却悄悄让字段保持默认值(null、0、false)。
默认构造方法
如果你从没写过构造方法,Java 会悄悄提供一个默认构造方法:一个公有的、无参数的、不做任何额外事情的构造方法。这就是为什么在完全没有构造方法的类上也能使用 new。
问题在于:只要你写了任何一个构造方法,这个免费的就没有了。
class Box {
int size;
Box(int size) { // now there is NO no-arg constructor
this.size = size;
}
}
new Box(); // compile error: no constructor Box() exists
如果你仍希望 new Box() 能用,就自己声明无参构造方法:
构造方法重载
一个类可以有多个构造方法,只要它们的参数列表不同。这其实就是把方法重载用在了构造方法上。每个构造方法都提供了一种创建对象的不同方式。
Java 会根据你传给 new 的参数个数和类型,选择匹配的构造方法。
用 this() 链式调用
注意上面的重复:每个构造方法都自己给字段赋值。你可以让一个构造方法用 this(...) 调用另一个,从而避免重复。这个调用必须是构造方法中的第一条语句。
现在真正的初始化只集中在一处。较简单的构造方法只是填上默认值,再把工作转交出去。如果你试图在 this(...) 之前放一条语句,编译器会拒绝。
构造方法与 super()
每个构造方法都会先隐式调用父类的构造方法。如果你什么都不写,Java 会在方法体开头插入一个隐藏的 super()(父类的无参构造方法)。等你开始编写子类时,就会直接用到它,这也是下一个主题。
class Animal {
String name;
Animal(String name) { this.name = name; }
}
class Dog extends Animal {
Dog(String name) {
super(name); // must call the parent constructor explicitly here
}
}
因为 Animal 没有无参构造方法,Dog 必须显式调用 super(name):没有免费的 super() 可以兜底。和 this() 一样,super(...) 调用也必须是构造方法中的第一条语句。
下一步:继承
构造方法负责初始化单个对象,但 super() 已经暗示了更大的东西:类可以建立在其他类之上,复用它们的字段、方法和构造方法。这种一个类扩展另一个类的关系就是继承,也就是下一页的内容。
常见问题
Java 中的构造方法是什么?
构造方法是一种特殊的方法,在你用 new 创建对象时运行。它和类同名,没有返回类型(连 void 都没有)。它的任务是让新对象处于有效的初始状态,通常是把构造方法的参数赋给对象的字段。
Java 中构造方法和普通方法有什么区别?
构造方法与类的名字完全相同,不声明返回类型,而且只能在创建对象时通过 new 调用。普通方法有自己的名字,声明返回类型(或 void),在已有的对象上调用。构造方法负责初始化,普通方法负责之后的工作。
如果我不写构造方法会怎样?
编译器会免费给你一个默认构造方法:它不接收参数,除了隐式的 super() 调用之外什么也不做。但只要你自己写了任何一个构造方法,这个免费的就会消失。所以如果你添加了带参数的构造方法,又仍希望 new Thing() 能用,就必须显式声明无参构造方法。