不知道公共类名如何编译 .java 文件

6
我想知道如何在不知道公共类名称的情况下编译.java文件。
举例说明:我正在操作一个沙盒,允许用户向我发送字符串。我将该字符串写入一个.java文件中,然后编译它,评估生成的类并回复输出。 ?????.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

如果我随机给?????.java这个文件命名,比如说test.java,然后运行javac test.java命令,那么它将会失败。错误信息是:error: class HelloWorld is public, should be declared in a file named HelloWorld.java。因此,正确的文件名应该是HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

这是否可能?我应该放弃编译Java的能力,还是有其他替代方案?


3
让用户告诉你?在文件名中吗?你实际上不需要类名,只需要文件名。 - user207421
现在我再次阅读你的问题,我有些困惑。如果用户已经向你发送了一个 .java 文件,那你已经知道文件的名称,为什么还要重命名它呢?而且由于文件应该包含一个 public 类,用户需要确保文件中确实有这个类。知道文件的名称也就意味着你知道类的名称,所以你知道如何编译和运行它。 - Andreas
@Andreas 为了澄清,他们只是发送一串文本字符串,而不是一个命名的文件。 - Trevor
所以请指导他们给类命名为某个特定的名称,例如“Challenge”、“Assignment”、“Exercise”等。如果您将文件命名为“Challenge.java”,但它无法编译,或者即使编译了但没有创建正确名称的类,或者该类没有一个“main()”方法,则他们就未能完成挑战/任务/练习。 - Andreas
7个回答

1

嗯...为了能够评估自定义代码,您还需要知道一个带有main方法的类名。

我认为最便宜的方法是强制用户遵循某种代码约定,例如"包名称应为foo.bar.baz,类名称为Qux,主类是必需的"。在这种情况下,您可以将编译器的错误消息直接传递给用户,并显示代码约定。

较慢的方法是使用一些不良手段,例如使用已知名称的另一个类正确包装接收到的代码,混淆包名称,然后尝试调用内部类的反射main方法,但在这种情况下,您将面临适当的错误报告问题并容易出错。

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

编辑

你也可以查看java-repl项目。这可以让你评估类似脚本的程序。
希望这有所帮助!

0
如果我随机给?????.java分配一个名称,比如test.java并运行命令javac test.java,那么它将失败。 错误:类HelloWorld是公共的,应在名为HelloWorld.java的文件中声明。
好的,看看错误信息!编译器正在有用地告诉您文件的名称应该是什么。不要丢弃这些信息。解析错误消息以找到正确的文件名称,重新命名文件,然后再试一次。

OP已经看到了错误信息。它在问题文本中。 - Andreas
@Andreas,好的,我没有在OP的信息中看到那个,但我的观点仍然是一样的。OP有一个Java文件的文本,不知道它的名称应该是什么。如果他编造一个名称,并尝试编译它,编译器会很乐意地“告诉他”应该是什么名称。显然的解决方案是从错误消息中提取文件名,然后再次尝试。 - Solomon Slow
我觉得这真的很聪明,谢谢@jameslarge - Trevor
只要注意不要在包含多个公共类的文件上无限循环即可。 ;) - Dolda2000

0

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


但说实话,这是一个你应该用不同方式解决的问题:

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

  • 如果您正在编译“为您提供”的文件,则可以(并且在我看来应该)坚持以适当的文件名提供“.java”文件。如果文件名与类名不匹配,则是一个错误...而您不知道错误是文件名还是类名!


这里有一个思维实验。假设有人提供给你源代码文件:

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

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

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


0
到现在为止,你可能已经实施了它。 但是这是给那些在2018年之后尝试实施这样的系统的人, Java 11支持直接运行Java文件。
如果你有xxxx.java文件,你可以直接执行它。
java xxxx.java

这适用于不同的文件名和类名。
请参考文档中的java命令。

0

如果您不将类声明为public,实际上可以编译该类。

例如:

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

将文件保存为.java格式,并在命令提示符上编译。
javac .java

你可以通过使用类名后运行

java Test

这是关于编译文件而不是运行它的问题,当然。但是这个答案直接针对这个问题......因为 OP 问如何在不知道类名的情况下编译文件。 - crystalfrost
1
问题说“我编译后,运行生成的类并回复输出”。评估意味着运行,为此您需要类名。 - Andreas
@Andreas...不一定。如果你知道它实现的接口的名称... - Stephen C
@StephenC 这个问题明确展示了一个带有 main() 方法的类,这意味着你需要运行它。即使你是正确的,你仍然需要知道类的名称才能实例化它,然后才能使用接口来调用方法。 - Andreas
1
我在想,是否可以根据返回的错误实际运行一个命令来解决问题?例如,使用从 javac .java 返回的值 --> error ---> grep -o -P '(?<=class).*(?={)',这应该是类的名称,将其存储在变量中,然后作为 java "storedClassName" 运行。 - crystalfrost
显示剩余3条评论

0
我有一个建议。当有人发送给你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.
   }
}

-3

为了保持编译后的类名不变,需要使用相同的类名。在 "public class HelloWorld" 中,'HelloWorld' 是类名,同时也是编译后的名称。

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


你可以在命令行中编译它,使用以下命令:javac Hello.java
运行程序:java Hello


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

你可以在命令行中这样编译:javac Hello2.java
运行:java Hello2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接