🏗️ Java构造器(Constructor)深度解析
📚 构造器基本概念
什么是构造器?
构造器是创建对象时调用的特殊方法,用于初始化对象的状态。
核心特性
- 方法名必须与类名完全相同
- 没有返回值类型(连void都不能写)
- 不能被
static、final、abstract修饰 - 不能被子类继承(但可以被子类调用)
构造器的注意事项
- 类默认自带了一个无参构造器
- 如果为类定义了有参构造器,类默认的无参构造器就没有了,此时如果还想用无参构造器,就不许自己手动写一个无参构造器
📝 构造器语法格式
基本语法
java
[访问修饰符] 类名(参数列表) {
// 初始化代码
}
🎯 构造器的分类
1. 默认构造器(无参构造器)
java
public class Person {
String name;
int age;
// 默认构造器(无参)
public Person() {
this.name = "未知";
this.age = 0;
System.out.println("Person默认构造器被调用");
}
public void display() {
System.out.println("姓名: " + name + ", 年龄: " + age);
}
public static void main(String[] args) {
Person p = new Person(); // 调用无参构造器
p.display(); // 输出: 姓名: 未知, 年龄: 0
}
}
2. 有参构造器
java
public class Student {
private String id;
private String name;
private int score;
// 有参构造器
public Student(String id, String name, int score) {
this.id = id;
this.name = name;
this.score = score;
System.out.println("创建学生: " + name);
}
public static void main(String[] args) {
// 使用有参构造器创建对象
Student s1 = new Student("001", "张三", 90);
Student s2 = new Student("002", "李四", 85);
}
}
3. 构造器重载(Overloading)
java
public class Book {
private String title;
private String author;
private double price;
// 构造器1:只有书名
public Book(String title) {
this(title, "未知作者", 0.0);
}
// 构造器2:书名和作者
public Book(String title, String author) {
this(title, author, 0.0);
}
// 构造器3:所有参数
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
this.price = price;
}
public void showInfo() {
System.out.println("《" + title + "》 - " + author + " - ¥" + price);
}
public static void main(String[] args) {
Book b1 = new Book("Java编程思想");
Book b2 = new Book("Effective Java", "Joshua Bloch");
Book b3 = new Book("深入理解Java虚拟机", "周志明", 89.0);
b1.showInfo();
b2.showInfo();
b3.showInfo();
}
}
🔄 构造器的特殊调用
1. this() - 调用本类其他构造器
java
public class Rectangle {
private int width;
private int height;
private String color;
// 构造器1:只有宽高
public Rectangle(int width, int height) {
this(width, height, "黑色");
}
// 构造器2:全部参数
public Rectangle(int width, int height, String color) {
this.width = width;
this.height = height;
this.color = color;
}
// ❌ 错误:this()必须是第一条语句
/*
public Rectangle() {
System.out.println("初始化..."); // 错误!
this(10, 10); // 必须放在第一行
}
*/
// ✅ 正确:this()在第一行
public Rectangle() {
this(10, 10); // 正确:调用双参构造器
System.out.println("使用默认尺寸");
}
}
2. super() - 调用父类构造器
java
class Animal {
private String name;
public Animal(String name) {
this.name = name;
System.out.println("Animal构造器: " + name);
}
}
class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 必须放在第一行,调用父类构造器
this.breed = breed;
System.out.println("Dog构造器: " + breed);
}
// ❌ 错误:如果没有显式调用super()或this()
// 编译器会自动添加super(),但如果父类没有无参构造器就会报错
/*
public Dog() {
// 这里会自动添加 super();
// 如果父类Animal没有无参构造器,就会编译错误
}
*/
}
public class SuperConstructorDemo {
public static void main(String[] args) {
Dog dog = new Dog("旺财", "金毛");
}
}
⚙️ 构造器的执行顺序
java
class Parent {
{
System.out.println("1. Parent实例初始化块");
}
static {
System.out.println("0. Parent静态初始化块(类加载时执行一次)");
}
public Parent() {
System.out.println("2. Parent构造器");
}
}
class Child extends Parent {
{
System.out.println("3. Child实例初始化块");
}
static {
System.out.println("0.5 Child静态初始化块(类加载时执行一次)");
}
public Child() {
super(); // 隐式调用(可以省略)
System.out.println("4. Child构造器");
}
}
public class ExecutionSequence {
public static void main(String[] args) {
System.out.println("=== 第一次创建对象 ===");
Child c1 = new Child();
System.out.println("\n=== 第二次创建对象 ===");
Child c2 = new Child();
/*
输出结果:
=== 第一次创建对象 ===
0. Parent静态初始化块(类加载时执行一次)
0.5 Child静态初始化块(类加载时执行一次)
1. Parent实例初始化块
2. Parent构造器
3. Child实例初始化块
4. Child构造器
=== 第二次创建对象 ===
1. Parent实例初始化块
2. Parent构造器
3. Child实例初始化块
4. Child构造器
*/
}
}
🛡️ 构造器的访问控制
java
public class AccessControl {
// 1. public - 任何地方都可以访问
public AccessControl() {
System.out.println("Public构造器");
}
// 2. protected - 同包或子类可以访问
protected AccessControl(int a) {
System.out.println("Protected构造器");
}
// 3. 默认(包访问权限)- 同包可以访问
AccessControl(String s) {
System.out.println("默认构造器");
}
// 4. private - 仅本类可以访问(常用于单例模式)
private AccessControl(boolean b) {
System.out.println("Private构造器");
}
// 提供静态方法访问私有构造器
public static AccessControl getInstance() {
return new AccessControl(true);
}
}
// 测试类
class TestAccess {
public static void main(String[] args) {
new AccessControl(); // OK - public
new AccessControl(1); // OK - protected(同包)
new AccessControl("test"); // OK - 默认(同包)
// new AccessControl(true); // ❌ 错误 - private
AccessControl.getInstance(); // OK - 通过静态方法
}
}
🎭 特殊类型的构造器
1. 私有构造器(单例模式)
java
public class Singleton {
// 唯一实例
private static Singleton instance;
// 私有构造器,防止外部创建实例
private Singleton() {
System.out.println("Singleton实例被创建");
}
// 提供全局访问点
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void showMessage() {
System.out.println("Hello Singleton!");
}
public static void main(String[] args) {
// ❌ 不能直接new
// Singleton s = new Singleton();
// ✅ 只能通过getInstance获取
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
s1.showMessage();
System.out.println("s1 == s2: " + (s1 == s2)); // true
}
}
2. 拷贝构造器
java
public class Employee implements Cloneable {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
// 拷贝构造器
public Employee(Employee other) {
this.name = other.name;
this.age = other.age;
this.salary = other.salary;
}
// 深拷贝方法
public Employee deepCopy() {
return new Employee(this.name, this.age, this.salary);
}
public void display() {
System.out.printf("姓名: %s, 年龄: %d, 薪资: %.2f%n", name, age, salary);
}
public void raiseSalary(double percentage) {
this.salary *= (1 + percentage / 100);
}
public static void main(String[] args) {
Employee emp1 = new Employee("张三", 30, 10000);
Employee emp2 = new Employee(emp1); // 使用拷贝构造器
emp1.raiseSalary(10); // 给emp1涨薪10%
emp1.display(); // 薪资: 11000.00
emp2.display(); // 薪资: 10000.00(不受影响)
}
}
3. 构造器链
java
public class Product {
private String id;
private String name;
private String category;
private double price;
private int stock;
// 最基础的构造器
private Product(String id, String name, String category,
double price, int stock) {
this.id = id;
this.name = name;
this.category = category;
this.price = price;
this.stock = stock;
}
// 公共构造器1:只有id和name
public Product(String id, String name) {
this(id, name, "未分类", 0.0, 0);
}
// 公共构造器2:id、name、price
public Product(String id, String name, double price) {
this(id, name, "未分类", price, 0);
}
// 公共构造器3:id、name、category、price
public Product(String id, String name, String category, double price) {
this(id, name, category, price, 0);
}
// 显示产品信息
public void showInfo() {
System.out.printf("产品ID: %s, 名称: %s, 分类: %s, 价格: ¥%.2f, 库存: %d%n",
id, name, category, price, stock);
}
public static void main(String[] args) {
Product p1 = new Product("P001", "手机");
Product p2 = new Product("P002", "笔记本电脑", 6999.99);
Product p3 = new Product("P003", "耳机", "电子产品", 299.99);
p1.showInfo();
p2.showInfo();
p3.showInfo();
}
}
⚠️ 常见错误和注意事项
错误1:忘记调用父类构造器
java
class Parent {
private String name;
public Parent(String name) { // 有参构造器
this.name = name;
}
}
class Child extends Parent {
// ❌ 编译错误:没有显式调用super(),编译器会自动添加super()
// 但Parent没有无参构造器,所以会报错
public Child() {
// 这里会自动添加 super(); 导致错误
}
// ✅ 正确:显式调用父类有参构造器
public Child(String name) {
super(name);
}
}
错误2:构造器中有返回值
java
public class ErrorExample {
// ❌ 错误:构造器不能有返回值类型
// public void ErrorExample() { } // 这是一个普通方法,不是构造器!
// ❌ 错误:构造器不能有返回值类型
// public int ErrorExample() { return 1; }
// ✅ 正确:没有返回值类型
public ErrorExample() {
System.out.println("正确的构造器");
}
}
错误3:this()和super()的顺序
java
public class OrderError {
private int value;
public OrderError() {
System.out.println("初始化开始");
// ❌ 错误:this()必须是第一句
// this(10);
// ❌ 错误:super()必须是第一句
// super();
}
public OrderError(int value) {
this(); // ✅ 正确:在第一行
this.value = value;
}
}
💡 构造器最佳实践
实践1:参数验证
java
public class BankAccount {
private final String accountNumber;
private String accountHolder;
private double balance;
public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
// 参数验证
if (accountNumber == null || accountNumber.length() != 16) {
throw new IllegalArgumentException("账号必须是16位数字");
}
if (accountHolder == null || accountHolder.trim().isEmpty()) {
throw new IllegalArgumentException("户主姓名不能为空");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("初始余额不能为负数");
}
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = initialBalance;
}
// 简化构造器,调用主要构造器
public BankAccount(String accountNumber, String accountHolder) {
this(accountNumber, accountHolder, 0.0);
}
}
实践2:Builder模式(当参数很多时)
java
public class Computer {
private final String cpu;
private final String ram;
private final String storage;
private final String gpu;
// 私有构造器,只能通过Builder创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.gpu = builder.gpu;
}
// Builder内部类
public static class Builder {
// 必填参数
private final String cpu;
private final String ram;
// 可选参数(有默认值)
private String storage = "512GB SSD";
private String gpu = "集成显卡";
// Builder构造器接收必填参数
public Builder(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public Builder storage(String storage) {
this.storage = storage;
return this;
}
public Builder gpu(String gpu) {
this.gpu = gpu;
return this;
}
public Computer build() {
return new Computer(this);
}
}
public void showSpec() {
System.out.println("电脑配置:");
System.out.println("CPU: " + cpu);
System.out.println("内存: " + ram);
System.out.println("硬盘: " + storage);
System.out.println("显卡: " + gpu);
}
public static void main(String[] args) {
// 使用Builder模式创建对象
Computer computer = new Computer.Builder("i7-12700K", "32GB")
.storage("1TB NVMe")
.gpu("RTX 3070")
.build();
computer.showSpec();
}
}
实践3:防御性拷贝
java
import java.util.ArrayList;
import java.util.List;
public class Course {
private final String courseName;
private final List<String> students;
public Course(String courseName, List<String> students) {
this.courseName = courseName;
// 防御性拷贝:避免外部修改影响内部数据
this.students = new ArrayList<>(students);
}
// 返回不可修改的列表
public List<String> getStudents() {
return new ArrayList<>(students); // 返回拷贝
}
public static void main(String[] args) {
List<String> studentList = new ArrayList<>();
studentList.add("张三");
studentList.add("李四");
Course course = new Course("Java编程", studentList);
// 修改原始列表
studentList.add("王五");
System.out.println("原始列表: " + studentList);
System.out.println("课程学生: " + course.getStudents());
// 课程中的学生列表不受影响
}
}
📊 构造器与方法对比
| 特性 | 构造器 | 普通方法 |
|---|---|---|
| 名称 | 必须与类名相同 | 可以是任意合法标识符 |
| 返回值 | 没有返回值类型 | 必须有返回值类型(void也算) |
| 调用方式 | 通过new关键字调用 |
通过对象或类名调用 |
| 继承 | 不能被子类继承 | 可以被子类继承和重写 |
| 目的 | 初始化对象 | 执行操作、返回结果 |
| 修饰符 | 不能是abstract、static、final |
可以是这些修饰符 |
🎓 总结要点
- 构造器作用:初始化对象,为对象分配内存并设置初始值
- 默认构造器:如果没有显式定义构造器,编译器会自动生成无参构造器
- 一旦定义构造器:如果定义了有参构造器,默认的无参构造器就不会自动生成
- this()和super():必须在构造器的第一行,且不能同时存在
- 执行顺序 :
- 父类静态代码块 → 子类静态代码块
- 父类实例代码块 → 父类构造器
- 子类实例代码块 → 子类构造器
- 良好实践 :
- 进行参数验证
- 保持构造器简洁
- 使用构造器链减少重复代码
- 考虑使用Builder模式处理多个参数
记住:构造器决定了对象如何诞生,设计良好的构造器是创建健壮对象的基础!