[Java] 如何自动生成简单的 PlantUML 类图

背景

我们在 [Java] 如何自动生成简单的 Mermaid 类图 一文中探讨过如何自动生成简单的 Mermaid 类图的问题。最近我经常使用 PlantUML 的插件来画类图,所有又探索了一下如何自动生成简单的 PlantUML 类图的问题。

虽说手动生成类图的过程有利于加深自己的理解,但是查看各个类/接口的信息毕竟比较麻烦,如果可以把生成类图的过程自动化,就可以大大提升画类图的效率了。

本文展示了我自己写的可以生成简单类图的 java 代码。文中展示了用它生成的以下类的类图

  1. ArrayList
  2. LinkedList
  3. java.util.Arrays$ArrayListArrays.asList(...) 方法返回的是它的实例
  4. List/Set/Deque
  5. HashMap/LinkedHashMap/ConcurrentHashMap
  6. Set12/SetNSet.of(...) 方法返回的是它们的实例
  7. java.util.JumboEnumSet/java.util.RegularEnumSet (EnumSet.of(...) 方法返回的是它们的实例)
  8. java.util.concurrent.ThreadPoolExecutor
  9. 自己手写一些类/接口

代码

我写了如下的 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 C3
  • java 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.Cloneablejava.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.Cloneablejava.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 的插件,看到的效果如下 ⬇️

请注意上图中的 ArrayListjava.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.Cloneablejava.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$Set12java.util.ImmutableCollections$SetN 的类图

当我们调用 Set.of(...) 方法时,会得到以下两个类的实例 ⬇️

  • java.util.ImmutableCollections$Set12
  • java.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.JumboEnumSetjava.util.RegularEnumSet 的类图

当我们调用 EnumSet.of(...) 方法时,会得到以下两个类的实例 ⬇️

  • java.util.JumboEnumSet
  • java.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 的插件,看到的效果如下 ⬇️

如果想忽略掉某些类/接口,例如想忽略 BI4,那么可以使用如下的命令 ⬇️

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 的插件,看到的效果如下 ⬇️

参考资料

版权问题

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

相关推荐
Edward111111111 小时前
3月23Math类,Arrays类
java·学习
小江的记录本1 小时前
【Spring Boot】Spring Boot 全体系知识结构化拆解(附 Spring Boot 高频面试八股文精简版)
java·spring boot·后端·spring·面试·tomcat·mybatis
ETA81 小时前
流式背后的状态机:深入解析 AI Agent 的核心循环机制
后端·源码
Thomas.Sir1 小时前
从底层源码深入剖析 MyBatis 工作原理
java·架构·mybatis
九天轩辕2 小时前
Android CI/CD 编译 AIDL 报错分析与解决
android·java·ci/cd
在屏幕前出油2 小时前
04. FastAPI——响应类型
开发语言·后端·python·pycharm·fastapi
码农4272 小时前
点评项目深入改造-------日常学习笔记
java·笔记·学习·搜索引擎·全文检索
Ivanqhz2 小时前
寄存器分配的核心函数 allocate
java·开发语言·后端·python·rust
爱吃烤鸡翅的酸菜鱼2 小时前
Spring Cloud Eureka 服务注册与发现实战详解:从原理到高可用集群搭建
java·spring·spring cloud·eureka