Java 零基础教程

Java 构造方法(Constructors)

构造方法(Constructors)是 Java 中一种特殊的方法,在面向对象编程中扮演着重要的角色。它们负责创建类的对象并初始化对象的状态。理解构造方法是高效使用 Java 类和对象的基础。

本章将深入探讨构造方法的细节、种类,以及如何正确使用它们来创建和初始化对象。

1. 理解构造方法

构造方法是在创建类对象时被自动调用的一种特殊方法。它的名称必须与类名完全相同,并且没有返回类型(连 void 都不能写)。构造方法的主要目的是初始化对象的状态,即为对象的属性(字段)设置初始值。

1.1 构造方法的核心特征

  • 名称一致:构造方法的名称必须与其所属的类名完全相同。
  • 无返回类型:构造方法没有任何返回类型,包括 void
  • 自动调用:当使用 new 关键字创建对象时,构造方法会被自动触发。
  • 专职初始化:它的主要职责是为对象的字段赋予初始值。
  • 支持多重定义:一个类可以拥有多个参数列表不同的构造方法(即构造方法重载)。

2. 构造方法的类型

Java 中的构造方法主要分为两种类型:

  1. 默认构造方法(无参构造方法):不接收任何参数的构造方法。如果你没有在类中定义任何构造方法,Java 会自动为你提供一个默认构造方法。但是,如果你手动定义了任何带有参数的构造方法,Java 就不再自动提供默认构造方法了。
  2. 参数化构造方法:接收一个或多个参数的构造方法。这些参数用于在创建对象时为其字段赋予特定的值。

2.1 默认构造方法(无参构造方法)

默认构造方法不接收任何参数。它会将对象的字段初始化为默认值(例如:int 为 0,对象引用为 nullbooleanfalse)。

默认构造方法示例:

class Dog {
    String name;
    int age;

    // 默认构造方法
    public Dog() {
        name = "未知";
        age = 0;
        System.out.println("调用了默认构造方法");
    }

    public void displayDetails() {
        System.out.println("名字: " + name);
        System.out.println("年龄: " + age);
    }

    public static void main(String[] args) {
        Dog myDog = new Dog(); // 调用默认构造方法
        myDog.displayDetails();
    }
}

在这个例子中:

  • Dog 类拥有一个默认构造方法 Dog()
  • 在构造方法内部,name 被初始化为 "未知",age 被初始化为 0。
  • 当执行 new Dog() 时,默认构造方法开始运行,对象随之被初始化。

2.2 何时会自动提供默认构造方法?

只有当你没有在类中定义任何构造方法时,Java 编译器才会默默地为你提供一个默认构造方法。如果你定义了哪怕一个构造方法(带参数的),且你依然需要一个无参构造方法,你就必须显式地把无参构造方法写出来。

未显式定义构造方法的示例:

class Cat {
    String name;
    int age;

    public void displayDetails() {
        System.out.println("名字: " + name);
        System.out.println("年龄: " + age);
    }

    public static void main(String[] args) {
        Cat myCat = new Cat(); // 调用隐式的默认构造方法
        myCat.displayDetails(); // 输出: 名字: null, 年龄: 0
    }
}

在这种情况下,因为没有显式定义任何构造方法,Java 提供了一个隐式的默认构造方法,将 name 初始化为 null,将 age 初始化为 0

2.3 参数化构造方法

参数化构造方法接收一个或多个参数。它允许你在创建对象的同时,用传入的具体数值来初始化对象的字段。

参数化构造方法示例:

class Car {
    String model;
    String color;
    int year;

    // 参数化构造方法
    public Car(String modelName, String carColor, int modelYear) {
        model = modelName;
        color = carColor;
        year = modelYear;
        System.out.println("调用了参数化构造方法");
    }

    public void displayDetails() {
        System.out.println("型号: " + model);
        System.out.println("颜色: " + color);
        System.out.println("年份: " + year);
    }

    public static void main(String[] args) {
        Car myCar = new Car("特斯拉 Model 3", "红色", 2023); // 调用参数化构造方法
        myCar.displayDetails();
    }
}

在这个例子中:

  • Car 类有一个参数化构造方法 Car(String modelName, String carColor, int modelYear)
  • 构造方法接收三个参数。
  • 当执行 new Car("特斯拉 Model 3", "红色", 2023) 时,参数被传入并在构造方法内部赋值给对象的 modelcoloryear 字段。

3. 构造方法重载 (Constructor Overloading)

一个类可以拥有多个参数列表不同的构造方法,这就是构造方法重载。它为你提供了多种创建对象的方式,使对象的初始化更加灵活。

构造方法重载示例:

class Book {
    String title;
    String author;
    int publicationYear;

    // 默认构造方法
    public Book() {
        title = "未知";
        author = "未知";
        publicationYear = 0;
        System.out.println("调用了默认构造方法");
    }

    // 带有书名和作者的参数化构造方法
    public Book(String bookTitle, String bookAuthor) {
        title = bookTitle;
        author = bookAuthor;
        publicationYear = 0;
        System.out.println("调用了带有(书名, 作者)的参数化构造方法");
    }

    // 带有书名、作者和出版年份的参数化构造方法
    public Book(String bookTitle, String bookAuthor, int year) {
        title = bookTitle;
        author = bookAuthor;
        publicationYear = year;
        System.out.println("调用了带有(书名, 作者, 年份)的参数化构造方法");
    }

    public void displayDetails() {
        System.out.println("书名: " + title);
        System.out.println("作者: " + author);
        System.out.println("出版年份: " + publicationYear);
    }

    public static void main(String[] args) {
        Book book1 = new Book(); // 调用默认构造方法
        book1.displayDetails();

        Book book2 = new Book("指环王", "J.R.R. 托尔金"); // 调用带有书名和作者的构造方法
        book2.displayDetails();

        Book book3 = new Book("傲慢与偏见", "简·奥斯汀", 1813); // 调用带有书名、作者和年份的构造方法
        book3.displayDetails();
    }
}

取决于你创建 Book 对象时传入的参数,Java 会智能匹配并调用对应的构造方法。

4. 构造方法中的 this 关键字

this 关键字代表当前对象的引用。在构造方法中,它经常被用来区分对象的字段(实例变量)和构造方法的参数,特别是当它们名字相同的时候。

使用 this 关键字的示例:

class Laptop {
    String model;
    String brand;
    int ram;

    // 使用 'this' 关键字的参数化构造方法
    public Laptop(String model, String brand, int ram) {
        this.model = model;
        this.brand = brand;
        this.ram = ram;
        System.out.println("调用了带有 'this' 的参数化构造方法");
    }

    public void displayDetails() {
        System.out.println("型号: " + model);
        System.out.println("品牌: " + brand);
        System.out.println("内存: " + ram + " GB");
    }

    public static void main(String[] args) {
        Laptop myLaptop = new Laptop("MacBook Pro", "Apple", 16); // 调用参数化构造方法
        myLaptop.displayDetails();
    }
}

4.1 为什么要使用 this?

  • 清晰易读:当参数名和字段名完全一致时,代码的意图非常直观。
  • 解决作用域冲突:明确告诉编译器,this.model 指的是对象身上的属性,而等号右边的 model 是传进来的局部变量。

5. 构造方法链 (Constructor Chaining)

构造方法链是指在一个构造方法中调用同一个类里的另一个构造方法。这通过使用 this() 关键字来实现。它的核心优势在于代码复用,确保无论使用哪个构造方法创建对象,都能执行一套公共的初始化逻辑。

构造方法链示例:

class Phone {
    String model;
    String brand;
    int storage;

    // 构造方法 1: 默认构造方法
    public Phone() {
        this("未知", "未知", 0); // 调用构造方法 3
        System.out.println("调用了默认构造方法");
    }

    // 构造方法 2: 带有型号和品牌的参数化构造方法
    public Phone(String model, String brand) {
        this(model, brand, 0); // 调用构造方法 3
        System.out.println("调用了带有(型号, 品牌)的参数化构造方法");
    }

    // 构造方法 3: 带有型号、品牌和存储容量的参数化构造方法
    public Phone(String model, String brand, int storage) {
        this.model = model;
        this.brand = brand;
        this.storage = storage;
        System.out.println("调用了带有(型号, 品牌, 容量)的参数化构造方法");
    }

    public void displayDetails() {
        System.out.println("型号: " + model);
        System.out.println("品牌: " + brand);
        System.out.println("存储容量: " + storage + " GB");
    }

    public static void main(String[] args) {
        Phone phone1 = new Phone(); // 触发链:构造方法 1 -> 构造方法 3
        phone1.displayDetails();

        Phone phone2 = new Phone("iPhone 13", "Apple"); // 触发链:构造方法 2 -> 构造方法 3
        phone2.displayDetails();

        Phone phone3 = new Phone("Galaxy S21", "Samsung", 128); // 直接调用构造方法 3
        phone3.displayDetails();
    }
}

在这个例子中,所有其他构造方法最终都会将初始化工作委派给“参数最全”的那个构造方法(构造方法 3),从而保证了逻辑的统一。

5.1 构造方法链的规则

  1. 必须使用 this(...) 语法来调用。
  2. this(...) 的调用必须是构造方法中的第一行有效代码
  3. 一个构造方法只能调用同一个类中的另一个构造方法。
  4. 严禁递归调用(例如:构造方法 A 调用 B,B 又调用 A,这会导致死循环编译报错)。

6. 综合实战演练

让我们看一个更贴近实际开发的复杂例子,模拟一个学生信息管理系统:

class Student {
    String name;
    int studentId;
    String major;
    double gpa;

    // 默认构造方法
    public Student() {
        this("未知", 0, "未定", 0.0);
        System.out.println("调用了默认 Student 构造方法");
    }

    // 带有姓名和学号的参数化构造方法
    public Student(String name, int studentId) {
        this(name, studentId, "未定", 0.0);
        System.out.println("调用了带有(姓名, 学号)的 Student 构造方法");
    }

    // 带有姓名、学号和专业的参数化构造方法
    public Student(String name, int studentId, String major) {
        this(name, studentId, major, 0.0);
        System.out.println("调用了带有(姓名, 学号, 专业)的 Student 构造方法");
    }

    // 包含所有属性的参数化构造方法(核心初始化器)
    public Student(String name, int studentId, String major, double gpa) {
        this.name = name;
        this.studentId = studentId;
        this.major = major;
        this.gpa = gpa;
        System.out.println("调用了带有(所有属性)的 Student 构造方法");
    }

    public void displayDetails() {
        System.out.println("姓名: " + name);
        System.out.println("学号: " + studentId);
        System.out.println("专业: " + major);
        System.out.println("GPA: " + gpa);
    }

    public static void main(String[] args) {
        Student student1 = new Student();
        student1.displayDetails();

        Student student2 = new Student("爱丽丝·史密斯", 12345);
        student2.displayDetails();

        Student student3 = new Student("鲍勃·约翰逊", 67890, "计算机科学");
        student3.displayDetails();

        Student student4 = new Student("查理·布朗", 13579, "工程学", 3.75);
        student4.displayDetails();
    }
}