AP Computer Science A · 鼎睿学苑

Unit 3: Class Creation

单元 3:类的定义

Design your own classes with instance variables, constructors, methods, encapsulation, static members, scope, and the this keyword.

自行设计类,掌握实例变量、构造方法、方法、封装、静态成员、作用域以及 this 关键字。

10–18% of AP Exam 占 AP 考试 10–18% ~20–22 Class Periods 约 20–22 课时 9 Topics 9 个小节

Abstraction and Program Design

抽象与程序设计

Abstraction reduces complexity by focusing on the main idea while hiding irrelevant details.

抽象(abstraction)通过聚焦主要思想、隐藏无关细节来降低复杂度。

Data Abstraction, separates what data represents from how it's stored. An instance variable like balance names the concept without exposing implementation.

数据抽象,将数据"代表什么"与"如何存储"分离开来。像 balance 这样的实例变量(instance variable)只为概念命名,而不暴露具体实现。

Procedural Abstraction, gives a name to a process (method). You use a method knowing what it does, not how. This enables code reuse and method decomposition.

过程抽象,为某个过程(方法)赋予名称。你只需知道方法做了什么,而不必关心如何做。这便于代码复用与方法分解。

Design First 先设计 Before coding, plan each class: identify its attributes (data/nouns) and behaviors (methods/verbs). This can be done with natural language, UML diagrams, or pseudocode. 编码前先为每个类(class)做规划:识别其属性(数据/名词)与行为(方法/动词)。可用自然语言、UML 图或伪代码来完成。
Key Vocabulary 关键词汇 An attribute is a variable defined in a class outside any method. An instance variable is unique to each object. A class variable is shared by all objects. 属性是在类中、任何方法之外定义的变量。实例变量每个对象(object)独有一份。类变量由所有对象共享。
Worked Example: Designing a GradeTracker class
例题:设计一个 GradeTracker 类

Problem. Sketch a class that records grades, keeps a running total, and reports the average.

题目。勾画一个记录成绩、维护累计总分并报告平均分的类。

Step 1. Identify nouns (attributes). The tracker has a count and a total.

第 1 步:识别名词(属性)。这个追踪器有一个 count 和一个 total。

Step 2. Identify verbs (behaviors). It can add a grade and calculate the average.

第 2 步:识别动词(行为)。它可以添加一个成绩并计算平均分。

Step 3. Decide what to expose. Public methods form the API. Keep instance variables private unless the specification says otherwise.

第 3 步:决定对外暴露什么。公有方法构成 API。除非题目规范另有说明,否则实例变量应保持 private。

Step 4. Sketch the skeleton.

第 4 步:勾画骨架。

public class GradeTracker {
    private int count;
    private int total;

    public GradeTracker() { count = 0; total = 0; }
    public void add(int grade) { total += grade; count++; }
    public double average() { if (count == 0) return 0.0; return (double) total / count; }
}

Parameter generalization. A method written for one fixed value can be generalized by turning that value into a parameter. A countAbove90() method works for exactly one cutoff; countAbove(int cutoff) does the same job for any cutoff the caller supplies, so one method replaces many near-duplicates.

Changing internals without changing the interface. A class can change its internal representation later while preserving its constructor and method signatures. Here, add(int grade) hides whether grades are stored in a list or only summarized by count and total; callers see the same behavior.

用参数实现通用化。只针对某个固定值编写的方法,可以把该值改成形参来实现通用化。countAbove90() 只适用于一个固定分数线;countAbove(int cutoff) 则能按调用方给出的任意分数线完成同样的工作,于是一个方法就取代了许多近乎重复的方法。

改变内部实现而不改变接口。类可以在不改变构造方法和各方法签名的前提下,改变内部表示。这里 add(int grade) 隐藏了成绩究竟是存入列表,还是只用 count 与 total 做汇总;调用方看到的行为保持不变。

AP Trap: Attribute vs Method AP 陷阱:属性 vs 方法 On AP MCQs, "has-a" relationships indicate attributes (data); "can-do" or "does" indicate methods (behavior). A library has books (attribute); a library checks out books (method). Don't model behaviors as fields. 在 AP 选择题中,"有一个"(has-a)关系表示属性(数据);"能做"或"会做"则表示方法(行为)。图书馆有书(属性);图书馆外借书(方法)。不要把行为建模为字段。

Impact of Program Design

程序设计的影响

Programs impact society, the economy, and culture, both positively and negatively. The current CED and official practice materials include conceptual questions that ask you to recognize these impacts in short scenarios.

程序会对社会、经济和文化产生正面或负面的影响。当前 CED 与官方练习材料包含概念题,要求你在简短情境中识别这些影响。

System Reliability, a program should perform as expected under all stated conditions, not just the happy path. Test with edge cases, invalid input, and boundary values to maximize reliability. A reliability gap is a failure for a stated condition.

系统可靠性,程序应当在所有规定条件下都按预期运行,而不仅仅是顺利路径。要用边界情况、非法输入和临界值来测试以最大化可靠性。可靠性缺口指的是在某个规定条件下出现失败。

Unintended Consequences, a program built for one purpose can cause harm outside its intended scope. A recommendation algorithm optimised for engagement can amplify misinformation; a routing app's optimum may cause traffic chaos on residential streets. The harm was not intended by the designer but emerged from the design.

意料之外的后果,为某种目的设计的程序可能在其预期范围之外造成危害。以"参与度"为目标优化的推荐算法可能放大错误信息;以最优路径为目标的导航 App 可能让住宅街道交通陷入混乱。这种危害并非设计者所期望,而是从设计中浮现出来。

Intellectual Property, AP materials describe responsible reuse: check the license, follow its terms, and give attribution where required. Permissive and copyleft licenses have different conditions, so the safe exam-ready behavior is to treat "no explicit permission" as "do not reuse" unless the problem states otherwise.

知识产权,AP 官方材料强调负责任地复用代码:查看许可证、遵守其条款,并在需要时注明出处。宽松许可证与 copyleft 许可证的条件不同,因此备考时应掌握的安全做法是:除非题目另有说明,否则“没有明确许可”就应视为“不可复用”。

Plagiarism, copying another student's submitted code (even partially, even modified) without attribution is plagiarism, a serious academic-integrity violation. Cite the source of any code you didn't write yourself, including snippets adapted from documentation or online forums.

抄袭,未署名地复制其他同学提交的代码(哪怕只是部分、哪怕做过修改)就是抄袭,属于严重的学术诚信违规。任何并非你本人编写的代码都应标注出处,包括根据文档或在线论坛改编的代码片段。

Telling Unintended Consequence from Reliability 如何区分"意料之外的后果"与"可靠性" A subtle but exam-tested distinction. Reliability = the program fails to do what it was designed to do under some valid input. Unintended consequence = the program does exactly what it was designed to do, but that behavior causes harm in a way the designer did not anticipate. 这是一个细微但考点常考的区别。可靠性 = 程序在某些合法输入下没能完成设计要做的事。意料之外的后果 = 程序恰恰完成了设计要做的事,但这种行为以设计者未预见的方式造成了危害。
A weather-prediction app shows accurate forecasts 99% of the time but on rare extreme-weather days predicts mild conditions. Which best describes the issue?
一款天气预报 App 在 99% 的情况下都能准确预报,但在罕见的极端天气日却预报为温和天气。下列哪一项最贴切地描述了问题?
Intellectual property violation侵犯知识产权
Plagiarism抄袭
Lack of system reliability under edge-case inputs在边界输入下缺乏系统可靠性
Unintended consequence意料之外的后果
Correct! The app fails on a specific class of inputs (extreme weather). It's not behaving as designed under all stated conditions, that's a reliability gap. (Unintended consequence would be: the app works perfectly, but its accurate predictions cause panic-buying that harms grocery supply chains.)
正确!这个 App 在某一类特定输入(极端天气)下失败。它没有在所有规定条件下都按设计运行,这就是可靠性缺口。("意料之外的后果"会是:App 工作完全正常,但其准确预报引发抢购,破坏了食品供应链。)
"Reliability" = performs as expected under all conditions. A 99% success rate with a known failure mode on edge cases is a reliability issue. "Unintended consequence" means the program works as intended but causes side-effect harm.
"可靠性" = 在所有条件下都按预期运行。99% 的成功率、且在边界情形下存在已知失败模式,这属于可靠性问题。"意料之外的后果"指的是程序按设计运行,但引发副作用造成危害。

Anatomy of a Class

类的结构剖析

Encapsulation

封装

Data encapsulation hides implementation details from external classes using access modifiers.

数据封装(encapsulation)利用访问修饰符(access modifier)把实现细节对外部类隐藏起来。

Keyword关键字Who Can Access谁可以访问Used For用途
publicAny class任何类Classes, constructors, methods that form the API类、构造方法(constructor)、构成 API 的方法
privateOnly the declaring class只有声明它的类Instance variables (best practice), helper methods实例变量(最佳实践)、辅助方法
AP CSA Rules AP CSA 规则 In this course, classes and constructors are always public. Instance variables should be private unless the specification says otherwise. Methods may be public or private according to their required access. 在本课程中,类和构造方法总是 public。除非规范另有说明,否则实例变量应使用 private。方法根据所需访问范围使用 public 或 private。
public class Dog {
    // Instance variables, private!// 实例变量,私有!
    private String name;
    private int age;

    // Constructor, public// 构造方法,公有
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Public methods// 公有方法
    public String getName() { return name; }
    public int getAge() { return age; }
}
Worked Example: Translate an FRQ prompt to a class skeleton
例题:把 FRQ 题干翻译成类骨架

Problem. "Write a class Student with a name (String), grade (int 9–12), and methods to access each."

题目。"编写一个类 Student,包含 name(String)、grade(int 9–12),以及访问每个字段的方法。"

Step 1. Class declaration. Always public class.

第 1 步:类声明。总是写 public class。

Step 2. Instance variables. private unless the specification says otherwise. Types come from the prompt: String name, int grade.

第 2 步:实例变量。除非规范另有说明,否则使用 private。类型来自题干:String name、int grade。

Step 3. Constructor. Public. Parameters match the data the prompt says the class needs. Use this. when the parameter shadows the field.

第 3 步:构造方法。public。形参与题干所述类所需的数据一致。当形参与字段同名时,使用 this.。

Step 4. Accessors. One per private field. Return type matches the field type. Body is a single return.

第 4 步:访问器。每个私有字段一个。返回类型与字段类型一致。方法体只有一行 return。

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

    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() { return name; }
    public int getGrade() { return grade; }
}
AP Trap: Don't Forget public AP 陷阱:别忘了 public Omitting an access modifier does not cause a Java compile error; it makes the member package-private. For this course, write public class and public constructors. Give each method the access required by the class specification: externally called methods are public, while internal helper methods may be private. 省略访问修饰符不会导致 Java 编译错误,而会得到包级访问权限。在本课程中,应写 public class 和 public 构造方法。每个方法按类规范要求设置访问权限:从类外调用的方法使用 public,内部辅助方法可以使用 private。

Constructors

构造方法

A constructor sets the initial state of an object by assigning values to all instance variables.

构造方法(constructor)通过给所有实例变量(instance variable)赋值来设定对象的初始状态。

State = the object's attributes and their current values (has-a relationship).

状态 = 对象的属性及其当前值(has-a 关系)。

When a constructor is called with new, memory is allocated and an object reference is returned.

当用 new 调用构造方法时,内存被分配,并返回一个对象引用。

Mutable Object Parameters 可变对象形参 If a constructor receives a mutable object, store a copy, not the original reference. This prevents outside code from changing the object's internal state. 如果构造方法收到一个可变对象,应保存一份副本,而不是原始引用。这样可以防止外部代码改动对象的内部状态。
JQR-safe independent copy符合 JQR 的独立副本

Suppose the specification defines a Player's complete initial state by its name and supplies getName() plus a one-String constructor. A Roster constructor can then make its own Player without relying on an ArrayList copy constructor:

假设规范说明 Player 的完整初始状态只由姓名确定,并提供 getName() 和一个接收单个 String 的构造方法。那么 Roster 的构造方法可以创建自己的 Player,而不依赖 ArrayList 的拷贝构造方法:

public class Roster {
    private Player captain;

    public Roster(Player p) {
        captain = new Player(p.getName());
    }
}

The copied Player is a different object, but it is created only from the methods and constructors supplied by the specification.

复制出的 Player 是另一个对象,但它只使用规范所提供的方法和构造方法来创建。

Default Constructor

默认构造方法

If you write no constructors, Java provides a default (no-argument) constructor that sets fields to default values:

如果你一个构造方法都没写,Java 会提供一个默认(无参)构造方法(default constructor),把各字段设为类型默认值:

Default Values, Must Know
默认值(必须掌握)
Type类型Default Value默认值
int0
double0.0
booleanfalse
Reference types引用类型null
If a class has no constructors and contains private boolean active;, what is active after new MyClass()?
如果一个类没有任何构造方法,并且包含 private boolean active;,那么 new MyClass() 之后 active 的值是?
true
null
false
Compile error编译错误
Correct! The default value of a boolean is false.
正确!boolean 的默认值是 false。
The default value for boolean instance variables is false (not null, that's for reference types).
boolean 实例变量的默认值是 false(不是 null,null 是引用类型的默认值)。

Overloaded Constructors

重载构造方法(overloaded constructor)

A class can have multiple constructors with different parameter lists. The compiler picks the right one at the call site based on the arguments, this is method overloading.

一个类可以有多个构造方法,每个的形参列表不同。编译器会在调用处根据实参选择合适的那一个,这就是方法重载(overloading)。

public class Dog {
    private String name;
    private int age;

    // Full constructor// 完整构造方法
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Convenience constructor, defaults age to 0// 便利构造方法,age 默认为 0
    public Dog(String name) {
        this.name = name;
        this.age = 0;
    }
}

Dog a = new Dog("Rex", 5);  // uses 2-arg constructor// 使用 2 参构造方法
Dog b = new Dog("Buddy");     // uses 1-arg constructor// 使用 1 参构造方法
AP Trap: A "constructor" with a return type isn't a constructor AP 陷阱:带返回类型的"构造方法"不是构造方法 If you accidentally write public void Dog(String name) { ... } (with void), it's a method named "Dog", not a constructor. Java won't invoke it via new Dog("Rex"). Worse, since no real constructor was declared, the compiler auto-generates a default no-arg constructor, so new Dog() compiles but does nothing useful. Constructors have no return type. 如果你不小心写成 public void Dog(String name) { ... }(带 void),它就是一个名叫 "Dog" 的方法,而不是构造方法。Java 不会通过 new Dog("Rex") 来调用它。更糟的是,由于没有声明真正的构造方法,编译器会自动生成一个默认无参构造方法,于是 new Dog() 能通过编译,但什么有用的事都没做。构造方法没有返回类型。

Methods: How to Write Them

方法:如何编写

void vs Non-void Methods

void 方法 vs 非 void 方法

Type类型Returns返回Keyword关键字Example示例
voidvoidNothing不返回任何值voidpublic void bark()
Non-void非 voidA single value单个值Return type返回类型public int getAge()

Accessor vs Mutator

访问器 vs 修改器

Accessor (getter), returns a value associated with an instance variable. It is always non-void. For a reference field, Java returns a copy of the reference, not a new object; the specification determines whether a fresh object is required.

访问器(accessor / getter),返回与某个实例变量相关的值,并且总是非 void。对于引用字段,Java 返回的是引用的副本,而不是新对象;是否需要新对象由规范决定。

Mutator (setter), changes an instance variable's value. Usually void.

修改器(mutator / setter),修改某个实例变量的值。通常是 void。

// Accessor// 访问器
public String getName() {
    return name;
}

// Mutator// 修改器
public void setName(String newName) {
    name = newName;
}
return Exits Immediately return 立即退出 The return keyword sends control back to the caller. Any code after return in the same block is unreachable and will cause a compile error. A return inside a loop or if-statement exits the entire method. return 关键字会把控制权交回调用者。同一代码块中 return 之后的任何代码都不可达,会导致编译错误。位于循环或 if 语句中的 return 会退出整个方法。
Pass by Value (Primitives) 按值传递(pass by value,基本类型) When a primitive is passed as an argument, the parameter gets a copy of the value. Changing the parameter inside the method does not affect the original variable. 当基本类型作为实参传入时,形参拿到的是值的副本。在方法内部修改形参不会影响原变量。

Mutator with Input Validation

带输入校验的修改器

A robust mutator validates input before changing state. Reject obviously-bad values; preserve the object's invariants.

健壮的修改器会在改变状态之前先校验输入。拒绝明显非法的值,以维护对象的不变式。

public class BankAccount {
    private double balance;

    public void deposit(double amount) {
        if (amount > 0) {           // validate// 校验
            balance += amount;
        }
        // silently ignore non-positive deposits// 静默忽略非正存款
    }

    public boolean withdraw(double amount) {
        if (amount <= 0 || amount > balance) {
            return false;            // reject invalid// 拒绝非法输入
        }
        balance -= amount;
        return true;
    }
}

Accessor Returning a Mutable Object, Defensive Copy

访问器返回可变对象,防御性拷贝(defensive copy)

If an accessor returns a reference to a mutable object such as an ArrayList, the caller can hold the same reference as your field and mutate it. Whether you must return a copy depends on the supplied specification; when the specification asks you to protect encapsulation, return a fresh copy.

如果访问器返回一个可变对象(例如 ArrayList)的引用,调用方就可能持有与字段相同的引用并修改它。是否需要返回副本取决于题目给出的规范;当规范要求保护封装时,应返回一份新副本。

// BAD: caller can mutate our internal list// 错误示范:调用方可以修改我们的内部列表
public ArrayList<String> getNames() {
    return names;
}

// GOOD: return a fresh copy using JQR-listed methods// 正确示范:使用 JQR 所列方法返回一份新副本
public ArrayList<String> getNames() {
    ArrayList<String> copy = new ArrayList<String>();
    for (int i = 0; i < names.size(); i++) {
        copy.add(names.get(i));
    }
    return copy;
}
AP Trap: Accessor That Leaks Internal State AP 陷阱:泄露内部状态的访问器 Returning the raw list reference exposes internal state. A defensive copy should be used when the specification's behavior requires it, not as a universal guaranteed rubric point. String is immutable and can be returned directly; ArrayList is mutable and may need a copy depending on the prompt. 直接返回列表引用会暴露内部状态。防御性拷贝应在规范要求保护封装时使用,而不是任何题目都必然给分的通用规则。String 是不可变的,可以直接返回;ArrayList 是可变的,是否复制要依据题干要求。

Methods: Passing and Returning References

方法:传递与返回引用

When an object reference is passed to a method, the parameter receives a copy of the reference, but both the caller's variable and the parameter refer to the same object on the heap. Mutations made through the parameter are visible to the caller. This comes up a lot on AP MCQ questions about methods.

当一个对象引用传给方法时,形参收到的是引用的副本,但调用方的变量和形参指向堆上的同一个对象。通过形参所做的修改对调用方是可见的。这是关于方法的 AP 选择题里反复出现的考点。

Primitives vs Objects in Method Calls 方法调用中:基本类型 vs 对象 A method that takes a primitive (int, double, etc.) can never change the caller's variable, the parameter is a fresh copy of the value. A method that takes an object can change the caller's object through mutation, but cannot make the caller's variable refer to a different object. It's a common AP MCQ trap. 接收基本类型(int、double 等)的方法永远无法改变调用方的变量,形参只是值的全新副本。接收对象的方法可以通过变异来改变调用方的对象,但不能让调用方的变量指向另一个对象。这是 AP 选择题常见的陷阱。
Worked Example 1: Method mutates a passed object
例题 1:方法修改传入的对象
public class Dog {
    private String name;
    public Dog(String n) { name = n; }
    public String getName() { return name; }
    public void setName(String n) { name = n; }
}

public static void rename(Dog d) {
    d.setName("Max");  // mutates the original Dog// 修改原始 Dog 对象
}

Dog rex = new Dog("Rex");
rename(rex);
System.out.println(rex.getName());  // prints: Max// 打印:Max

Why? The parameter d is a copy of the reference to the same Dog object as rex. d.setName(...) mutates that object. rex "sees" the change because both names still refer to the same heap object.

原因。形参 d 是 rex 所指 Dog 对象的引用副本。d.setName(...) 修改了该对象。rex 能"看到"这个变化,因为这两个名字仍然指向堆上同一个对象。

Worked Example 2: Reassigning the parameter doesn't change the caller
例题 2:重新给形参赋值并不会改变调用方
public static void replace(Dog d) {
    d = new Dog("Replaced");  // reassigns the LOCAL copy// 重新赋值的是本地副本
}

Dog rex = new Dog("Rex");
replace(rex);
System.out.println(rex.getName());  // prints: Rex (unchanged!)// 打印:Rex(未变!)

Why? d = new Dog(...) only changes what the local parameter d points to. The caller's rex reference is unchanged. You can't reassign your way to "replacing" the caller's object. This is the AP trap that catches the most students.

原因。d = new Dog(...) 只改变本地形参 d 所指的对象。调用方的 rex 引用并未改变。你无法靠重新赋值来"替换"调用方的对象。这是抓走最多学生的 AP 陷阱。

Worked Example 3: Aliasing in assignment
例题 3:赋值中的别名
Dog rex = new Dog("Rex");
Dog buddy = rex;       // alias: buddy and rex refer to the SAME object// 别名:buddy 和 rex 指向同一个对象
buddy.setName("Buddy");
System.out.println(rex.getName());  // prints: Buddy// 打印:Buddy

Why? buddy = rex copies the reference, not the object. Both names refer to the same Dog object. A mutation through one is visible through the other. This is "aliasing" and is the same mechanism as passing an object to a method.

原因。buddy = rex 复制的是引用,不是对象。两个名字指向同一个 Dog 对象。通过其中一个所做的修改,通过另一个也能看到。这就是"别名(aliasing)"现象,与把对象传给方法是同一种机制。

Aliasing: Friend or Bug 别名:是助手还是 bug Aliasing is fine when intentional (e.g., a shorter local name for clarity). It is a bug when it violates required behavior, such as a constructor retaining a mutable argument that the caller later changes. Constructors should copy mutable object arguments; for returned references, make a fresh object when the specification requires independent state. 如果是有意为之(例如取一个更短的本地名以提升可读性),别名没问题。当它违反所需行为时才是 bug,例如构造方法保留了调用方之后会修改的可变实参。构造方法应复制可变对象实参;对于返回的引用,则在规范要求独立状态时创建新对象。
Private Access Rule 私有访问规则 A method cannot access the private fields of a parameter object, unless the parameter is the same type as the enclosing class. e.g., a Dog instance method can read another Dog parameter's private fields directly, because they're in the same class. 方法不能访问形参对象的 private 字段,除非形参与所在类是同一类型。例如,Dog 的实例方法可以直接读取另一个 Dog 形参的私有字段,因为它们在同一个类中。
Consider public static void inc(int x) { x++; } and the call int n = 5; inc(n); System.out.println(n);. What is printed?
考虑 public static void inc(int x) { x++; },以及调用 int n = 5; inc(n); System.out.println(n);。会打印什么?
4
5
6
Compile error编译错误
Correct! x is a local copy of the value 5. x++ increments that copy, not the caller's n. n remains 5. Primitives are always passed by value in Java.
正确!x 是值 5 的本地副本。x++ 自增的是这份副本,而不是调用方的 n。n 仍然是 5。在 Java 中,基本类型始终按值传递。
Primitives are passed by value. The method's x is a copy. Mutating the copy doesn't affect the caller. n = 5.
基本类型按值传递。方法里的 x 只是一个副本,修改副本不会影响调用方。n = 5。
Consider a class Box with a void clear() method that sets a private field to null. Given Box b = new Box(); Box c = b; c.clear();, what happens to b?
某个类 Box 含有一个 void clear() 方法,会把某个私有字段设为 null。给定 Box b = new Box(); Box c = b; c.clear();,b 会发生什么?
b is unchanged because c is a copyb 不变,因为 c 是副本
b becomes nullb 变成 null
b's internal field is now nullb 的内部字段现在是 null
Compile error, can't share Box across variables编译错误,Box 不能在多个变量间共享
Correct! c = b creates an alias. c and b refer to the same Box. c.clear() mutates that shared Box, so the Box's internal field is null. The variable b itself is unchanged (still points to the same object), it's the object's state that changed.
正确!c = b 创建了一个别名。c 和 b 指向同一个 Box。c.clear() 修改了这个共享的 Box,因此 Box 的内部字段是 null。变量 b 本身没变(仍指向同一个对象),变的是该对象的状态。
Aliasing: c = b shares the reference. c.clear() mutates the object. b (the variable) still refers to the same object, but that object's internal state has changed.
别名现象:c = b 共享了引用。c.clear() 修改了该对象。变量 b 仍然指向同一个对象,但该对象的内部状态已经改变。

Class Variables and Methods

类变量与类方法

static Variables

static 变量

A static variable belongs to the class, not to any individual object. All instances share one copy.

static 变量属于类,而不属于任何单个对象。所有实例共享同一份。

public class Dog {
    private static int dogCount = 0;  // shared// 共享
    private String name;               // per object// 每个对象一份

    public Dog(String name) {
        this.name = name;
        dogCount++;
    }

    public static int getDogCount() {
        return dogCount;
    }
}
Feature特性Instance实例Class (static)类(static)
Belongs to归属Each object每个对象The class itself类本身
Accessed via访问方式obj.method()ClassName.method()
Can use instance vars?能使用实例变量吗?Yes可以No (unless passed an instance)不能(除非传入一个实例)
final Keyword final 关键字 A variable declared final cannot be reassigned after initialization. Often combined with static for constants: public static final double PI = 3.14159; 声明为 final 的变量在初始化之后不能再被赋值。常与 static 组合来定义常量:public static final double PI = 3.14159;
Can a static method directly access an instance variable?
static 方法可以直接访问实例变量吗?
Yes, always可以,总是可以
No, it needs an instance passed as a parameter不可以,需要把一个实例作为形参传入
Only if the variable is public只有当变量是 public 时才行
Correct! Static methods have no implicit object (this), so they can only access instance data through an explicit object reference.
正确!static 方法没有隐式对象(没有 this),所以只能通过显式的对象引用来访问实例数据。
Static methods don't belong to any object, so they can't directly access instance variables. They need an object reference passed in.
static 方法不属于任何对象,因此不能直接访问实例变量,需要外部传入对象引用。

Scope and Access

作用域与可见性

Local variables are declared inside a method, constructor, or block. They exist only within that block.

局部变量(local variable)声明在方法、构造方法或代码块(block)内部,只在该代码块中存在。

Scope rules:

作用域(scope)规则:

Instance variables → accessible throughout the entire class.

实例变量 → 在整个类中都可访问。

Local variables → accessible only in the block where declared.

局部变量 → 只在其声明所在的代码块内可访问。

Parameters → local to their method/constructor.

形参 → 仅在其所属的方法/构造方法中有效。

Name Shadowing 名称遮蔽(name shadowing) If a local variable or parameter has the same name as an instance variable, the local variable takes precedence inside that method. Use this.variableName to access the instance variable. 如果某个局部变量或形参与实例变量同名,那么在该方法内部,局部变量优先生效。要访问实例变量,请使用 this.variableName。
public Dog(String name) {
    // "name" here refers to the parameter// 这里的 "name" 指形参
    // "this.name" refers to the instance variable// "this.name" 指实例变量
    this.name = name;
}
Worked Example: Shadowing pitfall in action
例题:名称遮蔽陷阱实例
public class Counter {
    private int count = 100;  // instance variable// 实例变量

    public void increment(int count) {  // parameter shadows field// 形参遮蔽字段
        count++;            // increments the PARAMETER, not the field// 自增的是形参,不是字段
        this.count++;       // increments the FIELD// 自增的是字段
    }

    public int getCount() { return count; }  // field// 字段
}

Counter c = new Counter();
c.increment(5);
                  // parameter starts at 5, becomes 6 (ignored)// 形参从 5 自增到 6(被忽略)
                  // field starts at 100, becomes 101// 字段从 100 自增到 101
System.out.println(c.getCount());  // prints: 101// 打印:101

Takeaway. Without this., a bare count inside increment refers to the parameter. The field is reachable only via this.count. This is the most common cause of "constructor doesn't seem to work" bugs.

要点。在 increment 内部,如果不加 this.,光写 count 指的是形参。字段只能通过 this.count 才能访问。这是"构造方法好像没生效"这类 bug 最常见的原因。

AP Trap: Silent Shadowing AP 陷阱:无声的名称遮蔽 Shadowing compiles cleanly, the compiler never warns you. The bug manifests at runtime as "the field never changed". On the AP MCQ, watch for parameter names that match field names; if the method body doesn't use this., the field is untouched. 名称遮蔽能正常编译通过,编译器永远不会发出警告。bug 表现在运行时是"字段始终没变"。在 AP 选择题中,要盯住与字段同名的形参;如果方法体没用 this.,那么字段就没被改动过。

this Keyword

this 关键字

Inside an instance method or constructor, this is a reference to the current object, the object whose method is being called.

在实例方法或构造方法内部,this 是指向当前对象的引用,即被调用方法所属的那个对象。

Common uses of this:

this 的常见用法:

1. Disambiguate instance variables from parameters with the same name.

1. 区分实例变量与同名的形参。

2. Pass the current object as an argument to another method.

2. 把当前对象作为实参传给另一个方法。

public void addToList(DogList list) {
    list.add(this);  // pass the current Dog object// 传入当前 Dog 对象
}
No this in static Methods static 方法中没有 this Since static methods aren't called on any object, they have no this reference. Attempting to use this in a static method causes a compile error. 由于 static 方法不针对任何对象调用,它们没有 this 引用。在 static 方法中使用 this 会导致编译错误。
Worked Example: Using this to compare two objects
例题:用 this 比较两个对象

Problem. Write an isHeavierThan method for a Dog class with a private weight field, comparing the calling object against another Dog passed in.

题目。为带私有 weight 字段的 Dog 类编写 isHeavierThan 方法,把调用它的对象与传入的另一个 Dog 比较。

public boolean isHeavierThan(Dog other) {
    return this.weight > other.weight;  // this = calling object, other = parameter// this = 调用对象,other = 形参
}

Dog rex = new Dog("Rex", 30);
Dog fido = new Dog("Fido", 18);
rex.isHeavierThan(fido);  // this = rex, other = fido → true// this = rex,other = fido → true

Takeaway. Writing this.weight is optional here (no shadowing parameter named weight), but it makes explicit which object's field is on which side of the comparison — a habit that prevents mistakes once methods get more complex.

要点。这里写 this.weight 并非必需(没有同名的 weight 形参造成遮蔽),但它明确标出了比较式两侧分别是哪个对象的字段——方法变复杂后,这个习惯能避免出错。

Inside an instance method, what does this refer to?
在实例方法内部,this 指的是什么?
The object the method was called on调用该方法所依附的对象
The class itself类本身
Whichever object is passed as a parameter作为形参传入的对象
Correct! this always refers to the current object — the one whose method is executing, e.g. the object before the dot in obj.method().
正确!this 始终指向当前对象——即正在执行其方法的那个对象,也就是 obj.method() 中点号前面的那个对象。
this refers to the current object, not the class or the parameter variable. A Dog parameter is a separate reference; it may refer to a different object or alias this, and is accessed through its parameter name.
this 指的是当前对象,不是类,也不是形参变量。类型为 Dog 的形参是另一个引用;它可能指向不同对象,也可能与 this 指向同一对象,代码中通过形参名访问它。

Complete Class, BankAccount

完整类示例:BankAccount

public class BankAccount {
    // Class variable: tracks how many accounts exist// 类变量:记录存在多少个账户
    private static int accountCount = 0;

    // Instance variables (private for encapsulation)// 实例变量(为封装而私有)
    private String owner;
    private double balance;
    private int id;

    // Constructor// 构造方法
    public BankAccount(String owner, double initialBalance) {
        this.owner = owner;
        this.balance = initialBalance;
        accountCount++;
        this.id = accountCount;
    }

    // Accessor methods// 访问器方法
    public String getOwner() { return owner; }
    public double getBalance() { return balance; }
    public int getId() { return id; }

    // Mutator methods// 修改器方法
    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        }
    }

    // Class method// 类方法
    public static int getAccountCount() {
        return accountCount;
    }
}
What to Notice 要点 Private instance variables, public constructor and methods, this for disambiguation, a static variable for counting, and a static method to access it. This is the pattern the AP exam expects you to write for FRQ #2 (Class Design). 私有实例变量(instance variable)、公有构造方法(constructor)与方法、用 this 消除歧义、用 static 变量计数、再用一个 static 方法读取它。这正是 AP 考试 FRQ #2(类设计题)希望你写出的模式。

How Class Creation Shows Up on the AP Exam

类的定义在 AP 考试中如何考

The current AP CSA exam has 4 Free-Response Questions, including FRQ #2: Class Design. The exact prompt and scoring guidelines vary, so implement the supplied class specification rather than relying on fixed point allocations.

当前 AP CSA 考试有 4 道自由回答题(FRQ),其中包括 FRQ #2:类设计。具体题干和评分细则会变化,因此应按给定的类规范实现,而不要依赖固定给分点。

Typical FRQ #2 prompt structure:

典型的 FRQ #2 题干结构:

"Write a class X that has <list of attributes>. Include a constructor that <assigns the attributes from parameters>. Include accessor methods <getName, getAge, ...>, mutator methods <setAge with validation>, and a behavior method <summary, isAvailable, ...> that uses the fields."

"编写一个类(class)X,它包含 <属性清单>。包含一个 <从形参为属性赋值> 的构造方法(constructor)。包含访问器方法(accessor)<getName、getAge…>、修改器方法(mutator)<带校验的 setAge>,以及一个 <summary、isAvailable…> 这样的行为方法,使用各字段。"

Read the complete specification before coding, then verify every requested attribute, constructor, method signature, precondition, and behavior.

编码前先读完整规范,再逐项核对所需属性、构造方法、方法签名、前置条件和行为。

Class Design Specification Checklist (Scoring Varies) 类设计规范核对表(评分会变化)
  • Declare public class X; keep instance variables private unless the specification states otherwise
  • Match the constructor parameters and initialize every instance variable
  • Use this. where a parameter shadows an instance variable
  • Match every requested method's access, return type, parameter list, and behavior
  • Implement validation or copying only where the course rule or supplied specification requires it
  • 声明 public class X;除非规范另有说明,否则实例变量使用 private
  • 构造方法形参与题干一致,并初始化每个实例变量
  • 形参与实例变量同名时使用 this. 消除歧义
  • 每个所需方法的访问权限、返回类型、形参列表和行为都与规范一致
  • 仅在课程规则或给定规范要求时实现校验或复制
Common Class Design Mistakes (Scoring Varies) 类设计常见错误(评分会变化)
  • Public instance variables when the specification does not provide an exception
  • Missing public on the class, constructor, or an externally called method
  • Constructor with a return type (e.g., public void Dog(...), silently becomes a method)
  • Accessor with the wrong return type (e.g., void on a getter)
  • Forgetting to assign some of the instance variables in the constructor
  • Parameter name shadowing the field, without this. qualification
  • Returning a raw mutable reference when the specification requires independent state
  • Forgetting input validation on a mutator when the prompt requires it
  • 规范没有给出例外时,实例变量却写成 public
  • 类、构造方法或需要从外部调用的方法漏写 public
  • 构造方法带返回类型(例如 public void Dog(...),悄无声息地变成普通方法)
  • 访问器返回类型写错(例如给 getter 写 void)
  • 构造方法中漏给某些实例变量赋值
  • 形参与字段同名,但没有用 this. 限定
  • 规范要求独立状态时,却直接返回可变对象的原始引用
  • 题干要求校验,却忘了在修改器中做输入校验
Time-Saver, Read the Stem Twice 省时技巧:把题干读两遍 Before writing code, identify the data and behaviors named in the prompt, then derive field types, visibility, method return types, and parameters from the specification. Do not add unrequested fields or methods that could change the required behavior. 写代码前,先识别题干中的数据和行为,再依据规范确定字段类型与可见性,以及方法的返回类型和形参。不要添加可能改变所需行为的额外字段或方法。

Flashcards, Click to Flip

闪卡:点击翻面

What is encapsulation?什么是封装(encapsulation)?
Hiding implementation details from external classes using private access.用 private 访问权限把实现细节对外部类隐藏起来。
Accessor vs Mutator?访问器(accessor)vs 修改器(mutator)?
Accessor → returns data (getter, non-void)
Mutator → changes data (setter, usually void)
访问器 → 返回数据(getter,非 void)
修改器 → 修改数据(setter,通常是 void)
Default value of an int field?int 字段的默认值?
0
Default value of a reference type field?引用类型字段的默认值?
null
What does this refer to?this 指代什么?
The current object, the one whose method or constructor is executing.当前对象,即正在执行其方法或构造方法的那个对象。
static variable belongs to…?static 变量属于……?
The class (shared by all instances), not any individual object.类(由所有实例共享),而不是任何单个对象。
Can a static method use this?static 方法可以使用 this 吗?
No, static methods don't run on an object, so there's no this.不行,static 方法不针对对象运行,所以没有 this。
What is name shadowing?什么是名称遮蔽(name shadowing)?
Local var hides the instance field of the same name.
Fix: use this.var to reach the field.
局部变量遮蔽了同名的实例字段。
修复:用 this.var 来访问字段。
Pass-by-value for objects means…?对象的按值传递(pass by value)是指……?
A copy of the reference is passed.
The method can still mutate the original object.
传入的是引用的副本。
方法仍然可以修改原始对象。
What does final do?final 有什么作用?
Prevents a variable from being reassigned after initialization.禁止变量在初始化之后被重新赋值。
Can constructors be overloaded?构造方法(constructor)可以重载(overloading)吗?
Yes, multiple constructors with different parameter lists. Java picks one based on the arguments at the call site.可以,多个构造方法、形参列表各不相同。Java 会根据调用处的实参选择其中一个。
Should a constructor declare a return type?构造方法应该声明返回类型吗?
No. Writing void or any type turns it into a regular method, not a constructor, and Java will auto-generate the default no-arg constructor.不应。写 void 或任何类型,都会把它变成普通方法而非构造方法,Java 会自动生成默认无参构造方法。
When does a getter need a fresh object?getter 何时需要返回新对象?
A mutable field's reference lets the caller mutate the same object. Return a fresh object when the specification requires independent state.可变字段的引用会让调用方修改同一对象。当规范要求独立状态时,应返回新对象。
Can a method reassign the caller's reference?方法能重新指定调用方的引用吗?
No. Reassigning the parameter (d = new Dog(...)) only changes the local copy. The caller's reference still points to the original object.不能。给形参重新赋值(d = new Dog(...))只改变本地副本。调用方的引用仍指向原对象。
How are static methods called?static 方法如何调用?
ClassName.method(), using the class name, not an object reference. e.g., Math.abs(-5), User.getTotalUsers().ClassName.method(),用类名调用,而不是对象引用。例如 Math.abs(-5)、User.getTotalUsers()。

Unit 3, Practice Quiz

单元 3 练习小测

1. Unless the class specification says otherwise, which access modifier should instance variables have for encapsulation?
1. 除非类规范另有说明,为实现封装,实例变量应使用哪个访问修饰符?
private
public
static
final
Correct! Instance variables should be private unless the specification states otherwise.
正确!除非规范另有说明,否则实例变量应使用 private。
Use private for instance variables unless the specification provides an exception.
除非规范给出例外,否则实例变量应使用 private。
2. What happens if you write no constructors in a class?
2. 如果你在类(class)中一个构造方法(constructor)都不写,会发生什么?
The class cannot create objects该类无法创建对象
All fields are set to null所有字段都被设为 null
Java provides a default no-arg constructor with default valuesJava 提供一个无参的默认构造方法,字段使用默认值
Compile error编译错误
Correct! Java auto-generates a no-parameter constructor. Fields get type-based defaults: 0, 0.0, false, or null.
正确!Java 会自动生成一个无参构造方法。字段获得基于类型的默认值:0、0.0、false 或 null。
Java provides a default constructor when none is written. Fields get defaults based on type.
当一个构造方法都没写时,Java 会提供默认构造方法。字段按类型获得默认值。
3. What type of method is public double getBalance()?
3. public double getBalance() 属于哪一类方法?
Mutator (void)修改器(mutator,void)
Accessor (non-void)访问器(accessor,非 void)
Constructor构造方法
Static methodstatic 方法
Correct! It returns a value (non-void) and reads data without changing it, that's an accessor/getter.
正确!它返回一个值(非 void),并且只读数据不修改数据,这就是访问器/getter。
A method with a return type that reads (not modifies) data is an accessor.
带返回类型、只读取(不修改)数据的方法就是访问器。
4. Given private static int count = 0;, how is count shared?
4. 给定 private static int count = 0;,count 是如何共享的?
Each object gets its own copy每个对象拥有自己的副本
Only the first object can access it只有第一个对象可以访问
It cannot be changed它不能被修改
All objects of the class share one copy该类的所有对象共享同一份
Correct! static means the variable belongs to the class, all objects share a single copy.
正确!static 意味着该变量属于类,所有对象共享同一份。
static variables are shared: one copy for the whole class, not per-object.
static 变量是共享的:整个类只有一份,而不是每个对象一份。
5. Inside a constructor, this.name = name; does what?
5. 在构造方法内部,this.name = name; 的作用是什么?
Assigns the parameter name to the instance variable name把形参 name 赋给实例变量 name
Creates a new variable called this.name创建一个名为 this.name 的新变量
Causes a compile error引发编译错误
Assigns the instance variable to the parameter把实例变量赋给形参
Correct! this.name refers to the instance variable, while name refers to the parameter. The parameter's value is stored in the field.
正确!this.name 指向实例变量,而 name 指向形参。形参的值被存入字段。
this.name = instance variable, name = parameter. The line stores the parameter value into the field.
this.name = 实例变量,name = 形参。这一行把形参的值存到字段里。
6. Consider:
public class Box {
    private int[] contents;
    public Box(int[] items) {
        contents = items;
    }
}
int[] arr = {1, 2, 3};
Box b = new Box(arr);
arr[0] = 99;
What is in b's contents after this code runs?
6. 考虑:
public class Box {
    private int[] contents;
    public Box(int[] items) {
        contents = items;
    }
}
int[] arr = {1, 2, 3};
Box b = new Box(arr);
arr[0] = 99;
这段代码执行后,b 的 contents 是什么?
{1, 2, 3}, the Box made a copy{1, 2, 3},Box 做了一份副本
Compile error编译错误
{99, 2, 3}, the Box's field aliases arr{99, 2, 3},Box 的字段是 arr 的别名
null
Correct! contents = items stores a copy of the caller's reference, so both references designate the same array. Mutating arr is visible through contents. A conforming constructor should create an independent copy using operations supplied by the specification.
正确!contents = items 存下调用方引用的副本,所以两个引用指向同一数组。修改 arr 后,通过 contents 也能看到。符合课程规则的构造方法应使用规范提供的操作创建独立副本。
Both contents and arr reference the same array, so mutation through either reference is visible through the other. The shown constructor did not create a new array.
contents 和 arr 指向同一数组,因此通过任一引用所做的修改都能通过另一个看到。题示构造方法没有创建新数组。
7. Given the class
public class Counter {
    private static int total = 0;
    private int value;
    public Counter(int v) { value = v; total++; }
    public int getValue() { return value; }
    public static int getTotal() { return total; }
}
which of the following lines does not compile?
7. 给定如下类:
public class Counter {
    private static int total = 0;
    private int value;
    public Counter(int v) { value = v; total++; }
    public int getValue() { return value; }
    public static int getTotal() { return total; }
}
下列哪一行不能通过编译?
Counter c = new Counter(5);
Counter.getValue();
Counter.getTotal();
new Counter(0).getValue();
Correct! getValue() is an instance method, it needs an object reference. Counter.getValue() tries to call it as a static method, which doesn't compile.
正确!getValue() 是实例方法,需要对象引用。Counter.getValue() 把它当作 static 方法调用,不能通过编译。
Static methods are called on the class. Instance methods need an object. getValue() is instance, so Counter.getValue() fails to compile.
static 方法用类名调用,实例方法需要对象。getValue() 是实例方法,所以 Counter.getValue() 编译失败。
8. Consider:
public class P {
    private int n;
    public P(int n) { n = n; }
    public int getN() { return n; }
}
System.out.println(new P(42).getN());
What is printed?
8. 考虑:
public class P {
    private int n;
    public P(int n) { n = n; }
    public int getN() { return n; }
}
System.out.println(new P(42).getN());
会打印什么?
42
0
Compile error编译错误
null
Correct! Classic shadowing bug. Inside the constructor, n = n assigns the parameter n to itself, it never touches the field. The field stays at its default value 0. Fix: this.n = n;
正确!经典的名称遮蔽(name shadowing)bug。在构造方法内部,n = n 把形参 n 赋给它自己,根本没碰字段。字段保留默认值 0。修复:this.n = n;
The constructor body is n = n, which assigns the parameter to itself. The field is shadowed and never written, so it keeps its default value 0. Always use this.n = n when names collide.
构造方法体是 n = n,把形参赋给它自己。字段被遮蔽且从未被写入,因此保留默认值 0。名称冲突时永远写 this.n = n。
9. A class has a private String[] arr and public String[] getArr() { return arr; }. What behavior follows?
9. 某个类有 private 字段 String[] arr 和方法 public String[] getArr() { return arr; }。会产生什么行为?
The caller can mutate the array, breaking encapsulation调用方可以修改数组,从而破坏封装
The method signature is invalid for arrays对数组而言该方法签名不合法
The method needs to be static该方法需要写成 static
There is no issue, accessors should return the field directly没有问题,访问器就该直接返回字段
Correct! The returned reference designates the same array, so a caller can mutate that array through the returned reference. Whether the method must instead create a fresh object depends on the supplied specification.
正确!返回的引用指向同一数组,所以调用方能通过该引用修改数组。该方法是否必须改为创建新对象,取决于给定规范。
The signature is valid and the method need not be static. It returns the existing array reference; the specification determines whether that aliasing is permitted.
这个方法签名合法,也不需要写成 static。它返回现有数组引用;这种别名是否允许由规范决定。

AP-Style Practice Problems

AP 风格练习题

15 exam-level multiple-choice questions (3 medium · 12 hard), code-trace heavy with realistic distractors and fully worked solutions. Built for top-score prep — go here after you've worked through the notes and the in-page quiz above.

15 道考试难度选择题(3 道中等 · 12 道困难),偏重代码追踪,干扰项贴近真题,每题都有完整解析。为冲击满分而设——把上面的讲义和小测过完之后再来。

Practice Problems →练习题 →

鼎睿学苑 · Dingrui Scholars

AP Computer Science A, Unit 3: Class Creation · 2026 Edition

AP 计算机科学 A,单元 3:类的定义 · 2026 版