【问题标题】:Compile .java file without knowing public class name在不知道公共类名的情况下编译 .java 文件
【发布时间】:2016-08-20 00:19:48
【问题描述】:

我想知道如何在不知道公共类名的情况下编译 .java 文件。

提供一个示例用例:我正在操作一个沙盒,并且允许用户向我发送一个字符串。我将该字符串写入 .java 文件,我对其进行编译、评估生成的类并回复输出。

?????.java

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

如果我给?????.java 随机分配一个名称,例如test.java 并运行命令javac test.java,那么它会失败

错误:HelloWorld 类是公共的,应该在名为 HelloWorld.java 的文件中声明,如下所示:

HelloWorld.java

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

这可能吗?我应该放弃编译 Java 的能力,还是有其他解决方案?

【问题讨论】:

  • 呃,让用户告诉你?在文件名中?您实际上不需要类名,只需要文件名。
  • 现在我再次阅读了您的问题文本,我很困惑。如果"users [...] send [you] a .java file",那么您已经知道文件的名称,所以为什么要重命名它。而且由于该文件应该包含public 类,因此由用户来确保它包含。知道文件名意味着你也知道类名,所以你知道如何编译和运行它。
  • @Andreas 澄清一下,他们只是发送一个文本字符串,而不是一个命名文件。
  • 所以指示他们将类称为特定的东西,例如ChallengeAssignmentExercise 等。如果您将文件命名为 Challenge.java 并且它不会编译,或者如果它编译但没有创建正确名称的类,或者该类没有没有main() 方法,那么他们在挑战/作业/练习中失败了。

标签: java


【解决方案1】:

嗯...为了能够评估自定义代码,您还必须知道使用 main 方法的类名。

我认为最便宜的方法是强迫用户遵守某种代码约定,即“包名应该是 foo.bar.baz,类名是 Qux,主类是强制性的”。在这种情况下,您可以将错误消息从编译器按原样传递给用户并显示代码约定。

不太快速的方法是做一些肮脏的技巧,比如用另一个具有已知名称的类正确包装接收到的代码,弄乱包名,然后尝试对内部类main方法进行反射调用,但在这种情况下,你会正确的错误报告有问题,很容易出错。

正确且可能很长的方法是采用java源代码解析器(如this,构建AST,找出顶级类,根据包/类名重命名文件并执行javac (甚至从 AST 生成字节码)。

编辑

您也可以参加java-repl 项目。这可以让您评估类似脚本的程序。

希望有帮助!

【讨论】:

    【解决方案2】:

    如果源文件的基名与源代码中的类名不匹配,则没有简单的方法来编译“.java”文件。如果您不知道文件名应该是什么,那么您唯一的选择是(部分)解析源代码以找到它。


    但老实说,这是您应该以不同的方式解决的问题:

    • 如果您自己生成了源代码,那么您应该知道类名是什么时候,因此您应该能够推断出正确的名称“.java”文件。

    • 如果您正在编译“为您提供”的文件,那么您可以(并且 IMO 应该)坚持在具有适当文件名的“.java”文件中提供该文件。如果文件名与类名不匹配,那就是错误……而且你不知道错误是文件名还是类名!


    这是一个给你的思想实验。假设有人向您提供了源代码文件:

    • A.java ... 包含类A
    • B.java ...还包含类A

    你应该怎么做?如果你试图从文件中的类名中找出“正确”的文件名,你可能会错过用户在 B.java 中出错并将错误的类名放入源代码中!

    简而言之,您有两条相互矛盾的信息。其中任何一个都可能不正确。如果你让一个自动优先于另一个,你就会错过错误。

    【讨论】:

      【解决方案3】:

      如果你不将它声明为public,你实际上可以编译这个类。

      示例:

      class Test {
         public static void main(String[] args) {
             System.out.println("Hello world");
         }
      }
      

      将文件另存为.java,然后在cmd上编译

      javac .java
      

      之后可以使用类名运行

      java Test
      

      【讨论】:

      • 这是关于编译文件,而不是运行它,当然。但是这个答案直接针对......因为OP询问如何在不知道类名的情况下编译文件。
      • 问题说“我编译它,评估结果类并回复输出”。评估意味着运行,为此您需要类名。
      • @Andreas ...不一定。如果您知道它实现的接口的名称 ....
      • @StephenC 题中具体展示了一个类有main() 方法,表示需要运行它。即使你是对的,你仍然需要知道类的名称才能实例化它,然后才能使用接口调用方法。
      • 所以我想知道,根据返回的错误实际运行命令是否是一个有效的解决方案?例如,使用 javac .java --> error ---> grep -o -P '(?
      【解决方案4】:

      如果我要为 ?????.java 随机分配一个名称,例如 test.java 并运行命令 javac test.java 那么它会失败

      错误:HelloWorld 类是公共的,应该在名为 HelloWorld.java 的文件中声明

      好的,看看错误信息!编译器有助于告诉您文件的名称应该是什么。不要丢弃这些信息。解析错误信息以找到正确的文件名,重命名文件,然后重试。

      【讨论】:

      • OP 已经看到该错误消息。它在问题文本中。
      • @Andreas,好的,我错过了在 OP 的消息中看到这一点,但我的观点仍然相同。 OP 具有 Java 文件的文本,但不知道它的名称应该是什么。如果他编造了一个名字并尝试编译它,编译器会很有帮助地告诉他这个名字应该是什么。显而易见的解决方案是从错误消息中提取文件名并重试。
      • 我真的觉得这很聪明,谢谢@jameslarge
      • 请注意,不要在包含多个公共类的文件上无限循环。 ;)
      【解决方案5】:

      我有一个建议。当有人向您发送 xxxx.jar 文件时,您必须将其保存到某个地方,以便您知道文件夹路径。然后你可以使用下面的代码来获取名称。获取文件名和路径后,使用java反射技术获取方法并调用方法。

      final static void showAllFiles(File dir) throws Exception
      {
         File[] fs = dir.listFiles();
         for(int i=0; i<fs.length; i++)
         {
            // here you can get file name and path, then use reflection to get the instance and methods.
         }
      }
      

      【讨论】:

        【解决方案6】:

        类名保持编译时保持同名,在“public class HelloWorld”中,'HelloWorld'是类名,同时也是类名 编译。

        public class Hello{
                public static void main(String[] args) {
                   System.out.println("Hello, World");
            }
        }
        


        你可以在 cmd javac Hello.java 上编译。
        运行java Hello


        public class Hello2{
                public static void main(String[] args) {
                   System.out.println("Hello, Man");
            }
        }
        

        您可以在 cmd javac Hello2.java
        运行java Hello2

        【讨论】:

          猜你喜欢
          • 2021-04-06
          • 2016-07-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-29
          • 1970-01-01
          相关资源
          最近更新 更多