3.4 构造方法
构造方法是对象出生时自动执行的特殊方法——负责在 new 的那一刻把属性装上真值。相当于新手机的"开机设置向导",出厂(new)就完成,不用你事后挨个补。
1. 为什么需要构造方法——3.2/3.3 留下的两步麻烦
回顾一下现在造一辆车的流程:
Car car = new Car(); // 出生:属性全是默认值(null、0.0)
car.setName("宝马"); // 第 1 步补:名字
car.setPrice(200000); // 第 2 步补:价格
new Car() 出来的车是一辆"空白车":brand 是 null、price 是 0.0。然后必须记得挨个调 setter——忘了补一行,车就带着脏状态跑。而且这两步谁也拦不住:任何人都可以 new 完不 set,直接 car.run()。
需求很明确:能不能在 new 那一刻直接把值传进去? new Car("宝马", 200000)——有值当场给,没值别出厂。这就是构造方法。
2. 构造方法的写法——三个特征
public class Car {
private String brand;
private double price;
// 这就是构造方法:方法名 = 类名,没有返回值类型
public Car(String b, double p) {
brand = b; // 有 this 更好:this.brand = b;
price = p;
}
}
三个特征,一个都不能少(上面那张图的紫色条):
| 特征 | 说明 | 对照普通方法 |
|---|---|---|
| ① 方法名 = 类名 | Car 类的构造方法只能叫 Car |
普通方法随意起名 |
| ② 没有返回值类型 | 连 void 都不写——写了它就变成普通方法了 |
普通方法必须有 |
| ③ new 时自动执行 | 你永远不能手动喊 car.Car(...) |
普通方法要手动调用 |
void 意思是"返回空";构造方法是压根不写返回值这一栏。public void Car() 这行写出来,它就不再是构造方法,只是一个名字叫 Car 的普通方法——这是新手第一大坑,自测题专门考。
调用方式就是 3.2 学的 new:
Car car = new Car("宝马", 200000); // 参数进构造方法,出生即满配
这一行其实是三步(上面那张图):堆里开格子 → 构造方法自动上岗装值 → 地址返回给 car。以前你以为 new 只是一句话,现在知道它肚子里有个"开机向导"在干活。
3. 默认构造方法——免费送的,但会收回
现在有个问题:3.2 和 3.3 里我们从来没写过构造方法,new Car() 照样能跑——为什么?
一个类一个构造方法都没写时,Java 编译器免费送一个看不见的空构造方法:
public Car() { } // 什么也不干,但让你 new Car() 不报错
但是:只要你自己写了任何一个构造方法,免费送的当场收回。类里只剩你写的那一个。
这就是本节最大的坑:
public class Car {
public Car(String b, double p) { ... } // 只写了带参的
}
// 测试类里:
Car c1 = new Car("宝马", 200000); // ✅ 没问题
Car c2 = new Car(); // ❌ 编译报错!
java: 无法将类 Car中的构造器 Car应用到给定类型
(翻译成人话:你喊的"空白出生"这种开法不存在了)
解法(上面那张图右边):两种都自己写——
public class Car {
public Car() { // 无参构造:空白出生
}
public Car(String brand, double price) { // 带参构造:出生带设定
this.brand = brand;
this.price = price;
}
}
参数不同、方法名相同——这正是方法重载的思想(同名积木按原料自动匹配)。以后写类,无参 + 带参成对写,两种开法都留着,谁也坑不了谁。
可以。它也是正经构造方法,new 时一样自动执行。常见用法:给属性装个兜底默认值(比如 brand = "未知品牌";)——保证 new 出来的车再空白,也不是 null。
4. 构造方法 × 封装——安检不能绕过去
3.3 的 setter 安检,构造方法能绕过吗?看这段:
public Car(double p) {
price = p; // 直接装值,安检呢?!
}
构造方法是从类内部直接给属性赋值——private 锁的是"类外用点号摸属性",管不了类自己。所以构造方法里的赋值必须自己带安检:
public Car(double p) {
if (p >= 0) {
price = p;
} else {
System.out.println("价格不能为负,按 0 出厂");
price = 0;
}
}
两种思路:
- 构造里重复写安检——直观,但规则写了两遍,改一处忘一处就出 bug
- 构造方法调用 setter(更地道)——
this.setPrice(p);,安检只在 setter 一处,构造方法当个转发门
3.3 说过"private 了外面还能 new,锁了个寂寞"。现在补全:private 管"出生后的属性访问"(getter/setter),构造方法管"出生瞬间的状态"——两道闸门配合,对象从头到尾都别想脏。
5. IDEA 快捷生成——构造方法也不用手写
- 在类里按 Alt + Insert(或右键 → Generate)
- 选 Constructor
- 弹窗里按住 Ctrl 点选要进构造的属性(不选直接 OK = 无参构造;全选 = 全参构造)
- 回车生成——注意它生成的字段赋值自动带 this,正好复习 3.3
6. 动手部分:给封装版 Car 加上出生设定
第 1 步:打开 3.3 的 Car 类(或新建 Car2),先按 Alt + Insert 生成无参 + 全参两个构造方法。
第 2 步:把全参构造改成"带安检"版(价格安检转发给 setter):
public class Car {
private String brand;
private double price;
public Car() {
this.brand = "未知品牌"; // 无参构造:装个兜底值
}
public Car(String brand, double price) {
this.brand = brand;
this.setPrice(price); // 走 setter,安检只在门里写一遍
}
public void setPrice(double price) {
if (price >= 0) {
this.price = price;
} else {
System.out.println("价格不能为负,赋值失败");
}
}
public double getPrice() {
return price;
}
public String getBrand() {
return brand;
}
public void run() {
System.out.println(brand + " 在跑,价格 " + price);
}
}
第 3 步:新建测试类 TestCar2:
public class TestCar2 {
public static void main(String[] args) {
Car c1 = new Car(); // 空白出生 → 兜底值
c1.run(); // 未知品牌 在跑,价格 0.0
Car c2 = new Car("宝马", 200000); // 出生带设定
c2.run(); // 宝马 在跑,价格 200000.0
Car c3 = new Car("废铁", -100); // 出生也过安检
c3.run(); // 提示 + 废铁 在跑,价格 0.0
}
}
看到什么算成功:控制台三行——"未知品牌 在跑,价格 0.0"、"宝马 在跑,价格 200000.0"、"价格不能为负,赋值失败" + "废铁 在跑,价格 0.0"。第三行证明出生那一瞬也在过安检。
三个 30 秒实验:
- 删掉无参构造,把
new Car()那行留着——编译报错,亲眼看看"免费送的收回"长什么样(这个报错以后一定自己撞,先看一次) - 在构造方法返回值位置加个
void,再 new 一次——报错变了,说明它已经不是构造方法 - 无参构造里打印一句
System.out.println("一辆车出生了");——new 三次打印三句,每 new 一次构造方法就跑一次,现场证明"自动执行"
7. 常见误区
写了 void 它就不是构造方法了,只是个撞名的普通方法。构造方法的"没有返回值"是整栏不写,不是写 void。特征②里专门划过重点。
不能。免费送的默认构造只在你一个都没写时存在。写了任何一个,它就消失——所以"无参 + 带参成对写"是铁纪律。
不行。它只能被 new 触发,写 car.Car("宝马", 200000); 是编译错误。想改属性,走 setter——构造方法管出生,setter 管售后,分工不同。
不能 return 值(连类型都没写),但可以提前结束:return;(不带值)合法,用于参数不合法时直接放弃后续赋值逻辑。
反了——构造方法绕过了 setter(除非你主动调 this.setXxx(...))。从类内部直接 this.price = p; 是不经过任何门的。所以出生时的安检要你在构造里自己安排,见第 4 节。
8. 自测题
①名字必须是类名(普通方法随意);②没有返回值类型,连 void 都不写(普通方法必须有);③new 创建对象时自动执行、不能手动调用(普通方法手动喊)。写反任何一个都编译报错或行为异常。
编译报错"无法将构造器应用到给定类型"。因为 Java 只在一个构造方法都没写时才赠送默认无参构造;你写了带参的,赠送的就被收回,"空白出生"这种开法不存在了。解法:自己补一个无参构造。
在 new 的瞬间自动执行,new 一次执行一次。它负责把参数装进新对象的属性,执行完把堆里对象的地址返回给变量 car。每 new 一次都是"开格子 → 构造 → 交地址"完整一轮。
两种:①构造方法里直接写 if 安检(规则写两遍,易漏);②构造方法里调 this.setPrice(price);,安检只在 setter 写一遍(推荐)。因为构造方法的内部赋值绕过了 private 门禁,安检必须自己安排,而转发给 setter 是最省心的安排法。
不是。加了 void 它就是"一个碰巧叫 Car 的普通方法",不参与 new。后果:这个类里实际一个构造方法都没写,Java 会送默认无参构造——程序碰巧能跑,但那不是你写的 Car()。这种"看起来是、其实不是"最迷惑,特征②(返回值整栏不写)必须记死。
9. 下一步学什么
- ☑
3.4 构造方法✅ 对象从此"出生即满配",脏数据连门都进不来 - ☑
3.5 this关键字✅ 已完成——this(...)构造互调正式登场:无参构造当"转发前台",赋值逻辑收拢到全参构造一处,详见 3.5 this关键字
相关笔记
- 3.3 封装性 ← 上一篇:private 门禁,本节补上"出生瞬间"这道闸
- 3.2 类与对象 ← new 的三步(开格子/装值/交地址)本节拆开了讲
- 2.6 方法 ← 构造方法是特殊的方法,普通方法的规矩全适用
- 2.2 Java中的变量 ← 属性默认值(null/0.0)的本源
- 📇 Java基础 · 索引
- 🏠 返回主页