IntelliJ IDEA类信息查询及插件中程序化获取类信息方法
关于IntelliJ IDEA类信息查询与插件开发的解决方案
嘿,我来帮你拆解这两个问题——先讲在IDEA里手动查找类相关信息的方法,再教你怎么在插件里通过代码获取这些数据,包括你给出的actionPerformed方法该怎么修改~
一、在IntelliJ IDEA中手动查找类相关信息
如果你只是手动查看类的信息,IDEA自带了超多便捷工具:
- 快速定位类本身:按下
Ctrl+N(Windows/Linux)或Cmd+O(Mac),输入类名就能直接跳转到类文件,还能看到类的继承/实现层级(点击类名旁的小箭头展开)。 - 查看类的结构(方法/字段):打开类文件后,按下
Alt+7(Windows/Linux)或Cmd+7(Mac)调出Structure面板,这里能看到类的所有方法、字段、内部类,还能按类型筛选,点击就能直接跳转到对应位置。 - 查找类的所有引用:右键类名选择「Find Usages」(或按下
Alt+F7),能看到整个项目里哪里用到了这个类,包括继承、调用、实例化等场景,还能过滤引用类型。 - 获取代码统计数据:用IDEA自带的Code Metrics功能,右键类文件选择「Analyze > Calculate Code Metrics」,就能得到类的代码行数、方法数量、圈复杂度等统计数据。
二、通过IntelliJ IDEA插件编程获取类的详细信息
你要开发插件的话,核心是用IntelliJ的PSI(Program Structure Interface)——这是平台用来解析代码结构的核心API,能让你直接操作类、方法、字段等代码元素。先修正你给出的代码里的小问题:event.getClass()获取的是AnActionEvent类,不是用户选中的目标类,我们需要先拿到用户选中的类元素,再分析它的信息。
第一步:修改actionPerformed方法,获取目标类
先从事件中提取用户选中的类(支持选中类节点或打开类文件两种场景):
import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.ui.Messages; import com.intellij.psi.*; public class ClassInfoAction extends AnAction { @Override public void actionPerformed(AnActionEvent event) { Project project = event.getProject(); if (project == null) return; // 尝试获取选中的PSI元素 PsiElement selectedElement = CommonDataKeys.PSI_ELEMENT.getData(event.getDataContext()); PsiClass targetClass = null; // 场景1:用户选中了类节点或类中的元素 if (selectedElement instanceof PsiClass) { targetClass = (PsiClass) selectedElement; } // 场景2:用户打开了Java文件,选中的是文件或其中的非类元素 else { PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(event.getDataContext()); if (psiFile instanceof JavaPsiFile) { PsiClass[] classes = ((JavaPsiFile) psiFile).getClasses(); if (classes.length > 0) { targetClass = classes[0]; // 取文件中的第一个类 } } } if (targetClass == null) { Messages.showInfoMessage(project, "请选中一个Java类或打开Java文件", "提示"); return; } // 调用分析方法获取类信息 analyzeClassDetails(project, targetClass); } }
第二步:实现类信息分析方法analyzeClassDetails
这个方法会获取你需要的所有数据:类的代码行数、方法数量与名称、每个方法的行数,还有方法引用(用于分析耦合/内聚):
private void analyzeClassDetails(Project project, PsiClass targetClass) { StringBuilder result = new StringBuilder(); String className = targetClass.getQualifiedName() != null ? targetClass.getQualifiedName() : targetClass.getName(); result.append("类信息:").append(className).append("\n\n"); // 1. 类的代码行数(精确到类本身的范围) PsiFile containingFile = targetClass.getContainingFile(); if (containingFile != null) { TextRange classTextRange = targetClass.getTextRange(); String classContent = containingFile.getText().substring(classTextRange.getStartOffset(), classTextRange.getEndOffset()); int classLineCount = classContent.split("\n").length; result.append("类本身的代码行数:").append(classLineCount).append("\n"); result.append("类所在文件的总行数:").append(containingFile.getText().split("\n").length).append("\n\n"); } // 2. 方法的数量、名称与每个方法的代码行数 PsiMethod[] methods = targetClass.getMethods(); result.append("方法总数:").append(methods.length).append("\n"); for (PsiMethod method : methods) { // 跳过构造方法(如果不需要可以去掉这个判断) if (method.isConstructor()) continue; TextRange methodRange = method.getTextRange(); String methodContent = containingFile.getText().substring(methodRange.getStartOffset(), methodRange.getEndOffset()); int methodLineCount = methodContent.split("\n").length; result.append("- 方法名:").append(method.getName()).append(",行数:").append(methodLineCount).append("\n"); } result.append("\n"); // 3. 方法引用分析(耦合与内聚) result.append("方法调用关系分析:\n"); for (PsiMethod currentMethod : methods) { // 遍历方法内的所有方法调用 currentMethod.accept(new JavaRecursiveElementVisitor() { @Override public void visitMethodCallExpression(PsiMethodCallExpression callExpr) { super.visitMethodCallExpression(callExpr); PsiMethod calledMethod = callExpr.resolveMethod(); if (calledMethod == null) return; PsiClass calledClass = calledMethod.getContainingClass(); if (calledClass == targetClass) { // 调用当前类的方法,属于内聚 result.append(currentMethod.getName()).append(" 调用了内部方法:").append(calledMethod.getName()).append("\n"); } else { // 调用外部类的方法,属于耦合 String calledClassName = calledClass.getQualifiedName() != null ? calledClass.getQualifiedName() : calledClass.getName(); result.append(currentMethod.getName()).append(" 调用了外部方法:").append(calledClassName).append(".").append(calledMethod.getName()).append("\n"); } } }); } // 弹出窗口显示结果 Messages.showInfoMessage(project, result.toString(), "类信息分析结果"); }
额外注意事项
- 依赖导入:确保你的插件项目中已经引入了
com.intellij.psi相关的依赖(在plugin.xml中声明必要的权限,比如com.intellij.modules.java)。 - 空值处理:比如
getQualifiedName()可能返回null(匿名类、本地类),所以要做非空判断。 - 代码行数精度:上面的示例是按换行符统计,如果需要排除空行、注释,可以扩展逻辑,比如用IDEA的
LineNumberCalculator或者自己过滤空白行和注释行。 - 耦合内聚扩展:你可以进一步统计每个方法的内部调用占比、外部调用次数,来量化类的内聚度和耦合度。
内容的提问来源于stack exchange,提问作者Bharthan




