Skip to content

S02-10 面向对象-UML

[TOC]

概述

什么是 UML

UML(Unified Modeling Language,统一建模语言) 并不是一种编程语言,而是一种用于软件系统设计和文档化的可视化建模工具。在 Java 这类强面向对象语言的学习和开发中,UML(尤其是类图)是理解面向对象思想、梳理业务逻辑以及学习设计模式的“通用语言”。

进一步,我们可以总结出几个关键字:软件建模语言工业标准面向对象系统建模架构设计最佳时间

在UML系统开发中有三个主要的模型

  • 「功能模型」 :从用户的角度展示系统的功能,包括用例图
  • 「对象模型」 :采用对象,属性,操作,关联等概念展示系统的结构和基础,包括类图对象图
  • 「动态模型」 :展现系统的内部行为。包括序列图活动图状态图

UML包含了一系列的图,最常用的有类图时序图用例图等。

类图

类图(Class Diagram) 是 UML 中最核心、使用最广泛的图表。它用于描述系统的静态结构,完美地映射了 Java 中的类、接口、属性、方法以及它们之间的相互关系。

掌握类图,不仅能帮助你快速看懂复杂的开源项目架构,更是学习和理解设计模式的基础。

类的 UML 描述

单个类的 UML 表示方法:

在类图中,一个 Java 类(或接口)通常被画成一个分为三层的矩形。

img
  • 顶部:类名 (Class Name)

    • 普通类:正体字显示类名(如 User)。
    • 抽象类:类名用斜体字表示。
    • 接口:在类名上方带有 <<interface>><<Interface>> 构造型标签(如 <<interface>> Runnable)。
  • 中部:属性 (Attributes)

    对应 Java 中的成员变量。书写格式为:可见性 名称 : 类型 [ = 默认值 ]

    可见性符号(对应 Java 访问修饰符):

    • + 代表 public
    • - 代表 private
    • # 代表 protected
    • ~ 代表 default (包可见/无修饰符) 示例: - age : int = 18 表示 Java 中的 private int age = 18;
  • 底部:方法 (Operations/Methods)

    对应 Java 中的方法。书写格式为:可见性 名称(参数列表) : 返回类型

    示例: + getName() : String 表示 Java 中的 public String getName()。如果是静态方法,在 UML 中通常会带下划线。如果是抽象方法,方法名用斜体

接口的 UML 描述

接口 的类图表述与类大致相同,不同的是接口名要添加 Interface 标识,且行为的可见性必须用 "+" 表示。如下图:

image-20260317112706287

类之间的关系

类与类之间的 6 种关系(核心重点):

理解 UML 类图的关键在于看懂类与类之间的连线。在 Java 中,类之间的关系按照耦合度由弱到强,可以分为以下 6 种:

依赖关系

依赖关系 (Dependency) —— “用到”:

  • 含义:一种临时性的、弱的引用关系。类 A 的修改会影响到类 B。

  • UML 图形带箭头的虚线,箭头指向被依赖的类。

  • Java 体现:被依赖的类作为方法参数方法返回值或在方法内部作为局部变量被使用。

    java
    public class Driver {
      // Car 作为方法参数,Driver 依赖了 Car
      public void drive(Car car) {
        car.move();
      }
    }

关联关系

关联关系 (Association) —— “知道”:

  • 含义:一种结构化的、平等的、长期的关系。表示一个类“知道”另一个类的属性和方法。可以分为单向关联、双向关联和自关联。

  • UML 图形带普通箭头的实线(单向)或不带箭头的实线(双向)。

  • Java 体现:被关联的类作为另一个类的成员变量(全局变量)。

    java
    public class Employee {
      // IDCard 作为成员变量,Employee 关联了 IDCard
      private IDCard idCard;
    }

聚合关系

聚合关系 (Aggregation) —— “拥有 (Has-a) / 弱整体与部分”:

  • 含义:关联关系的特例,表示“整体与部分”的关系。但整体和部分是可以分离的,拥有各自独立的生命周期。

  • UML 图形带空心菱形的实线,空心菱形指向“整体”。

  • Java 体现:同样是成员变量,但通常通过构造器参数或 Setter 方法从外部传入(依赖注入)。

    java
    public class Computer {
      private Mouse mouse;
    
      // 鼠标从外部传入,电脑销毁了,鼠标依然可以独立存在并插到别的电脑上
      public void setMouse(Mouse mouse) {
        this.mouse = mouse;
      }
    }

组合关系

组合关系 (Composition) —— “包含 (Contains-a) / 强整体与部分”:

  • 含义:也是“整体与部分”的关系,但是一种强依赖。部分不能脱离整体单独存在,整体销毁,部分也跟着销毁(同生共死)。

  • UML 图形带实心菱形的实线,实心菱形指向“整体”。

  • Java 体现:也是成员变量,但通常在“整体”的内部(如构造器中)直接实例化“部分”。

    java
    public class Bird {
      private Wing wing;
    
      public Bird() {
        // 翅膀在鸟内部创建,鸟死了,翅膀也没了
        this.wing = new Wing();
      }
    }

继承关系

泛化关系 (Generalization) —— “是一个 (Is-a) / 继承”:

  • 含义:面向对象三大特性之一的继承关系。子类继承父类的所有属性和方法。

  • UML 图形带空心三角箭头的实线,箭头指向父类。

  • Java 体现:使用 extends 关键字。

    java
    public class Dog extends Animal { ... }

实现关系

实现关系 (Realization) —— “遵循契约”:

  • 含义:类实现了某个接口定义的所有抽象方法。

  • UML 图形带空心三角箭头的虚线,箭头指向接口。

  • Java 体现:使用 implements 关键字。

    java
    public class UserServiceImpl implements UserService { ... }

学习建议与实践

学习建议与实践:

在实际的 Java 开发中,我们很少会从零开始用画图工具去手绘一个极其庞大的系统类图。类图更多是作为一种交流和梳理思路的工具。

当你在 IntelliJ IDEA 中编写或阅读复杂的 Java 代码时,如果遇到难以理清的类继承树或引用关系,可以直接在项目目录结构或代码编辑器中右键点击类名,选择 Diagrams -> Show Diagram(快捷键通常是 Ctrl + Alt + Shift + U)。IDEA 会自动根据现有的 Java 代码逆向生成 UML 类图,这对于分析源码(如 Spring 框架或 JDK 集合类源码)是非常直观和高效的利器。

时序图

如果说类图(Class Diagram)是展示系统静态结构的“骨架”,那么时序图(Sequence Diagram,也叫顺序图) 就是展示系统动态运行过程的“血液”。

时序图主要用于描述对象之间传递消息的时间顺序。在实际的 Java 开发中,它被广泛用于梳理复杂的业务流程(例如:用户登录鉴权流程、支付接口调用链路、微服务间的 RPC 调用等)。

时序图的核心构成要素

时序图的核心构成要素:

时序图主要是由对象生命线激活框消息组成的。

对象与生命线

对象与生命线 (Object & Lifeline):

  • 概念:代表参与交互的具体实体。生命线是一条垂直的虚线,表示对象在一段时间内的存在。
  • 表示法:顶部是一个矩形框,里面写明对象名或类名(格式通常为 对象名: 类名,如果匿名可以写 :类名)。往下延伸的虚线就是生命线。
  • Java 体现:对应 Java 堆内存中被 new 出来的实例对象(如 UserServiceImpl 的实例、UserController 的实例)。

激活框 / 控制焦点

激活框 / 控制焦点 (Activation Box):

  • 概念:表示对象正在执行某个操作(即占用 CPU 时间)。
  • 表示法:生命线上的一个细长矩形。
  • Java 体现:对应 Java 中某个对象的方法正在被执行(该方法被压入线程的虚拟机栈中)。激活框的顶部是方法开始执行,底部是方法执行完毕并出栈。

消息

消息 (Messages) —— 对象间的沟通桥梁:

消息是时序图中最重要的部分,代表对象之间的通信。不同的箭头代表不同的调用方式:

  • 同步消息 (Synchronous Message)

  • 表示法实线 + 实心箭头

  • Java 体现:这是最常见的普通方法调用。调用者调用某个方法后,必须等待被调用者执行完毕并返回结果,才能继续执行后面的代码(阻塞式)。

  • 异步消息 (Asynchronous Message)

  • 表示法实线 + 空心(线型)箭头

  • Java 体现:调用者发送消息后,不等待响应即可继续执行后面的操作。例如:开启一个新线程 (new Thread().start())、向消息队列(RabbitMQ/Kafka)发送消息、或者调用了带有 @Async 注解的方法。

  • 返回消息 (Return Message)

  • 表示法虚线 + 空心(线型)箭头

  • Java 体现:对应 Java 中的 return 语句。表示被调用方法执行完毕,将控制权和返回值交还给调用者。(注:为了图表简洁,如果调用流向很清晰,返回消息有时会被省略)。

  • 自调用消息 (Self Message)

  • 表示法:箭头从生命线出发,绕一圈又指回同一个激活框。

  • Java 体现:对应 Java 中类内部的私有方法调用递归调用(即 this.methodName())。

逻辑控制:组合片段

逻辑控制:组合片段 (Combined Fragments):

Java 代码中充满了 if-elsefor/while 循环,时序图使用组合片段来表达这些控制流。

常见的交互操作符包括:

  1. alt (Alternative - 替代片段)

    • 作用:表示互斥的条件分支。
    • Java 体现:对应 if-elseswitch-case 语句。框内会被虚线分成上下两部分,标注不同的条件(如 [balance > 0][balance <= 0])。
  2. opt (Option - 选项片段)

    • 作用:表示满足条件才会执行的分支(没有 else 部分)。
    • Java 体现:对应单个 if 语句。
  3. loop (Loop - 循环片段)

    • 作用:表示重复执行的一段交互。
    • Java 体现:对应 forwhiledo-while 循环。通常会在左上角标注循环条件,如 [for each item in cart]

经典 Java 业务场景映射示例

经典 Java 业务场景映射示例:

假设我们要描述一个简单的“电商下单支付”流程,用 Java 代码和时序图的思维来对应:

Java 代码逻辑:

java
public class OrderController {
    private OrderService orderService;

    public Result placeOrder(OrderReq req) {
        // 1. 同步调用 Service
        boolean success = orderService.createOrder(req);
        if (success) {
            return Result.ok("下单成功");
        } else {
            return Result.fail("库存不足");
        }
    }
}

public class OrderService {
    private InventoryService inventoryService;
    private MQProducer mqProducer;

    public boolean createOrder(OrderReq req) {
        // 2. 自调用,校验参数
        this.validateParams(req);

        // 3. 同步调用,扣减库存
        boolean hasStock = inventoryService.deduct(req.getProductId());

        // 4. alt 组合片段 (if-else 逻辑)
        if (hasStock) {
            // 5. 异步消息,发送延迟队列检查支付状态
            mqProducer.sendAsyncMsg("order_created_topic", req.getOrderId());
            return true; // 6. 返回消息
        }
        return false; // 6. 返回消息
    }

    private void validateParams(OrderReq req) { /* ... */ }
}

在时序图中,这个过程会这样展现:

  1. 客户端OrderController 生命周期发起一个同步消息placeOrder)。

  2. OrderControllerOrderService 发起同步消息createOrder)。

  3. OrderService 自身的生命线上出现一个自调用消息validateParams)。

  4. OrderServiceInventoryService 发起同步消息deduct)。

  5. 接下来是一个 alt

    • [hasStock == true] 分支OrderServiceMQProducer 发送一条异步消息(带空心箭头的实线)。然后通过返回消息(虚线)将 true 沿原路返回。
    • [hasStock == false] 分支:直接通过返回消息返回 false

实际开发建议

实际开发建议:

在现代 Java 开发中,手动拖拽画时序图非常耗时。开发者通常喜欢使用代码即图表 (Docs as Code) 的方式。例如使用 PlantUMLMermaid 语法,通过写简单的脚本语言就能自动生成时序图,不仅方便维护,还能直接嵌入到 Markdown 文档中。同时,IntelliJ IDEA 也有诸如 SequenceDiagram 等插件,可以直接根据 Java 方法的调用链路自动生成时序图。

你需要我为你演示一段如何使用 PlantUML 语法来生成上述“下单支付”流程的时序图代码吗?

分隔-----------------------------------

类描述

「类」 在UML中通常以实线矩形框表示。矩形框中有若干分割线。分别表示类名、属性和方法。如下图所示:

img
  • 类名:图中最上面的矩形框中为类名。如果字体为斜体 ,表示为抽象类 。(图中的上面部分)
  • 属性:类名下边的区域。(图中的中间部分)
  • 方法:(图中的下面部分)

说明:属性和方法前面的“+”、“-”和“#”表示访问级别:

  • +:public
  • -:private
  • #:protected

类之间的关系

类之间有六种关系

  • 继承
  • 实现
  • 关联
  • 依赖
  • 组合
  • 聚合

img

继承

继承(Inherit) 是面向对象语言的三大特性(封装,继承,多态)之一。子类继承父类。

img

UML类图中继承关系使用空心三角形+实线表示。

实现

实现(Implement) 与继承类似,实现类继承接口中的方法。

img

UML类图中实现关系使用空心三角形+虚线表示。

关联

依赖关系通常表现为类的私有属性。

kotlin
 体验AI代码助手 代码解读复制代码// 企鹅类
public class Penguin {
  // 天气类
  private Climate climate;
}

其UML类图表示如下:

img

UML类图中关联使用实线箭头表示。

依赖

「依赖」 关系体现为局部变量、方法的形参,或者对静态方法的调用。

typescript
 体验AI代码助手 代码解读复制代码public class Programmer {
  public void work(Computer computer){

  }
}

img

UML类图中依赖关系使用虚线箭头表示。

以下代码展示了依赖关系的三种具体代码实现:局部变量、方法的形参和对静态方法的调用。

csharp
 体验AI代码助手 代码解读复制代码public class Person{
  public void doSomething1(){
    Car car = new Car();//局部变量
    ...
  }

  public void doSomething2(Car car){//方法参数
    ...
  }

  public void doSomething3(){
    int price = Car.do();//静态方法调用
  }
}

组合

「组合」 是关联关系的一种,表示一种强的“拥有”关系。体现了严格的部分和整体的关系。部分和整体的生命周期一样。

csharp
 体验AI代码助手 代码解读复制代码public class Bird {
  private Wing wing;
  public Bird() {
    this.wing = new Wing();
  }
}

img

UML类图中组合关系使用实心菱形+实线表示。

聚合

「聚合」 是关联关系的一种,表示一种弱的“拥有”关系。

用Java代码表示大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以有多只大雁。

天气凉了,树叶黄了。 。。。 一群大雁往南飞,一会排成“S”字,一会排成“B”字。 ——《秋天》出自人教版小学语文一年级课文

kotlin
 体验AI代码助手 代码解读复制代码public class WildGooseAggregate {
  private List<WildGoose> wideGooses;
}

img

UML类图中聚合关系使用空心菱形实线表示。

综合示例

前面介绍了类之间的6种关系。为了更好地理解这6种关系。下面使用一个完整的例子(汽车)。该示例中包含了这6种关系。

img

说明:

  • 车的类图结构为,表示车是一个抽象类;
  • 它有两个继承类:小汽车和自行车;它们之间的关系为**「实现」** 关系,使用带空心箭头的虚线表示;
  • 小汽车为与SUV之间也是**「继承」** 关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
  • 小汽车与发动机之间是**「组合」** 关系,使用带实心箭头的实线表示;
  • 学生与班级之间是**「聚合」** 关系,使用带空心箭头的实线表示;
  • 学生与身份证之间为**「关联」** 关系,使用一根实线表示;
  • 学生上学需要用到自行车,与自行车是一种**「依赖」** 关系,使用带箭头的虚线表示;