Java 构造器(Constructor)完全指南

🏗️ Java构造器(Constructor)深度解析

📚 构造器基本概念

什么是构造器?

构造器是创建对象时调用的特殊方法,用于初始化对象的状态

核心特性

  • 方法名必须与类名完全相同
  • 没有返回值类型(连void都不能写)
  • 不能被staticfinalabstract修饰
  • 不能被子类继承(但可以被子类调用)

构造器的注意事项

  • 类默认自带了一个无参构造器
  • 如果为类定义了有参构造器,类默认的无参构造器就没有了,此时如果还想用无参构造器,就不许自己手动写一个无参构造器

📝 构造器语法格式

基本语法

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关键字调用 通过对象或类名调用
继承 不能被子类继承 可以被子类继承和重写
目的 初始化对象 执行操作、返回结果
修饰符 不能是abstractstaticfinal 可以是这些修饰符

🎓 总结要点

  1. 构造器作用:初始化对象,为对象分配内存并设置初始值
  2. 默认构造器:如果没有显式定义构造器,编译器会自动生成无参构造器
  3. 一旦定义构造器:如果定义了有参构造器,默认的无参构造器就不会自动生成
  4. this()和super():必须在构造器的第一行,且不能同时存在
  5. 执行顺序
    • 父类静态代码块 → 子类静态代码块
    • 父类实例代码块 → 父类构造器
    • 子类实例代码块 → 子类构造器
  6. 良好实践
    • 进行参数验证
    • 保持构造器简洁
    • 使用构造器链减少重复代码
    • 考虑使用Builder模式处理多个参数

记住:构造器决定了对象如何诞生,设计良好的构造器是创建健壮对象的基础!

相关推荐
啊哈灵机一动1 小时前
玩转 ESP32-S3 N16R8:PlatformIO 配置 PSRAM 并验证使用
后端
悟空码字1 小时前
Kubernetes实战:你的分布式系统“保姆”养成记
java·后端·kubernetes
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 哈希表(Hash Table)高效的数据结构
java·数据结构·后端·算法·架构
x***38161 小时前
比较Spring AOP和AspectJ
java·后端·spring
v***5651 小时前
【wiki知识库】07.用户管理后端SpringBoot部分
spring boot·后端·状态模式
3***C7441 小时前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
Zfox_1 小时前
【Go】结构体、自定义类型与接口
开发语言·后端·golang
星释1 小时前
Rust 练习册 101:字符串序列切片的艺术
开发语言·后端·rust
r***R2891 小时前
Spring Boot3.3.X整合Mybatis-Plus
spring boot·后端·mybatis