跳到内容

元数据卡

  • 前置知识:第7章全三节(上/中)
  • 预计时间:20 分钟
  • 完成标志:能独立完成两道编程题,能说出常见陷阱的成因

常见陷阱

陷阱一:两个变量指向同一个对象

java
Book b1 = new Book("Java 核心技术", "Horstmann", 99);
Book b2 = b1;                     // ⚠️ 不是复制对象,是复制引用!
b2.setTitle("不同书名");
System.out.println(b1.getTitle()); // "不同书名"——b1 也变了

原因b1b2 指向堆里同一个对象。解决方案:写拷贝构造方法 Book(Book other) { this(other.title, other.author, other.price); },然后 new Book(b1)

陷阱二:Java 是值传递

java
static void changeTitle(Book b) {
    b.setTitle("新书名");     // ✅ 通过地址副本修改了同一个对象
    b = new Book("另一本", "某", 50); // ❌ 新地址只影响本地副本
}

核心:Java 永远传值的副本。基本类型传数值,引用类型传地址副本。改"引用指向的对象"能影响外部,改"引用本身"不能。

陷阱三:构造方法里别调用可重写的方法

java
class Parent {
    Parent() { print(); }  // ⚠️ 危险
    void print() { System.out.println("Parent"); }
}
class Child extends Parent {
    String value = "Hello";
    @Override void print() { System.out.println(value.length()); }
}
new Child();  // NullPointerException——Child 的 value 还没初始化!

教训:极其难调试。构造方法中不要调用被子类重写的方法。

陷阱四:getter 暴露内部可变对象

java
public List<String> getCourses() { return courses; }  // ⚠️ 外部能改内部列表

安全做法return Collections.unmodifiableList(courses)return new ArrayList<>(courses)


热身练习(10 分钟,必做)

1. 定义 Student 类——private 字段 nameagescore,带校验的构造方法与 getter/setter。

java
public class Student {
    private String name;
    private int age;
    private double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        if (score < 0 || score > 100)
            throw new IllegalArgumentException("成绩必须 0-100");
        this.score = score;
    }
    // getter/setter 省略...
}

2.main 中创建三个 Student 对象,存数组,遍历算平均分。

3.static 字段 count 跟踪创建总数。


挑战练习(20 分钟,选做)

BankAccount 类

java
public class BankAccount {
    private String accountNumber;  // 不可修改
    private String ownerName;
    private double balance;        // 余额不能为负

    public BankAccount(String accountNumber, String ownerName, double initialDeposit) {
        if (accountNumber == null || accountNumber.isEmpty())
            throw new IllegalArgumentException("账号不能为空");
        if (initialDeposit < 0)
            throw new IllegalArgumentException("初始存款不能为负");
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = initialDeposit;
    }

    // getters——所有字段
    public String getAccountNumber() { return accountNumber; }
    public String getOwnerName() { return ownerName; }
    public double getBalance() { return balance; }
    // 没有 setAccountNumber / setBalance——账号不可改,余额靠 deposit/withdraw

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("存款金额必须 > 0");
        balance += amount;
    }

    public boolean withdraw(double amount) {
        if (amount <= 0) return false;
        if (amount > balance) return false;
        balance -= amount;
        return true;
    }

    public void transfer(BankAccount target, double amount) {
        if (withdraw(amount)) target.deposit(amount);
    }
}

测试:创建两个账户,存钱、取钱、转账、尝试透支。


旅人笔记

类 = 蓝图,对象 = 实例。构造方法 = 降生洗礼。this = 指向自己。static = 属于类的全体共享。private = 藏起来,public 方法 = 安全的门。getter/setter = 带校验的读写口。package = 命名空间。

这段旅程最难的不是语法——是从"写一段代码"到"定义一个类型"的思维转变。


验收标准

  • [ ] 说清类(蓝图)和对象(实例)的区别
  • [ ] 会写 private 字段 + public getter/setter
  • [ ] 会写带校验的构造方法,理解无参构造何时消失
  • [ ] 会用 this 区分字段和参数
  • [ ] 理解 static 属于类本身,static 方法不能访问实例成员
  • [ ] 理解 Java 值传递——引用复制不复制对象
  • [ ] 知道 private/默认/protected/public 的可见范围

下一步

现实世界不是一座孤岛——StudentPerson 的一种。类之间的继承、组合、多态将在下一章展开。

下一章:继承与多态ch08-inheritance-polymorphism.md

Built with VitePress | Software Systems Atlas