构造方法
概述
构造方法(Constructor)是类中用于初始化对象的一种特殊方法:在通过 new 创建对象时,JVM 会自动调用对应类中的构造方法,从而在对象「诞生」时就完成成员变量的赋值或其它初始化逻辑。构造方法的方法名必须与类名完全相同,且没有返回值类型(连 void 也不写)。
理解构造方法,可以避免对象创建后成员变量处于默认值或 null 的不可控状态,是 类与对象 之后、封装 与 继承 的重要基础。
前置建议
已掌握 类与对象,理解 new 类名() 创建对象及成员变量的访问。
基本语法
构造方法的定义规则
- 方法名:必须与类名完全一致(大小写一致)。
- 无返回值:不能写
void或其它返回类型;若写了返回类型,就变成普通方法而非构造方法。 - 修饰符:可以是
public、protected、private或默认(包私有),通常使用public。 - 参数:可以有零个或多个参数,与普通方法一样支持重载。
基本格式
// 无参构造方法
public 类名() {
// 初始化逻辑,可为空
}
// 有参构造方法
public 类名(参数类型1 参数1, 参数类型2 参数2, ...) {
// 通常用参数为成员变量赋值
}使用示例
示例 1:无参构造与有参构造
无参构造方法在 new 类名() 时被调用,有参构造方法在 new 类名(实参...) 时被调用;可根据需要为类提供多种构造方法(重载)。
public class Person {
String name;
int age;
// 无参构造:创建对象时给成员变量设默认值
public Person() {
name = "未命名";
age = 0;
}
// 有参构造:创建对象时直接传入 name 和 age
public Person(String name, int age) {
this.name = name; // this 表示当前对象,见下文
this.age = age;
}
void sayHello() {
System.out.println("Hello, I'm " + name + ", " + age + " years old.");
}
}
// 使用
Person p1 = new Person(); // 调用无参构造,name="未命名", age=0
Person p2 = new Person("张三", 20); // 调用有参构造,name="张三", age=20
p1.sayHello(); // Hello, I'm 未命名, 0 years old.
p2.sayHello(); // Hello, I'm 张三, 20 years old.提示
有参构造中常用 this.成员变量 = 参数 区分参数与成员变量(参数名与成员变量同名时,不加 this 的为参数)。this 表示「当前对象」,详见后续封装与多态章节。
示例 2:默认构造方法
若类中没有显式定义任何构造方法,编译器会自动提供一个无参的默认构造方法(即 public 类名() { }),因此可以写 new Person()。一旦类中定义了任意一个构造方法(无论有参无参),编译器就不再自动提供默认无参构造方法。
public class Book {
String title;
String author;
// 只定义了有参构造
public Book(String title, String author) {
this.title = title;
this.author = author;
}
}
// 可以这样用
Book b = new Book("Java 核心技术", "Cay S. Horstmann");
// 若取消下面注释,会编译错误:找不到无参构造方法
// Book b2 = new Book();若希望既保留有参构造,又能无参创建对象,需要显式写出无参构造方法:
public class Book {
String title;
String author;
public Book() {
title = "";
author = "";
}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
}注意
子类构造方法会隐式或显式调用父类构造方法。若父类只定义了有参构造、没有无参构造,子类必须通过 super(参数) 显式调用父类有参构造,否则编译报错。详见 继承。
示例 3:构造方法重载与 this() 调用
一个类可以有多个构造方法(重载:方法名相同、参数列表不同)。在某个构造方法中,可以通过 this(参数列表) 调用本类的其它构造方法,从而避免重复代码;this(...) 必须写在构造方法体的第一行,且最多调用一次。
public class Rectangle {
double width;
double height;
// 无参构造:默认 0x0
public Rectangle() {
this(0, 0); // 调用本类两参构造,必须位于第一行
}
// 单参构造:正方形
public Rectangle(double side) {
this(side, side); // 调用两参构造
}
// 两参构造:实际完成赋值
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
double getArea() {
return width * height;
}
}
// 使用
Rectangle r1 = new Rectangle(); // 0x0,面积 0
Rectangle r2 = new Rectangle(5); // 5x5 正方形,面积 25
Rectangle r3 = new Rectangle(3, 4); // 3x4,面积 12构造方法与 new 的关系
- 执行
new 类名(实参)时,JVM 会按实参类型和个数匹配类中对应的构造方法并执行。 - 构造方法执行完毕,对象才完成创建,引用被赋给等号左侧的变量。
- 构造方法不能像普通方法那样通过「对象.方法名()」主动调用,只能在
new时由 JVM 调用(同一类内部可通过this(实参)间接调用本类其它构造方法)。
注意事项
- 方法名与类名一致:若方法名与类名不同或写了返回类型,则变成普通方法,
new时不会调用,且可能失去「默认构造方法」。 - 默认构造方法会消失:只要类中定义了任意一个构造方法,默认无参构造就不再自动生成;需要无参创建时务必显式写出无参构造。
this(…)只能出现在构造方法中,且必须位于方法体第一行,不能与super(…)同时出现(子类构造方法中先通过super或this二选一调用,再写其它逻辑)。- 不要在构造方法中写过于复杂的逻辑或调用可能被重写的方法,否则在继承场景下可能导致未初始化完成就被子类重写方法访问,引发难以排查的问题。
提示
后续学习 封装与访问修饰符 时,常将成员变量设为 private,通过有参构造方法和 setter 方法赋值,既保证对象在创建时就有合理状态,又便于统一校验与维护。
相关链接
- 类与对象 — 对象创建与成员访问
- 封装与访问修饰符 — private 与 getter/setter
- 继承 — 子类与 super() 调用父类构造方法
- Oracle Java 教程 - 构造方法