我们在进行源码学习的时候,经常要查看某个类的类图,虽然 IDEA 有自带的类图显示,但那也只是付费版,社区版的并不支持,而 Eclipse 是压根儿就没有,其实功能挺简单的,于是就自己写了一个查看类图小工具给分享给大家。
先放效果图:
从这效果图中,我们就很清晰的看到,ArrayList 的父类是 AbstractList,父接口有 List、RandomAccess、Cloneable、Serializable。而父类 AbstractList 的父类是 AbstractCollection,它实现的接口同样有List,以此类推。
当然,我们用起来也比较简单,只需要调用工具类 showDiagram() 方法,把要查看的类传进去即可。
DiagramUtils.showDiagram(ArrayList.class, true);
如果你想要查看接口或类的简名,只需要把第二个参数改为 false 即可(效果图如下):
【源码展示】
package com.test.inherited;
import java.util.ArrayList;
import java.util.List;
/**
* 类图查看工具类
*
* @author zyq
* @since 2021/08/18
*/
public class DiagramUtils {
private static List<String> list = new ArrayList<>();
public static void main(String[] args) {
DiagramUtils.showDiagram(ArrayList.class, false);
}
/**
* 打印类或接口的类图关系
*
* @param clazz 类或接口
* @param isShowFullName 是否显示类全名,true-显示类全名,false-显示简名
*/
public static void showDiagram(Class clazz, boolean isShowFullName) {
if (clazz == null) {
System.out.println("你没有传参");
}
Node node = getSupper(clazz, isShowFullName);
if (node != null) {
print(node, 0);
}
handleAll();
for (int i = list.size() - 1; i >= 0; i--) {
System.out.println(list.get(i).substring(4));
}
}
private static void handleAll() {
// 获取最大的层级
int max = 1;
for (int i = list.size() - 1; i > 0; i--) {
int level = getLevel(list.get(i));
if (max < level) {
max = level;
}
}
// 循环处理每层树状
for (int i = max; i > 0; i--) {
handle(i);
}
}
private static void handle(int level) {
// 获取该层次的所有序号
List<Integer> rowList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (level == getLevel(list.get(i))) {
rowList.add(i);
}
}
if (rowList.size() <= 1) {
return;
}
// 判断两个依次同层级的是否为连续
int i = 0;
while (i < rowList.size() - 1) {
int num1 = rowList.get(i);
int num2 = rowList.get(i + 1);
i++;
// 相邻的直接略过
if (num1 + 1 == num2) {
continue;
}
// 判断两行之间是否有阻碍,没有阻碍才连续
boolean isOk = true;
int levelIndex = level * 4;
for (int j = num1; j < num2; j++) {
String s = list.get(j);
if (s.length() > levelIndex) {
char c = s.charAt(levelIndex);
if (c != ' ' && c != '|') {
isOk = false;
}
}
}
if (isOk) {
for (int j = num1; j < num2; j++) {
list.set(j, setLevel(list.get(j), level));
}
}
}
}
private static String setLevel(String s, int level) {
int levelIndex = level * 4;
if (s.length() > levelIndex) {
char[] chars = s.toCharArray();
chars[levelIndex] = '|';
return new String(chars);
} else {
int diff = levelIndex - s.length();
for (int i = 0; i < diff - 1; i++) {
s += " ";
}
return s + "|";
}
}
private static int getLevel(String s) {
String start = "|---";
int level = 0;
while (level < 100) {
level ++;
start = " " + start;
if (s.startsWith(start)) {
break;
}
}
return level;
}
private static void print(Node node, int level) {
String lineBlank = "";
for (int i = level; i > 0; i--) {
lineBlank += " ";
}
String s = lineBlank + "|---" + node.getName() + " " + node.getType();
list.add(s);
List<Node> superList = node.getSuperList();
if (superList.size() > 0) {
for (Node each : superList) {
print(each, level + 1);
}
}
}
private static Node getSupper(Class clazz, boolean isShowFullName) {
Node node = new Node();
if (clazz.isInterface()) {
node.setType("(I)");
} else {
node.setType("(C)");
}
node.setName(getClassName(clazz, isShowFullName));
Class superclass = clazz.getSuperclass();
if (superclass != null) {
node.getSuperList().add(getSupper(superclass, isShowFullName));
}
Class[] interfaces = clazz.getInterfaces();
if (interfaces != null && interfaces.length > 0) {
for (Class anInterface : interfaces) {
node.getSuperList().add(getSupper(anInterface, isShowFullName));
}
}
return node;
}
private static String getClassName(Class clazz, boolean isShowFullName) {
return isShowFullName ? clazz.getName() : clazz.getSimpleName();
}
private static class Node {
private String name;
private String type;
private List<Node> superList = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Node> getSuperList() {
return superList;
}
}
}