一、UML类图概述在软件工程中UML类图是描述 系统 静态结构的标准建模语言而类之间的六种关系是理解系统设计意图的关键。无论是阅读现有系统源码还是进行新功能的设计开发准确识别类之间的关系都能显著提升代码质量和可维护性。二、速览UML类图基础类图其实很简单主要包含三部分类名这个类有什么属性这个类有什么数据方法这个类能做什么在所有类图形式中常用的三种是普通类、抽象类和接口。它们的表示方式略有差异但结构一致。1. 普通类Class普通类是系统中最基本的构成单元可以直接 实例 化。UML类图示例Java代码示例public class Student { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id id; } public void study() { System.out.println(Studying...); } }2. 抽象类Abstract Class抽象类是一种不能被实例化的类通常作为父类被子类继承。它可以包含普通方法也可以包含抽象方法。UML类图示例Java代码示例public abstract class Person { protected String name; protected int age; public void speak() { System.out.println(Person speaking...); } public abstract void work(); }3. 接口Interface接口表示一组行为能力是一种完全抽象的类型。接口不包含实现只规定方法签名。从 JDK 8 开始接口可以包含普通方法default 方法static 方法UML类图示例除了上图中常见的 interface 类风格画法外UML 还允许使用更简洁的棒棒糖符号Lollipop Notation 来表示接口Java代码示例public interface Movable { void move(); }UML类图不仅有这三类还包括枚举Enum泛型类模板类Template Class元类Meta Class但使用频率远不如普通类、抽象类、接口因此本篇只介绍最常见的三种即可4. UML类图中的表示规范补充(1) 抽象方法与抽象类的表示抽象方法的标准表示方式是在类图中将操作的名称以斜体显示。根据 UML 2.5.1 规范第 9.6.4 节Operations Notation“An abstract operation or property is denoted by writing the name of the operation or property initalics.”规范明确指出需要将操作的名称以斜体表示。在实际应用中整个操作签名包括方法名、参数和返回类型通常以斜体显示以保持 视觉 一致性。说明抽象方法优先以斜体表示但也可以在操作签名后添加{abstract}注解。抽象类的名称同样优先以斜体显示。根据 UML 2.5.1 规范第 9.2.4.1 节Classifier Notation“The name of an abstract Classifier is shown initalics, where permitted by the font in use.”作为备选或补充可以在类名后或下方使用文本注解{abstract}即“Alternatively or in addition, an abstract Classifier may be shown using the textual annotation{abstract}after or below its name.”说明文献写{abstract}是规范允许的文本注解实际绘图中通常用刻板印象abstract。类名斜体和{abstract}/abstract都表示抽象类。(2) 静态方法与构造方法的表示静态方法的标准表示方式是将特征名称包括方法名、参数和返回类型整体下划线。根据 UML 2.5.1 规范第 9.4.4 节Static Features Notation“Static features are underlined in the compartments in which they appear.”下划线覆盖整个特征名称而不是仅在方法名前添加下划线前缀。作为备选可以在操作签名后添加{static}关键字即“An Operation may be specified as static. This is shown by writing{static}after the Operation signature.”关于构造方法UML 规范中没有强制要求在类图中显示构造方法。通常情况下默认的无参数构造方法可以省略显示因为它在大多数 编程语言 中是隐式存在的。只有当构造方法具有特殊参数、多个重载形式或在设计中具有重要意义时才需要在类图中明确表示。(3) 接口的表示方式接口的表示必须在名称上方标注关键字«interface»。根据 UML 2.5.1 规范第 10.4.4 节Interface Notation“An Interface may be designated using the default notation for Classifier with the keyword «interface».”仅通过斜体接口名称而不添加«interface»关键字不能明确表示接口因为这无法与抽象类有效区分。接口有两种 主要的 表示方式分类器矩形表示法在名称上方标注«interface»关键字可以显示接口的所有操作棒棒糖Lollipop表示法接口表示为一个圆圈球体通常称为棒棒糖圆圈旁标注接口名称在棒棒糖表示法中接口通常不列出具体操作而是通过实线连接到实现该接口的分类器接口名称可以选择性地以斜体显示以强调其抽象性质但这不是必需的因为接口的抽象性主要通过«interface»关键字来标识。(4) 可见性的表示对于具有包可见性default/package visibility的特征可以使用符号~来明确表示可见性也可以省略可见性符号。在 UML 规范中省略可见性符号表示该特征具有包可见性因此对于包可见性的特征既可以明确写出~也可以不写任何可见性符号两者含义相同。三、六大关系详解在 UML 类图中类与类之间最常见的关系一共有 六种。每种关系都表示不同程度的“耦合”或“依赖”从最弱到最强关系如下1. 依赖Dependency含义A 用一下 B比如方法参数、方法返回值、方法局部变量使用到了B图形表示虚线箭头 由依赖者指向被依赖者UML类图示例Java代码示例class Printer { void print(String content) { System.out.println(content); } } class Document { String text; Document(String text) { this.text text; } String getText() { return text; } } class Logger { void log(String msg) { System.out.println(Log: msg); } } class Office { Document processDocument(Document doc, Printer printer) { // 方法参数依赖 Printer 和 Document new Logger().log(Processing document); // 局部变量依赖 Logger printer.print(doc.getText()); return new Document(Processed: doc.getText()); // 返回值依赖 Document } }如果是双向依赖的情况首尾连接处需画两个箭头但是这种情况极其少见且不推荐2. 关联Association含义A 和 B 有长期的关系A 拥有 B 的引用如成员变量生命周期独立图形表示实线箭头 由关联类指向被关联类UML类图示例Java代码示例class Address { String city; Address(String city) { this.city city; } } class Person { Address address; // Person 持有 Address }有时会存在特殊的双向关联情况类 A 持有类 B 的引用同时类 B 也持有类 A 的引用如下UML类图示例也可画双向箭头Java代码示例class Person { Address address; // Person 持有 Address } class Address { Person person; // Address 持有 Person }接下来会介绍聚合(Aggregation) 和组合(Composition)需要说明的是聚合和组合关系实际上是特殊的关联关系关联Association ← 最宽泛├─ 聚合Aggregation ← 整体-部分部分可独立└─ 组合Composition ← 整体-部分部分不可独立聚合和组合一定是关联关系但是关联不一定是聚合或组合关系3. 聚合Aggregation含义A 拥有 B 的引用如成员变量B 可以独立存在生命周期相互独立图形表示实线 空心菱形 由部分指向整体UML类图示例Java代码示例class Student { String name; Student(String name) { this.name name; } } class School { ListStudent students; // 学校聚合学生 void addStudent(Student s) { if (students null) students new ArrayList(); students.add(s); } } // 为什么这是“聚合” // 1. Student 对象不是 School 创建的而是外部传进来的。 // 2. Student 即使不属于 School 也能单独存在比如学生可以没有学校也能作为对象存在。 // 3. School 只是“拥有学生列表”一种“整体-部分但不强绑定”的关系。 // 因此学生属于学校但学生可以独立存在 → 这是“聚合”。4. 组合Composition含义A 拥有 B 的引用如成员变量B 随 A 的生命周期消失无法独立存在图形表示实线 实心菱形 由部分指向整体UML类图示例Java代码示例class Engine { Engine() { System.out.println(Engine created); } } class Car { private Engine engine new Engine(); // 引擎组合在汽车中 Car() { System.out.println(Car created); } } // 为什么这是“组合” // 1. Engine 是 Car 自己在内部创建的而不是外部传进来的。 // 2. Engine 的存在完全依赖 Car脱离汽车就没有意义比如单独的引擎对象没有用途。 // 3. Car 和 Engine 是“整体-部分中最紧密”的关系汽车创建引擎就被创建。 // 因此部分Engine不能脱离整体Car → 这是“组合”。提醒需要注意的是聚合和组合作为特殊的关联关系在图形表示上分别在整体端包含端使用空心菱形和实心菱形来标识。至于另一端部分端是否需要画箭头则取决于是否需要明确表达导航方向。无论是聚合关系还是组合关系从整体到部分的导航性都是关系本身固有的语义因为整体都需要通过引用来访问部分对象。因此在部分端画箭头可以更明确地表达这种导航关系但由于这种导航性在聚合和组合关系的语义中都已经存在画箭头都是可选的可以根据需要明确导航方向时使用不画箭头也是符合规范的。这种处理方式在聚合关系和组合关系中是统一的因为两种关系在导航性的语义上具有相同的特性整体都需要通过某种引用机制来访问其组成部分无论这种拥有关系是弱的聚合关系还是强的组合关系。在 UML 中依赖Dependency与关联/组合/聚合Association/Composition/Aggregation是完全不同的两类关系判断规则也完全不一样。依赖关系看的是“使用”只要一个类在代码里出现过另一个类型的名字包括变量类型、参数类型、返回值、new 的右边就表示它依赖该类型。依赖是最弱的关系属于“用到就算”。组合/聚合看的是“结构”它们映射的是类的内部组成关系因此只根据**成员变量的声明类型左边**来判断不看 new 时实际创建的子类。因为 UML 描述的是静态设计结构而不是运行时对象。5. 泛化Generalization含义子类继承父类图形表示实线空心三角箭头 由子类指向父类UML类图示例Java代码示例class Animanl { } class Dog extends Animal { } class Cat extends Animal { }6. 实现Realization含义类实现接口图形表示虚线空心三角箭头 由实现类指向接口UML类图示例Java代码示例interface Runnable { } class Task implements Runnable { }java运行12四、总结在 UML 类图中六种关系从 弱到强 的顺序通常为依赖 → 关联 → 聚合 → 组合 → 泛化继承 → 实现画类图时没有硬性规定必须用越强越好整体原则是尽量按真实业务关系选择最准确的那一种能确定整体–部分就不要只画关联而应画聚合或组合能确定生命周期绑定就画组合不能确定则画聚合无法判断是否是整体–部分时再画普通关联仅在方法中短暂使用到对方时使用最弱的依赖最后需要强调聚合与组合本质上都属于关联的更精确形式一旦确认某关系是聚合/组合就应该直接画它们而不是退回画成普通关联通过以上原则就能让你的类图相对更加准确UML 类图及六大关系详解继承、实现、依赖、关联、聚合、组合Java类图-CSDN博客