下载工作台
Java 编程实战

面向对象:类、继承与多态

试读上半部分 · 解锁后可读全文

第 5 章 · 面向对象:类、继承与多态

本章目标:理解类、对象、成员变量与方法;掌握构造器与 this;熟练使用访问修饰符;理解继承super方法重写;掌握多态(向上转型、动态绑定);能设计 toolkit-demo 的商品与折扣商品模型;对照 python-dev ch06 OOP。

学时建议:4~5 小时(含 2 小时跟练)

前置:完成 java-dev ch04;建议已学 python-dev ch06


5.1 场景说明:商品模型

toolkit-demo 需要描述普通商品与促销商品:共享 SKU、名称、单价,促销商品额外折扣率并重写价格计算——类似 python-dev 中 Product 基类与 SaleProduct 子类。

learn-java/toolkit-demo/src/main/java/demo/model/
  ├── Product.java
  ├── DiscountProduct.java
  └── CatalogDemo.java
概念说明
封装价格字段私有,通过方法访问
继承DiscountProduct extends Product
多态Product p = new DiscountProduct(...)
重写子类 getPrice() 含折扣

5.2 类、对象与构造器

类是模板,对象由 new 创建。Java 显式声明类型,用 this 指当前对象(类比 Python self)。

术语说明
字段成员变量,通常 private
构造器与类同名、无返回类型,用于初始化
this(...)调用其他构造器,须在第一行
重载同类多构造器参数不同

封装原则:字段 private,对外提供 getter/setter 或业务方法(见 5.7 Product 完整代码)。


5.3 访问修饰符

修饰符类内同包子类全局
private
默认(包私有)
protected
public

封装原则:字段通常 private,对外 getter/setter 或业务方法。


5.4 继承、多态与重写

子类 extends 父类,用 super 调父构造器/方法。@Override 标记重写;Java 单继承(接口多实现见 ch06)。

Product p2 = new DiscountProduct("B", "促销", 100, 0.8);
printPrice(p2);   // 80 — 动态绑定子类 getUnitPrice()

static void printPrice(Product p) {
    System.out.println(p.getSku() + " 售价:" + p.getUnitPrice());
}

向上转型:编译类型 Product,运行类型 DiscountProduct 时调用子类方法。

PythonJava
duck typing编译期检查 + 运行时多态
class B(A)class B extends A

5.5 重写 vs 重载

重写 Override重载 Overload
位置子类与父类同类
签名相同参数不同
返回可协变可不同
注解@Override
// 重载:同类多方法
public double calc(int qty) { return getUnitPrice() * qty; }
public double calc(int qty, double extra) { return calc(qty) + extra; }

5.6 toString、equals 与 hashCode:Object 的三大方法

所有类都直接或间接继承 java.lang.Object,其中有三个最常被重写的方法:

方法默认行为何时重写
toString()输出 demo.model.Product@6f496d9f 这类地址串需要可读日志、调试输出时
equals(Object)== 相同,比较引用需要「内容相等」语义时
hashCode()由对象地址计算重写 equals 时必须同时重写

为什么 equals 与 hashCode 必须成对重写:ch08 的 HashSet/HashMap 先用 hashCode() 决定放进哪个「桶」,再用 equals() 在桶内判等。只重写 equals 不重写 hashCode,两个内容相等的对象会落入不同桶,Set 里出现重复元素——这是面试与生产 Bug 的高频点。

import java.util.Objects;

// 以下三个方法加入 5.7 的 Product 类中
@Override
public String toString() {
    return "Product{sku='" + sku + "', name='" + name + "', unitPrice=" + unitPrice + "}";
}

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;                    // 同一引用,直接相等
    }
    if (!(o instanceof Product other)) {
        return false;                   // 类型不同或为 null
    }
    // 业务约定:sku 相同即同一商品
    return Objects.equals(sku, other.sku);
}

@Override
public int hashCode() {
    return Objects.hash(sku);           // 与 equals 使用相同的字段
}

要点

  • equals 的参数必须是 Object;写成 equals(Product p)重载而不是重写,集合框架永远不会调用它
  • instanceof 模式匹配(Java 16+)一步完成类型检查与转型
  • Objects.equals(a, b)Objects.hash(...) 内部处理了 null,避免 NPE
  • equalshashCode 必须使用相同的字段(这里是 sku),否则契约被破坏
  • 对照 Python:等价于重写 __eq____hash__,规则同样是「成对出现」

5.7 Product.java 完整代码

package demo.model;

/**
 * 商品基类 — toolkit-demo
 */
public class Product {

    private String sku;
    private String name;
    private double unitPrice;

    public Product() {
        this.sku = "UNKNOWN";
        this.name = "未命名";
        this.unitPrice = 0.0;
    }

    public Product(String sku, String name, double unitPrice) {
        this.sku = sku;
        this.name = name;
        setUnitPrice(unitPrice);
    }

    public String getSku() {
        return sku;
    }

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。