Mr.Mou @ ShiShi AP Center

AP CSA 3.3:Java 类的结构

在 Unit 1 中,我们一直在使用别人已经写好的类:例如 String、Turtle,以及它们的对象和方法。从这一课开始,我们反过来学习:一个 Java 类本身是怎样组成的,以及怎样设计自己的类。

一个类的主体通常由三部分组成:实例变量(instance variables)保存对象的数据,构造器(constructor)负责初始化对象,方法(methods)定义对象能做什么。

核心知识点

1. 第一个 AP 核心考法

下面的 Cat 类需要保存猫的名字和年龄,并提供一个构造器:

public class Cat
{
    /* missing code */
}

哪一种设计最合理?

A

public String name;
public int age;

public Cat(String n, int a)
{
    // implementation not shown
}

B

private String name;
private int age;

public Cat(String n, int a)
{
    // implementation not shown
}

C

private String name;
private int age;

private Cat(String n, int a)
{
    // implementation not shown
}

答案:B

这里先记住两个 AP CSA 中非常重要的设计习惯:

实例变量 → private
构造器   → public

为什么要这样设计,会在下面逐步解释。

2. 一个 Java 类由什么组成?

先看一个完整但很小的 Person 类:

public class Person
{
    // 1. instance variables
    private String name;
    private String email;

    // 2. constructor
    public Person(String n, String e)
    {
        name = n;
        email = e;
    }

    // 3. method
    public void print()
    {
        System.out.println(name + " " + email);
    }
}

一个典型的类可以先看成下面这个骨架:

public class ClassName
{
    // instance variables

    // constructor

    // methods
}
部分 作用 AP CSA 中常见写法
类(class) 定义一种新的对象类型 public class Person
实例变量(instance variables) 保存对象的数据 通常 private
构造器(constructor) 创建对象时初始化数据 public
方法(methods) 定义对象能做什么 根据是否需要类外访问决定 public / private

在 AP CSA 本课程范围内,类写成 public class ClassName,构造器使用 public,实例变量通常使用 private。

这节课最重要的不是马上学会把每一部分都写出来,而是先能够认出这些部分,并理解它们各自负责什么。

3. 实例变量:对象保存的数据

实例变量(instance variables)保存一个对象的属性或状态。

例如:

private String name;
private String email;

它们写在类的主体中,但写在构造器和方法之外。

public class Person
{
    private String name;
    private String email;

    // constructor and methods...
}

这里的 name 和 email 不是某一个特定人的数据,而是在说明:

每一个 Person 对象都应该拥有一个 name 和一个 email。

例如:

Person ada = new Person("Ada", "ada@example.com");
Person alan = new Person("Alan", "alan@example.com");

这两个对象来自同一个 Person 类,但它们各自保存自己的数据:

对象 name email
ada "Ada" "ada@example.com"
alan "Alan" "alan@example.com"

这就是实例变量中的 instance:

每个实例(object / instance)都有自己的一份变量。

改变 ada 的数据,不代表 alan 的数据也一起改变。

4. public、private 与数据封装

public 和 private 叫作访问修饰符(access modifiers)。它们决定代码可以从哪里访问一个成员。

修饰符 含义
public 当前类内部和其他类都可以访问
private 只能在声明它的类内部访问

例如:

public class Person
{
    private String name;

    public void print()
    {
        System.out.println(name);
    }
}

name 是 private,所以类外代码不应该直接这样做:

Person p = new Person("Ada", "ada@example.com");
p.name = "Grace";     // cannot directly access a private field

但 print() 是 public,所以类外代码可以调用:

p.print();

这种设计背后的概念叫 数据封装(data encapsulation):

把对象内部的数据和实现细节保护在类里面,只把需要使用的操作暴露给外部代码。

可以把它理解成:

外部代码
   ↓
public methods
   ↓
-----------------
类的内部
private instance variables
-----------------

所以在 AP CSA 中,除非题目特别说明,实例变量通常写成:

private type variableName;

例如:

private String name;
private int age;
private double balance;

5. 构造器和实例方法在类中的位置

构造器(constructor)

构造器的任务是在对象创建时初始化对象的数据。

public Person(String n, String e)
{
    name = n;
    email = e;
}

这一课先认出它即可:

构造器内部具体发生什么,会在 3.4 Constructors 中继续学习。

实例方法(instance methods)

实例方法定义对象的行为。

public void print()
{
    System.out.println(name + " " + email);
}

这个方法能够直接使用 name 和 email,因为这些变量属于同一个类中的对象。

在 Unit 1 中,我们已经一直在调用这种方法:

p.print();

这里的重点是:

对哪个对象调用方法,方法就使用哪个对象自己的实例变量。

例如:

ada.print();
alan.print();

ada.print() 使用的是 ada 自己的 name 和 email;alan.print() 使用的是 alan 自己的数据。

方法是否使用 public 或 private,取决于它是否需要被类外代码调用:

需要给外部使用 → public
只在类内部辅助使用 → private

6. 设计一个类时,先问两个问题

写类并不是先想 Java 语法,而是先决定这个类要表示什么。

假设要设计一个 Book 类,可以先问:

1. 这个对象需要保存什么数据?

例如:

title
author
pages

这些通常会变成实例变量:

private String title;
private String author;
private int pages;

2. 这个对象需要做什么?

例如:

printInfo()
isLongBook()

这些通常会变成方法。

所以可以把面向对象设计最开始的思路压缩成:

对象“有什么” → instance variables
对象“能做什么” → methods

构造器则负责在创建对象时,把一开始的数据放进这些实例变量中。

7. 常见初学者错误

错误 错误想法 / 代码 为什么不合适 正确理解
把实例变量写成 public public int age; 类外代码可以直接改内部数据 AP CSA 中通常使用 private int age;
把构造器写成 private private Person(...) 在本课程的 class design 中,构造器应允许外部创建对象 使用 public Person(...)
以为多个对象共享同一份实例变量 改 ada.name 后以为 alan.name 也会变 每个对象都有自己的实例变量 ada 和 alan 各有自己的 name
把类和对象混为一谈 说 Person 是某一个具体的人 Person 是蓝图 / 类型 ada 才是一个 Person 对象的引用变量
从类外直接访问 private 字段 p.name private 只允许当前类内部访问 通过类提供的 public 方法与对象交互
认为所有方法都必须 public helper method 也一定 public 有些方法只需要在类内部使用 外部要调用用 public;内部辅助可用 private

8. 小练习(Mini Practice)

Practice 1:识别类的三个主要部分

下面代码中的 name、Student(...) 和 printName() 分别是什么?

public class Student
{
    private String name;

    public Student(String n)
    {
        name = n;
    }

    public void printName()
    {
        System.out.println(name);
    }
}

答案:

name         → instance variable
Student(...) → constructor
printName()  → method

Practice 2:每个对象的数据

Student a = new Student("Amy");
Student b = new Student("Ben");

哪一种说法正确?

A. a 和 b 共用同一个 name 变量
B. a 和 b 各自有自己的 name 变量
C. 只有第一个创建的对象有实例变量

答案:B

同一个类创建出的每个对象都有自己的一份实例变量,因此 a 可以保存 "Amy",b 可以保存 "Ben"。


Practice 3:选择合适的访问权限

一个 Party 类需要:

下面哪种设计最合理?

A

public int numPeople;
private Party(int n) { }
private void startParty() { }

B

private int numPeople;
public Party(int n) { }
public void startParty() { }

C

private int numPeople;
private Party(int n) { }
public void startParty() { }

答案:B


Practice 4:哪一行不能从类外直接访问?

假设有:

public class Account
{
    private double balance;

    public void printBalance()
    {
        System.out.println(balance);
    }
}

并且变量 a 已经指向一个 Account 对象。下面哪一行在另一个类中不应该被允许?

A. a.printBalance();
B. System.out.println(a.balance);

答案:B

balance 是 private,只能在 Account 类内部直接访问;printBalance() 是 public,所以类外代码可以调用。

Unit 3.3 核心词汇(Vocabulary)

Vocabulary 中文理解 核心理解 / Example
class / 类 创建对象的蓝图或类型 public class Person
class body / 类主体 类的 { } 中定义成员的部分 包含实例变量、构造器和方法
instance variable / 实例变量 每个对象自己拥有的数据 private String name;
attribute / field / 属性、字段 常用来指对象保存的数据 在本课中通常就是 instance variable
constructor / 构造器 创建对象时初始化数据的特殊代码 public Person(...)
instance method / 实例方法 定义对象行为的方法 p.print()
access modifier / 访问修饰符 控制成员能从哪里访问 public、private
public / 公有 类外代码也可以访问 常用于需要对外提供的 constructor / method
private / 私有 只能在当前类内部直接访问 实例变量通常使用 private
data encapsulation / 数据封装 隐藏类的内部数据和实现细节 外部通过公开的方法与对象交互