当前位置: 首页 > 面试题库 >

Java JavaCompiler.run()也可以编译匿名类

杨凯旋
2023-03-14
问题内容

我试图动态加载文本文件并进行编译。

File file = new File("Files/"+fileName+".java");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, errStream, file.getAbsolutePath());

然后,我稍后将加载已编译的.class文件:

 public Class loadStrategyClass(File strategyClassFile) throws IOException
    {
        FileChannel roChannel = new RandomAccessFile(strategyClassFile, "r").getChannel();
        ByteBuffer buffer = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());

        return defineClass(strategyClassFile.getName(), buffer, (ProtectionDomain)null);
    }

我目前遇到两个问题:首先是我加载的.java文件是否包含匿名类。看来JavaCompiler类不会编译它们。线程“主”中的异常java.lang.IllegalAccessException:类Loader.ClassLoader无法使用修饰符“”访问类Files.myname.myclass
$ 1的成员

第二个:是有时候我会收到NoClassDefFoundError错误:线程“
main”中的异常java.lang.NoClassDefFoundError:Files / myname /
myclass尽管事实上其他类会正确加载并且.class文件在该路径中。


问题答案:

显然,您loadStrategyClass是在custom中定义的ClassLoader。问题在于,defineClass对于感兴趣的类仅调用一次是不够的,您的类加载器通常必须通过实现才能按需解析类findClass,以便JVM可以解析依赖项,例如内部类。

您没有指定如何获取方法的strategyClassFile参数loadStrategyClass。由于您没有任何选择地运行编译器,因此我想您只是相对于源文件查找了文件。要解决其他依赖关系,需要知道类目录的实际根目录。当您定义存储类文件的位置时,它将变得更加容易,例如

// customize these, if you want, null triggers default behavior
DiagnosticListener<JavaFileObject> diagnosticListener = null;
Locale locale = null;

JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
    = c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());

// define where to store compiled class files - use a temporary directory
Path binaryDirectory = Files.createTempDirectory("compile-test");
fm.setLocation(StandardLocation.CLASS_OUTPUT,
               Collections.singleton(binaryDirectory.toFile()));

JavaCompiler.CompilationTask task = c.getTask(null, fm,
    diagnosticListener, Collections.emptySet(), Collections.emptySet(),
    // to make this a stand-alone example, I use embedded source code
    Collections.singleton(new SimpleJavaFileObject(
        URI.create("string:///Class1.java"), Kind.SOURCE) {
            public CharSequence getCharContent(boolean ignoreEncodingErrors) {
                return "package test;\npublic class Class1 { public class Inner {} }";
            }
        }));
if(task.call()) try {
    URLClassLoader cl = new URLClassLoader(new URL[]{ binaryDirectory.toUri().toURL() });
    Class<?> loadedClass = cl.loadClass("test.Class1");
    System.out.println("loaded "+loadedClass);
    System.out.println("inner classes: "+Arrays.toString(loadedClass.getClasses()));
} catch(ClassNotFoundException ex) {
    ex.printStackTrace();
}

在上面的示例中,我们知道了类目录的根,因为已经定义了它。这允许简单地使用现有的URLClassLoader而不是实现新型的类加载器。当然,使用自定义文件管理器,我们还可以将内存存储用于临时目录。

您可以使用此API来发现生成的内容,这使您可以使用生成的类而无需事先知道要编译的源文件中存在哪些包或内部类声明。

public static Class<?> compile(
    DiagnosticListener<JavaFileObject> diagnosticListener,
    Locale locale, String sourceFile) throws IOException, ClassNotFoundException {

    JavaCompiler c = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fm
        = c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());

    // define where to store compiled class files - use a temporary directory
    Path binaryDirectory = Files.createTempDirectory("compile-test");
    fm.setLocation(StandardLocation.CLASS_OUTPUT,
                   Collections.singleton(binaryDirectory.toFile()));

    JavaCompiler.CompilationTask task = c.getTask(null, fm,
        diagnosticListener, Collections.emptySet(), Collections.emptySet(),
        fm.getJavaFileObjects(new File(sourceFile)));
    if(task.call()) {
        Class<?> clazz = null;
        URLClassLoader cl = new URLClassLoader(new URL[]{binaryDirectory.toUri().toURL()});
        for(JavaFileObject o: fm.list(
            StandardLocation.CLASS_OUTPUT, "", Collections.singleton(Kind.CLASS), true)) {

            String s = binaryDirectory.toUri().relativize(o.toUri()).toString();
            s = s.substring(0, s.length()-6).replace('/', '.');
            clazz = cl.loadClass(s);
            while(clazz.getDeclaringClass() != null) clazz = clazz.getDeclaringClass();
            if(Modifier.isPublic(clazz.getModifiers())) break;
        }
        if(clazz != null) return clazz;
        throw new ClassNotFoundException(null,
            new NoSuchElementException("no top level class generated"));
    }
    throw new ClassNotFoundException(null,
        new NoSuchElementException("compilation failed"));
}

如果使用此方法动态绑定插件或模块,则可以扩展搜索以查找实现特定接口或具有特定批注的结果类。



 类似资料:
  • 问题内容: 我想创建一个扩展另一个类的匿名内部类。 实际上,我要执行的操作如下: 这可能吗? 问题答案: 您不能给匿名类命名,这就是为什么它被称为“匿名”的原因。我看到的唯一选择是从您的外部范围引用变量 另一个选择是像这样定义一个本地类(不是匿名类): 如果您还需要更多,请创建一个常规类…

  • laravel的一个问题,我简化了一下是这样。 我想让访问接口 /a,未登录状态访问返回 0, 登录状态下返回 1,如何实现? 正常是这样。topic 状态为 1 时可以任何人访问,状态未 2 时,只有作者可以访问。 上边这种情况,无论登录与否,$user 都是 null。 如果 route 加上 middleware('auth'), 那么未登录状态又无法访问。

  • 问题内容: 我正在写一个简单的命令行游戏。我有很多功能,所有功能都将在这里发布。 问题:程序可以编译,但是当被调用并选择了一个数字时,我得到了: 这是我的代码: 有人可以在这里指出正确的方向吗?我究竟做错了什么?我感觉这是类“玩家”和在“游戏”类中创建的对象的类问题。 问题答案: 你得到一个因为是。您在这里做了什么: 是声明 局部 变量。静态类变量保持不变,因此保持不变。 这称为阴影(JLS,第6

  • 举例: 一篇新闻,匿名用户可以正常访问,已登录用户访问后会记录阅读历史。 这种情况下路由该怎么写的?

  • 问题内容: 我有以下LINQ示例: 所有这些都有效并且很合理。 我想要实现的是将分组为强类型而不是匿名类型。 例如,我有一个ProductColour类,我想分组为一个 这可能吗? 谢谢 问题答案: 编辑:好的,我会完全误读您的帖子。从外观上看,您不想 按 其他类型分组-您希望将组中的每个元素投影 到 不同的类型。这是我要做的:

  • 已经浏览了之前发布的关于同一问题的3个问题,但不知道我哪里出错了。我正在尝试为按钮(b)实现ActionListener作为- 编译时,这会给出一个消息- 我的框架。java:3:错误:MyFrame不是抽象的,并且不重写ActionListener类MyFrame extends Frame实现ActionListener中的抽象方法actionPerformed(ActionEvent)^1错