【问题标题】:How to Read Java File Structure using Java?如何使用 Java 读取 Java 文件结构?
【发布时间】:2011-02-07 16:42:21
【问题描述】:

我正在尝试读取一个 java 文件并在控制台中显示包、类和方法名称。像这样:

文件:Test.java

package tspec.test;

public class Test {
   public void addTest () {}
   public void deleteTest () {}
}

输出:

package name: tspec.test
class name: Test
method name:
addTest
deleteTest

提前致谢:)

【问题讨论】:

  • 当您说“读取 Java 文件”时,您是指 Java 源代码文件 (.java) 还是 Java 字节码文件 (.class)?
  • 我想你可以看看javac 的源代码,看看它是如何解析.java 文件的。您这样做只是为了练习,还是某个更大项目的一部分?

标签: java file


【解决方案1】:

唯一的困难是java代码可能没有很好的格式。就像函数声明可以分布在多行一样。

最终的解决方案是创建一个自动机来首先标记源代码,然后应用一些编译器技术从解析的数据中获取您想要的内容。

【讨论】:

  • 正则表达式可以处理多行,尽管我同意最终的解决方案是解析器。
【解决方案2】:

其目的是内省 Java 代码并报告其内容。使用反射,您可以执行以下操作:

 Class.forName(className).getDeclaredMethods();

这两种解决方案都不需要第三方库或工具。

【讨论】:

    【解决方案3】:

    我们使用 PMD Java 代码分析器来解决类似的问题。 很有用。

    http://pmd.sourceforge.net/

    【讨论】:

      【解决方案4】:

      您不必自己解析 Java 文件来执行此操作! Java 已经包含一种获取有关其自己的类、方法和包的信息的方法:它被称为reflection

      看看java.lang.Class 类。此类的每个实例都代表一个特定的 Java 类,并包含返回类名的方法、它所在的包、它包含的方法以及更多信息。

      同样值得一看的是java.lang.reflect 包,因为Class 的一些方法从这个包返回类型。该包包含表示方法、类型、字段等内容的类。

      要获取Test 类的Class 实例,可以使用以下代码:

      Class<?> testclass = Class.forName("tspec.test.Test");
      

      这将返回一个未知类型的类,如果您不熟悉泛型,这就是尖括号内的问号的含义。为什么类实例的类型是未知的,是因为你用一个字符串指定了类名,它是在运行时解析的。在编译时,Java 无法确定传递给 forName 的字符串是否代表一个有效的类。

      但是,上面定义的testclass 可以很好地获取类的名称、方法和包含的包。

      【讨论】:

      • 我认为发布者可以访问 .java 文件而不是 .class 文件。
      【解决方案5】:

      这可以使用Java Compiler API(在Java 6 中引入)来完成。不幸的是,这个解决方案仅限于 Sun 的 JDK。因此,您必须安装 JDK并且您必须在类路径中包含其 tools.jar 文件。

      public void displayInformation(File javaSourceFile) throws Exception {
          JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      
          // The file manager locates your Java source file for the compiler. Null arguments indicate I am comfortable with its default behavior.
          StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
      
          // These will be parsed by the compiler
          Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(javaSourceFile);
      
          // Creates a new compilation task. This doesn't actually start the compilation process.
          // Null arguments indicate I am comfortable with its default behavior.
          CompilationTask task = compiler.getTask(null, null, null, null, null, fileObjects);
      
          // Cast to the Sun-specific CompilationTask.
          com.sun.tools.javac.api.JavacTaskImpl javacTask = (com.sun.tools.javac.api.JavacTaskImpl) task;
      
          // The Sun-specific JavacTaskImpl can parse the source file without compiling it, returning 
          // one CompilationUnitTree for each JavaFileObject given to the compiler.getTask call (only one in our case).
          Iterable<? extends CompilationUnitTree> trees = javacTask.parse();
          CompilationUnitTree tree = trees.iterator().next();
      
          // Create a class that implements the com.sun.source.tree.TreeVisitor interface.
          // The com.sun.source.util.TreeScanner is a good choice because it already implements most of the logic.
          // We just override the methods we're interested in.
          class MyTreeVisitor extends TreeScanner<Void, Void> {
      
              @Override
              public Void visitClass(ClassTree classTree, Void p) {
                  System.out.println("class name: " + classTree.getSimpleName());
                  System.out.println("method name:");
                  return super.visitClass(classTree, p);
              }
      
              @Override
              public Void visitMethod(MethodTree methodTree, Void p) {
                  System.out.println(methodTree.getName());
                  return super.visitMethod(methodTree, p);
              }
      
          }
      
          tree.accept(new MyTreeVisitor(), null);
      }
      

      当我将此方法传递给File 其内容是您的样本时,我会收到以下输出:

      班级名称:测试 方法名称: 添加测试 删除测试

      不幸的是,我还没有弄清楚包名的存储位置。

      【讨论】:

      • 使用 "@Override public Void visitCompilationUnit( CompilationUnitTree unit, Void arg ) { System.out.println( "Package: " + unit.getPackageName() ); return super.visitCompilationUnit( unit, arg ) ; };"获取包裹。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-22
      • 2012-07-21
      • 2015-04-18
      相关资源
      最近更新 更多