背景
我们在 [Java] 如何自动生成简单的 Mermaid 类图 一文中探讨过如何自动生成简单的 Mermaid 类图的问题。最近我经常使用 PlantUML 的插件来画类图,所有又探索了一下如何自动生成简单的 PlantUML 类图的问题。
虽说手动生成类图的过程有利于加深自己的理解,但是查看各个类/接口的信息毕竟比较麻烦,如果可以把生成类图的过程自动化,就可以大大提升画类图的效率了。
本文展示了我自己写的可以生成简单类图的 java 代码。文中展示了用它生成的以下类的类图
ArrayListLinkedListjava.util.Arrays$ArrayList(Arrays.asList(...)方法返回的是它的实例)List/Set/DequeHashMap/LinkedHashMap/ConcurrentHashMapSet12/SetN(Set.of(...)方法返回的是它们的实例)java.util.JumboEnumSet/java.util.RegularEnumSet(EnumSet.of(...)方法返回的是它们的实例)java.util.concurrent.ThreadPoolExecutor- 自己手写一些类/接口
代码
我写了如下的 java 代码,它可以自动生成简单的 PlantUML 类图。 请将以下代码保存为 ClassDiagramGenerator.java ⬇️
java
import java.lang.reflect.AccessFlag;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ClassDiagramGenerator {
private final Map<Class<?>, Class<?>[]> realizationRelations = new HashMap<>();
private final Map<Class<?>, Class<?>> inheritanceRelations = new HashMap<>();
private final Set<Class<?>> analyzedClasses = new LinkedHashSet<>();
private final Set<Class<?>> ignoredClasses = new HashSet<>();
private static final Class<Object> OBJECT_CLASS = Object.class;
private static final String IGNORE_OPTION = "-i";
private static final String USAGE = "Usage: java ClassDiagramGenerator [-i 'java.io.Serializable'] [-i 'java.lang.Cloneable'] 'java.util.ArrayList' 'java.util.LinkedList'";
public static void main(String[] args) {
ClassDiagramGenerator generator = new ClassDiagramGenerator();
generator.convert(args).forEach(generator::analyzeHierarchy);
generator.generateClassDiagram();
}
public ClassDiagramGenerator() {
ignoredClasses.add(OBJECT_CLASS);
}
private void ignoreClass(String className) {
try {
Class<?> clazz = Class.forName(className);
ignoredClasses.add(clazz);
} catch (ClassNotFoundException e) {
String message = String.format("Class with name=%s can't be found, please check!", className);
throw new RuntimeException(message);
}
}
/**
* Convert class name to the corresponding class object.
* The "-i" option (it means some class should be ignored) will be handled in this method.
*
* @param classNames give class names
* @return a list that contains corresponding class objects
*/
private List<Class<?>> convert(String[] classNames) {
int index = 0;
while (index < classNames.length && classNames[index].equals(IGNORE_OPTION)) {
if (index + 1 >= classNames.length) {
String hint = "Please refer to below usage and specify correct parameter with the '-i' option!";
throw new IllegalArgumentException(String.join(System.lineSeparator(), hint, USAGE));
}
ignoreClass(classNames[index + 1]);
index += 2;
}
if (index == classNames.length) {
String hint = "Please refer to below usage and specify at least ONE class name!";
throw new IllegalArgumentException(String.join(System.lineSeparator(), hint, USAGE));
}
List<Class<?>> classList = new ArrayList<>(classNames.length);
for (; index < classNames.length; index++) {
String className = classNames[index];
try {
Class<?> clazz = Class.forName(className);
classList.add(clazz);
} catch (ClassNotFoundException e) {
String message = String.format("Class with name=%s can't be found, please check!", className);
throw new RuntimeException(message);
}
}
return classList;
}
private void showIgnoredClasses() {
List<Class<?>> classes = ignoredClasses.stream().
filter(c -> c != OBJECT_CLASS).
toList();
if (!classes.isEmpty()) {
printAnEmptyLine();
System.out.println("legend left");
System.out.println("请注意: 以下类/接口在类图中被忽略了");
classes.forEach(c -> System.out.println("<i>" + c.getName() + "</i>"));
System.out.println("end legend");
}
}
/**
* Generate first line for PlantUML class diagram
*/
private void generateFirstLine() {
System.out.println("@startuml");
}
/**
* Generate last line for PlantUML class diagram
*/
private void generateLastLine() {
System.out.println("@enduml");
}
/**
* Generate main content in PlantUML class diagram
*/
private void generateClassDiagram() {
generateFirstLine();
printAnEmptyLine();
doGenerateClassDiagram();
showIgnoredClasses();
printAnEmptyLine();
generateLastLine();
}
private void printAnEmptyLine() {
System.out.println();
}
private void doGenerateClassDiagram() {
generateNames();
analyzedClasses.forEach(c -> {
if (inheritanceRelations.containsKey(c)) {
System.out.printf("%s <|-- %s%n", inheritanceRelations.get(c).getName(), c.getName());
}
if (realizationRelations.containsKey(c)) {
String type = c.isInterface() ? "<|--" : "<|..";
Arrays.stream(realizationRelations.get(c)).forEach(item ->
System.out.printf("%s %s %s%n", item.getName(), type, c.getName()));
}
});
}
/**
* This method generates names for
* 1. Enum classes
* 2. Annotation classes
* 3. Interfaces
* 4. Abstract classes
* 5. Other classes
*/
private void generateNames() {
Set<Class<?>> enums = analyzedClasses.stream()
.filter(Class::isEnum)
.collect(Collectors.toSet());
// note: each annotation will appear in "annotations" and "interfaces" and "abstractClasses"
Set<Class<?>> annotations = analyzedClasses.stream()
.filter(Class::isAnnotation)
.collect(Collectors.toSet());
// note: each interface will appear in "interfaces" and "abstractClasses"
Set<Class<?>> interfaces = analyzedClasses.stream()
.filter(Class::isInterface)
.collect(Collectors.toSet());
Set<Class<?>> abstractClasses = analyzedClasses.stream()
.filter(this::isAbstractClass)
.collect(Collectors.toSet());
generateEnumNames(enums);
generateAnnotationNames(annotations);
generateInterfaceNames(interfaces, annotations);
generateAbstractClassNames(abstractClasses, interfaces);
generateRemainingClassNames(List.of(enums, abstractClasses, interfaces));
}
private boolean isAbstractClass(Class<?> candidate) {
return candidate.accessFlags().contains(AccessFlag.ABSTRACT);
}
private void generateEnumNames(Set<Class<?>> enums) {
Consumer<Class<?>> enumDisplayer = (annotationItem) ->
System.out.println("enum " + annotationItem.getName());
enums.forEach(enumDisplayer);
}
private void generateAnnotationNames(Set<Class<?>> annotations) {
Consumer<Class<?>> interfaceDisplayer = (annotationItem) ->
System.out.println("annotation " + annotationItem.getName());
annotations.forEach(interfaceDisplayer);
}
private void generateInterfaceNames(Set<Class<?>> interfaces, Set<Class<?>> annotations) {
Consumer<Class<?>> interfaceDisplayer = (interfaceItem) ->
System.out.println("interface " + interfaceItem.getName());
interfaces.stream()
.filter(interfaceItem -> !annotations.contains(interfaceItem))
.forEach(interfaceDisplayer);
}
private void generateAbstractClassNames(Set<Class<?>> abstractClasses, Set<Class<?>> interfaces) {
Consumer<Class<?>> abstractClassDisplayer = (abstractClass) ->
System.out.println("abstract " + abstractClass.getName());
abstractClasses.stream()
.filter(abstractClass -> !interfaces.contains(abstractClass))
.forEach(abstractClassDisplayer);
}
private void generateRemainingClassNames(List<Set<Class<?>>> alreadyProcessedClasses) {
Predicate<Class<?>> alreadyProcessed = (clazz) ->
alreadyProcessedClasses.stream().anyMatch(classes -> classes.contains(clazz));
analyzedClasses.stream()
.filter(item -> !alreadyProcessed.test(item))
.map(Class::getName)
.forEach(className -> System.out.println("class " + className));
}
private void analyzeHierarchy(Class<?> currClass) {
if (!analyzedClasses.contains(currClass) && !ignoredClasses.contains(currClass)) {
analyzeSuperClass(currClass);
analyzeInterfaces(currClass);
analyzedClasses.add(currClass);
}
}
private void analyzeSuperClass(Class<?> currClass) {
Class<?> superclass = currClass.getSuperclass();
if (superclass == null || ignoredClasses.contains(superclass)) {
return;
}
analyzeHierarchy(superclass);
if (!inheritanceRelations.containsKey(currClass)) {
inheritanceRelations.put(currClass, superclass);
}
}
private void analyzeInterfaces(Class<?> currClass) {
Class<?>[] interfaces = currClass.getInterfaces();
for (Class<?> item : interfaces) {
analyzeHierarchy(item);
}
if (!realizationRelations.containsKey(currClass)) {
Class<?>[] filteredInterfaces = Arrays.stream(interfaces).
filter(c -> !ignoredClasses.contains(c)).
toArray(Class<?>[]::new);
realizationRelations.put(currClass, filteredInterfaces);
}
}
}
用以下命令可以编译 ClassDiagramGenerator.java
bash
javac ClassDiagramGenerator.java
注意事项
请注意,ClassDiagramGenerator 生成的类图中,不包含任何泛型信息,而且也不展示任何字段/方法。
使用方法
用以下命令可以编译 ClassDiagramGenerator.java
bash
javac ClassDiagramGenerator.java
编译后,我们会得到 ClassDiagramGenerator.class 文件。
ClassDiagramGenerator 的使用有如下两种方式 ⬇️
java ClassDiagramGenerator C1 C2 C3java ClassDiagramGenerator -i EC1 -i EC2 C1 C2 C3
其中
C1/C2/C3表示要分析的类/接口的全限定类名(不必恰好是3个,但是至少要指定一个)EC1/EC2表示要排除的类/接口的全限定类名(可以不排除任何类/接口,也可以排除若干个类/接口)
这样说还是比较抽象,下面举一些例子,来说明 ClassDiagramGenerator 的用法。
例子
例 1: 生成 ArrayList 的类图
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.ArrayList'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.RandomAccess
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.List
interface java.lang.Cloneable
interface java.io.Serializable
abstract java.util.AbstractList
abstract java.util.AbstractCollection
class java.util.ArrayList
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.AbstractCollection <|-- java.util.AbstractList
java.util.List <|.. java.util.AbstractList
java.util.AbstractList <|-- java.util.ArrayList
java.util.List <|.. java.util.ArrayList
java.util.RandomAccess <|.. java.util.ArrayList
java.lang.Cloneable <|.. java.util.ArrayList
java.io.Serializable <|.. java.util.ArrayList
@enduml
但是掘金目前不支持 puml 语言。我在 Intellij IDEA (Community Edition) 里安装了 PlantUML 的插件,用它可以看到这样的效果 ⬇️

如何忽略某些类/接口
有的用户并不关心 java.lang.Cloneable 和 java.io.Serializable,此时可以通过 -i 选项忽略指定的类/接口,具体的命令如下 ⬇️
bash
java ClassDiagramGenerator -i 'java.lang.Cloneable' -i 'java.io.Serializable' 'java.util.ArrayList'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.RandomAccess
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.List
abstract java.util.AbstractList
abstract java.util.AbstractCollection
class java.util.ArrayList
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.AbstractCollection <|-- java.util.AbstractList
java.util.List <|.. java.util.AbstractList
java.util.AbstractList <|-- java.util.ArrayList
java.util.List <|.. java.util.ArrayList
java.util.RandomAccess <|.. java.util.ArrayList
legend left
请注意: 以下类/接口在类图中被忽略了
<i>java.lang.Cloneable</i>
<i>java.io.Serializable</i>
end legend
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 2: 生成 LinkedList 的类图
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.LinkedList'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.Deque
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.Queue
interface java.util.List
interface java.lang.Cloneable
interface java.io.Serializable
abstract java.util.AbstractList
abstract java.util.AbstractSequentialList
abstract java.util.AbstractCollection
class java.util.LinkedList
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.AbstractCollection <|-- java.util.AbstractList
java.util.List <|.. java.util.AbstractList
java.util.AbstractList <|-- java.util.AbstractSequentialList
java.util.Collection <|-- java.util.Queue
java.util.Queue <|-- java.util.Deque
java.util.SequencedCollection <|-- java.util.Deque
java.util.AbstractSequentialList <|-- java.util.LinkedList
java.util.List <|.. java.util.LinkedList
java.util.Deque <|.. java.util.LinkedList
java.lang.Cloneable <|.. java.util.LinkedList
java.io.Serializable <|.. java.util.LinkedList
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

如果想忽略 java.lang.Cloneable 和 java.io.Serializable,可以使用 -i 选项,具体的命令如下 ⬇️
bash
java ClassDiagramGenerator -i 'java.lang.Cloneable' -i 'java.io.Serializable' 'java.util.LinkedList'
运行结果如下 ⬇️
puml
@startuml
interface java.util.Deque
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.Queue
interface java.util.List
abstract java.util.AbstractList
abstract java.util.AbstractSequentialList
abstract java.util.AbstractCollection
class java.util.LinkedList
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.AbstractCollection <|-- java.util.AbstractList
java.util.List <|.. java.util.AbstractList
java.util.AbstractList <|-- java.util.AbstractSequentialList
java.util.Collection <|-- java.util.Queue
java.util.Queue <|-- java.util.Deque
java.util.SequencedCollection <|-- java.util.Deque
java.util.AbstractSequentialList <|-- java.util.LinkedList
java.util.List <|.. java.util.LinkedList
java.util.Deque <|.. java.util.LinkedList
legend left
请注意: 以下类/接口在类图中被忽略了
<i>java.lang.Cloneable</i>
<i>java.io.Serializable</i>
end legend
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 3: 生成 java.util.Arrays$ArrayList 的类图
调用 Arrays.asList(...) 方法,得到的是 java.util.Arrays$ArrayList 的实例。
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.Arrays$ArrayList'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.RandomAccess
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.List
interface java.io.Serializable
abstract java.util.AbstractList
abstract java.util.AbstractCollection
class java.util.Arrays$ArrayList
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.AbstractCollection <|-- java.util.AbstractList
java.util.List <|.. java.util.AbstractList
java.util.AbstractList <|-- java.util.Arrays$ArrayList
java.util.RandomAccess <|.. java.util.Arrays$ArrayList
java.io.Serializable <|.. java.util.Arrays$ArrayList
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

请注意上图中的 ArrayList 是 java.util.Arrays 中的一个嵌套类,而不是我们平时常用的 java.util.ArrayList
例 4: 生成 List/Set/Deque 的类图
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.List' 'java.util.Set' 'java.util.Deque'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.Set
interface java.util.Deque
interface java.lang.Iterable
interface java.util.Collection
interface java.util.SequencedCollection
interface java.util.Queue
interface java.util.List
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|-- java.util.SequencedCollection
java.util.SequencedCollection <|-- java.util.List
java.util.Collection <|-- java.util.Set
java.util.Collection <|-- java.util.Queue
java.util.Queue <|-- java.util.Deque
java.util.SequencedCollection <|-- java.util.Deque
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 5: 生成 HashMap/LinkedHashMap/ConcurrentHashMap 的类图
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.HashMap' 'java.util.LinkedHashMap' 'java.util.concurrent.ConcurrentHashMap'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.Map
interface java.util.SequencedMap
interface java.util.concurrent.ConcurrentMap
interface java.lang.Cloneable
interface java.io.Serializable
abstract java.util.AbstractMap
class java.util.HashMap
class java.util.LinkedHashMap
class java.util.concurrent.ConcurrentHashMap
java.util.Map <|.. java.util.AbstractMap
java.util.AbstractMap <|-- java.util.HashMap
java.util.Map <|.. java.util.HashMap
java.lang.Cloneable <|.. java.util.HashMap
java.io.Serializable <|.. java.util.HashMap
java.util.Map <|-- java.util.SequencedMap
java.util.HashMap <|-- java.util.LinkedHashMap
java.util.SequencedMap <|.. java.util.LinkedHashMap
java.util.Map <|-- java.util.concurrent.ConcurrentMap
java.util.AbstractMap <|-- java.util.concurrent.ConcurrentHashMap
java.util.concurrent.ConcurrentMap <|.. java.util.concurrent.ConcurrentHashMap
java.io.Serializable <|.. java.util.concurrent.ConcurrentHashMap
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

如果想忽略 java.lang.Cloneable 和 java.io.Serializable,可以使用 -i 选项,具体的命令如下 ⬇️
bash
java ClassDiagramGenerator -i 'java.lang.Cloneable' -i 'java.io.Serializable' 'java.util.HashMap' 'java.util.LinkedHashMap' 'java.util.concurrent.ConcurrentHashMap'
运行结果如下 ⬇️
puml
@startuml
interface java.util.Map
interface java.util.SequencedMap
interface java.util.concurrent.ConcurrentMap
abstract java.util.AbstractMap
class java.util.HashMap
class java.util.LinkedHashMap
class java.util.concurrent.ConcurrentHashMap
java.util.Map <|.. java.util.AbstractMap
java.util.AbstractMap <|-- java.util.HashMap
java.util.Map <|.. java.util.HashMap
java.util.Map <|-- java.util.SequencedMap
java.util.HashMap <|-- java.util.LinkedHashMap
java.util.SequencedMap <|.. java.util.LinkedHashMap
java.util.Map <|-- java.util.concurrent.ConcurrentMap
java.util.AbstractMap <|-- java.util.concurrent.ConcurrentHashMap
java.util.concurrent.ConcurrentMap <|.. java.util.concurrent.ConcurrentHashMap
legend left
请注意: 以下类/接口在类图中被忽略了
<i>java.lang.Cloneable</i>
<i>java.io.Serializable</i>
end legend
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 6: 生成 java.util.ImmutableCollections$Set12 和 java.util.ImmutableCollections$SetN 的类图
当我们调用 Set.of(...) 方法时,会得到以下两个类的实例 ⬇️
java.util.ImmutableCollections$Set12java.util.ImmutableCollections$SetN
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.ImmutableCollections$Set12' 'java.util.ImmutableCollections$SetN'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.Set
interface java.lang.Iterable
interface java.util.Collection
interface java.io.Serializable
abstract java.util.ImmutableCollections$AbstractImmutableCollection
abstract java.util.AbstractCollection
abstract java.util.ImmutableCollections$AbstractImmutableSet
class java.util.ImmutableCollections$Set12
class java.util.ImmutableCollections$SetN
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.AbstractCollection <|-- java.util.ImmutableCollections$AbstractImmutableCollection
java.util.Collection <|-- java.util.Set
java.util.ImmutableCollections$AbstractImmutableCollection <|-- java.util.ImmutableCollections$AbstractImmutableSet
java.util.Set <|.. java.util.ImmutableCollections$AbstractImmutableSet
java.util.ImmutableCollections$AbstractImmutableSet <|-- java.util.ImmutableCollections$Set12
java.io.Serializable <|.. java.util.ImmutableCollections$Set12
java.util.ImmutableCollections$AbstractImmutableSet <|-- java.util.ImmutableCollections$SetN
java.io.Serializable <|.. java.util.ImmutableCollections$SetN
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 7: 生成 java.util.JumboEnumSet 和 java.util.RegularEnumSet 的类图
当我们调用 EnumSet.of(...) 方法时,会得到以下两个类的实例 ⬇️
java.util.JumboEnumSetjava.util.RegularEnumSet
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.JumboEnumSet' 'java.util.RegularEnumSet'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.util.Set
interface java.lang.Iterable
interface java.util.Collection
interface java.lang.Cloneable
interface java.io.Serializable
abstract java.util.EnumSet
abstract java.util.AbstractSet
abstract java.util.AbstractCollection
class java.util.JumboEnumSet
class java.util.RegularEnumSet
java.lang.Iterable <|-- java.util.Collection
java.util.Collection <|.. java.util.AbstractCollection
java.util.Collection <|-- java.util.Set
java.util.AbstractCollection <|-- java.util.AbstractSet
java.util.Set <|.. java.util.AbstractSet
java.util.AbstractSet <|-- java.util.EnumSet
java.lang.Cloneable <|.. java.util.EnumSet
java.io.Serializable <|.. java.util.EnumSet
java.util.EnumSet <|-- java.util.JumboEnumSet
java.util.EnumSet <|-- java.util.RegularEnumSet
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 8: 生成 ThreadPoolExecutor 的类图
命令
请运行以下命令以生成对应的内容 ⬇️
bash
java ClassDiagramGenerator 'java.util.concurrent.ThreadPoolExecutor'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface java.lang.AutoCloseable
interface java.util.concurrent.ExecutorService
interface java.util.concurrent.Executor
abstract java.util.concurrent.AbstractExecutorService
class java.util.concurrent.ThreadPoolExecutor
java.util.concurrent.Executor <|-- java.util.concurrent.ExecutorService
java.lang.AutoCloseable <|-- java.util.concurrent.ExecutorService
java.util.concurrent.ExecutorService <|.. java.util.concurrent.AbstractExecutorService
java.util.concurrent.AbstractExecutorService <|-- java.util.concurrent.ThreadPoolExecutor
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

例 9: 自己手写一些类/接口,用 ClassDiagramGenerator 生成类图
请将以下代码保存为 E.java
java
class A {}
class B extends A {}
class C extends B implements I1, I2, I3 {}
class D extends C implements I1, I4 {}
interface I1 {}
interface I2 {}
interface I3 {}
interface I4 {}
public class E extends D implements I1, I4 { }
命令
用如下的命令可以编译 E.java 并为 E 生成类图
bash
javac E.java
java -cp . ClassDiagramGenerator 'E'
运行结果
运行结果如下 ⬇️
puml
@startuml
interface I1
interface I4
interface I3
interface I2
class A
class B
class C
class D
class E
A <|-- B
B <|-- C
I1 <|.. C
I2 <|.. C
I3 <|.. C
C <|-- D
I1 <|.. D
I4 <|.. D
D <|-- E
I1 <|.. E
I4 <|.. E
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

如果想忽略掉某些类/接口,例如想忽略 B 和 I4,那么可以使用如下的命令 ⬇️
bash
java -cp . ClassDiagramGenerator -i 'B' -i 'I4' 'E'
运行结果如下 ⬇️
puml
@startuml
interface I2
interface I1
interface I3
class C
class D
class E
I1 <|.. C
I2 <|.. C
I3 <|.. C
C <|-- D
I1 <|.. D
D <|-- E
I1 <|.. E
legend left
请注意: 以下类/接口在类图中被忽略了
<i>I4</i>
<i>B</i>
end legend
@enduml
借助 PlantUML 的插件,看到的效果如下 ⬇️

参考资料
版权问题
本文所展示的代码都是我自己写的(或者是用我写的代码生成的),读者朋友可以自由使用(包括复制粘贴,以及修改后再发布),不必通知我。