Mr.Mou @ ShiShi AP Center

AP CSA 3.4:编写构造器

在 3.3 中,我们已经认识了一个类的基本结构:实例变量(instance variables)保存数据,构造器(constructor)负责初始化对象,方法(methods)定义对象的行为。

这一课开始真正自己写构造器。

构造器最重要的任务,是在对象创建时给实例变量设置合适的初始值,让对象从一开始就处于可用的状态。

核心知识点

1. 第一个 AP 核心考法

下面的 Cat 类需要用构造器接收猫的颜色 c 和是否饥饿 h

public class Cat
{
    private String color;
    private boolean isHungry;

    public Cat(String c, boolean h)
    {
        /* missing code */
    }
}

哪一段代码可以正确替换 missing code

A

c = color;
h = isHungry;

B

color = c;
isHungry = h;

C

c = "black";
h = true;

答案:B

构造器参数 ch 接收外部传进来的值,再把这些值存入对象自己的实例变量:

传进来的参数 → 对象的实例变量

c → color
h → isHungry

所以最典型的构造器代码就是:

instanceVariable = parameter;

这会是 3.4 最常见的 AP 考法之一。

2. 构造器长什么样?

继续使用上一课的 Pet 类:

public class Pet
{
    private String name;
    private int energy;

    public Pet(String n, int e)
    {
        name = n;
        energy = e;
    }
}

构造器是这一部分:

public Pet(String n, int e)
{
    name = n;
    energy = e;
}

构造器的 signature 有几个非常重要的特征:

特征 Pet 中的例子
通常使用 public public Pet(...)
名字和类名完全相同 类叫 Pet,构造器也叫 Pet
没有返回类型 前面没有 voidintString
可以有参数 String n, int e

特别注意:

public void Pet(String n, int e)

不是构造器。

因为写了 void 之后,它就变成了一个名字叫 Pet 的普通方法。

可以先把构造器的模板记成:

public ClassName(parameters)
{
    // initialize instance variables
}

3. 构造器到底做什么?

构造器的核心任务是:

给一个刚创建的对象设置初始状态(initial state)。

例如:

Pet p1 = new Pet("Milo", 80);
Pet p2 = new Pet("Luna", 50);

两次 new Pet(...) 都会执行 Pet 的构造器,但传进去的数据不同。

执行完之后,可以理解成:

对象 name energy
p1 "Milo" 80
p2 "Luna" 50

也就是说,构造器不是只运行一次。

每创建一个新的对象,就会执行一次与参数匹配的构造器。

构造器还应该让对象进入一个合理、可用的状态(valid state)。

例如,如果一个 Fraction 类用 denominator 保存分母,那么:

private int denominator;

如果让它保持默认值 0,就会得到一个无效的分数。因此一个无参构造器可能会选择:

public Fraction()
{
    numerator = 1;
    denominator = 1;
}

重点不是一定要用 1,而是:

构造器应该把对象初始化成合理的状态,而不是机械地给变量随便一个值。

4. 参数怎样进入对象?

看这一行:

Pet p = new Pet("Milo", 80);

调用构造器时:

"Milo" → n
80       → e

然后构造器执行:

name = n;
energy = e;

数据继续流向实例变量:

"Milo" → n → name
80       → e → energy

因此可以把整个过程理解成:

arguments
   ↓
constructor parameters
   ↓
instance variables

左右两边不要看反

name = n;

意思是:

n 当前保存的值,赋给实例变量 name

而下面这样通常不是你想要的:

n = name;

它把方向写反了,并没有用参数初始化 name

这和 Unit 1 学过的普通赋值完全一样:

左边 ← 右边

5. new 的参数必须和构造器匹配

假设类中有:

public Pet(String n, int e)
{
    name = n;
    energy = e;
}

那么下面是合法的:

Pet p = new Pet("Milo", 80);

因为参数列表匹配:

constructor: Pet(String, int)
call:        Pet(String, int)

下面这些则不能匹配这个构造器:

new Pet("Milo");       // 少一个参数
new Pet(80, "Milo");   // 顺序 / 类型不对
new Pet();              // 没有对应的无参构造器

所以看 constructor call 时,检查三件事:

参数个数(number)
参数类型(type)
参数顺序(order)

这和 Unit 1 中调用别人写好的 constructor 时的规则完全相同;3.4 只是把视角反过来——现在是你自己负责写出那个构造器。

6. Java 自动提供的 default constructor

这是这一课最容易混淆的地方之一。

如果一个类完全没有写任何构造器

public class Pet
{
    private String name;
    private int energy;
}

Java 会自动提供一个默认无参构造器(default no-argument constructor)

因此下面可以创建对象:

Pet p = new Pet();

实例变量会得到各自类型的默认值:

类型 默认值
int 0
double 0.0
boolean false
引用类型,例如 String null

所以这个 Pet 对象一开始相当于:

name   → null
energy → 0

但是:只要自己写了一个构造器……

例如:

public Pet(String n, int e)
{
    name = n;
    energy = e;
}

Java 就不会再自动提供

Pet()

因此:

Pet p1 = new Pet("Milo", 80);   // OK
Pet p2 = new Pet();             // error

除非你自己再写一个无参构造器:

public Pet()
{
    name = "Unknown";
    energy = 100;
}

这里要区分两个词:

7. 一个类可以有多个构造器

同一个类可以提供不同的初始化方式。

例如:

public class Pet
{
    private String name;
    private int energy;

    public Pet()
    {
        name = "Unknown";
        energy = 100;
    }

    public Pet(String n, int e)
    {
        name = n;
        energy = e;
    }
}

于是两种创建方式都合法:

Pet p1 = new Pet();
Pet p2 = new Pet("Milo", 80);

这叫构造器重载(constructor overloading)。

学生在 Unit 1 已经见过 overloaded constructors;这里重点只是从“调用它们”转为“自己写它们”。

多个构造器的名字都一样,所以 Java 通过 signature 区分它们。参数列表需要在数量、类型或顺序上有所不同。

8. 常见初学者错误

错误 错误代码 问题 正确写法 / 理解
给构造器写返回类型 public void Pet(...) 这变成普通方法,不是 constructor public Pet(...)
构造器名称和类名不同 public Animal(...) 写在 Pet constructor 必须和 class 同名 public Pet(...)
赋值方向写反 n = name; 没有把参数保存到实例变量 name = n;
参数自己赋给自己 n = n; 只改变 / 保留参数本身,对实例变量没有作用 name = n;
写了一个 constructor 后还以为 new Pet() 自动存在 new Pet() Java 已经不会自动提供 default constructor 自己写 Pet(),或调用已有 constructor
调用时参数不匹配 new Pet(80, "Milo") 参数类型和顺序不符合 signature new Pet("Milo", 80)
以为 constructor 必须 return 一个对象 return p; constructor 没有返回类型,也不写返回对象 只负责初始化实例变量

9. 小练习(Mini Practice)

Practice 1:哪一个是真正的 constructor?

假设类名是:

public class Book

下面哪一个是合法的构造器?

A

public void Book(String t)

B

public Book(String t)

C

public String Book(String t)

答案:B

构造器和类同名,而且没有返回类型


Practice 2:补全构造器

public class Student
{
    private String name;
    private int grade;

    public Student(String n, int g)
    {
        /* missing code */
    }
}

应该怎样补全?

答案:

name = n;
grade = g;

构造器参数把创建对象时传入的数据交给对象自己的实例变量。


Practice 3:有没有自动的 default constructor?

public class Car
{
    private String model;
    private int year;

    public Car(String m, int y)
    {
        model = m;
        year = y;
    }
}

下面哪一个 constructor call 合法?

A. new Car()
B. new Car("Honda", 2026)
C. 两个都合法

答案:B

因为类已经自己写了 Car(String, int),Java 不会再自动提供 Car()


Practice 4:预测两个对象的数据

Pet a = new Pet("Milo", 80);
Pet b = new Pet("Luna", 50);

构造器是:

public Pet(String n, int e)
{
    name = n;
    energy = e;
}

创建完成后,下面哪一个正确?

A. ab 都保存最后一次传入的数据
B. a 保存 "Milo", 80b 保存 "Luna", 50
C. nameenergy 只是参数,不会保存到对象中

答案:B

每个对象都有自己的一份实例变量;每次调用构造器时,参数只用来初始化当前正在创建的那个对象。

Unit 3.4 核心词汇(Vocabulary)

Vocabulary 中文理解 核心理解 / Example
constructor / 构造器 初始化新对象的特殊代码 public Pet(String n, int e)
constructor signature / 构造器签名 构造器名称及参数信息 用于判断调用哪个 constructor
parameter / 参数 构造器定义中接收数据的变量 String n, int e
argument / 实参 调用构造器时真正传入的值 "Milo", 80
initialize / 初始化 给对象的数据设置初始值 name = n;
initial state / 初始状态 对象刚创建完成时保存的数据状态 name = "Milo", energy = 80
valid state / 有效状态 对象的数据处于合理、可使用的状态 分母不能为 0
no-argument constructor / 无参构造器 参数列表为空的构造器 public Pet()
default constructor / 默认构造器 没有写任何 constructor 时 Java 自动提供的无参构造器 实例变量使用类型默认值
constructor overloading / 构造器重载 同一个类中定义多个不同 signature 的构造器 Pet()Pet(String, int)