重现自己的程序
是否有可能制作一个Java程序将其源代码打印到一个新文件,并编译它,并运行已编译的程序?
对的,这是可能的。 一个简单的实现是:让源代码在字符串中包含自己,将字符串保存到文件中并用相同的字符串填充自己的字符串(否则,初始字符串将具有无限大小,因为它的递归方式实现),编译文件,并运行编译的文件(反过来,将执行相同的操作)。
非平凡的实现要困难得多。
更新:
好吧,不妨让它自动运行。 享受疯狂。 运行风险自负。
是的,这是可能的,因为我实际上已经写完了。 它没有RUN部分(这太疯狂了,因为正如其他人提到的那样,它会导致无限循环),但这里是: Quine.java
import java.io.*; public class Quine { public static void main(String[] args) throws Exception { char q = 34; String out = "Quine$"; String text = ( "import java.io.*; " + "public class [OUT] { " + "public static void main(String[] args) throws Exception { " + "char q = 34; String out = `[OUT]$`; String text = `[TEXT]`; " + "PrintWriter pw = new PrintWriter(out + `.java`); " + "pw.format(text, 34, out, text); " + "pw.close(); Runtime runtime = Runtime.getRuntime(); " + "runtime.exec(`javac ` + out + `.java`).waitFor(); " + "runtime.exec(`java ` + out); " + "} " + "}" ).replace("`", "%1$c").replace("[OUT]", "%2$s").replace("[TEXT]", "%3$s"); PrintWriter pw = new PrintWriter(out + ".java"); pw.format(text, 34, out, text); pw.close(); Runtime runtime = Runtime.getRuntime(); runtime.exec("javac " + out + ".java").waitFor(); runtime.exec("java " + out); } }
所以这里是如何让疯狂开始:
-
javac Quine.java
编译 -
java Quine
运行它- 它将生成,编译和运行
Quine$
- 它将生成,编译和运行
- 我确保
Quine.java
尽可能可读,因此与Quine$.java
的主要区别在于格式化和3xreplace
。 微小的区别是Quine$.java
已经设置为Quine$$
。 -
Quine$
将生成,编译和运行Quine$$
-
Quine$$
将生成,编译和运行Quine$$$
-
Quine$$$
将生成,编译和运行Quine$$$$
- …
请注意,这不会通过读取.java
源代码等进行任何逆向工程或作弊Quine
是一个quine生成器,因为它生成不同格式的不同代码,但Quine$
几乎是一个真正的自包含quine:它确实重现了它,它只是重新标记它Quine$$
(它复制自己并重新定位到Quine$$$
等)。
所以从技术上讲,没有无限循环:当文件系统无法处理另一个$
时,它最终会停止。 我能够通过强制删除所有Quine$*
文件手动停止疯狂,但运行风险自负!
当然有效 – 看看rosetta代码并导航到Quine,这是一个自助参与程序,可以在没有任何外部访问的情况下输出自己的源代码 。
在Java中有一个例子。
复制自身或自我复制程序的程序称为Quine程序
Java中的示例程序,它可以自我复制。
public class QuineProgram { public static void main(String[] args){ String f = "public class QuineProgram { " + "public static void main(String[] args)" + "{ String f =%c%s%1$c;" + " System.out.printf(f,34,f);}} "; System.out.printf(f, 34, f); } }
输出:
public class QuineProgram { public static void main(String[] args){ String f ="public class QuineProgram { public static void main(String[] args){ String f =%c%s%1$c; System.out.printf(f,34,f);}} "; System.out.printf(f,34,f);}}
您可以使用Java Compiler API(JSR-199)。 下面是来自JSR-199的代码,它编译来自String的代码(略微修改以使其编译)。 代码实际上将String
源代码编译成一个字节数组(即它不写入磁盘),加载它然后通过reflection执行它:
-
MemoryFileManager.java
:用于将字符串编译为字节数组的文件管理器。 -
ByteArrayClassLoader.java
:从字节数组加载类的类加载器。 -
CompileFromString.java
:将所有内容组合在一起的类。
这可能是一个起点(原作者Peter VanderAhé的作品)。
顺便说一下,你当然需要一个JDK来使用这个API。
我不知道你想要什么,但我认为BeanShell是你可以使用的东西。 BeanShell是一个解释器。 你可以运行未编译的Java代码(所以你给它一个包含代码的字符串,然后运行它)。
当然如果你真的想做你写的东西,运行程序的机器需要一个JDK来编译你的程序。
希望这可以帮助
我认为它不适用于Java。 这不会涉及覆盖正在运行的类文件。
假设您的程序在Quine.java中编译为Quine.class。
现在,Quine.class将尝试将其输出写入Quine.java(到目前为止一直很好),并将其编译为Quine.class。 由于Quine.class已经运行,这将是一个问题
是的 – 不要忘记使用JDK而不是JRE:
-
将应用程序的源代码文件与应用程序捆绑在一起。 该应用程序将源文件复制到一组新的源代码文件,编译新的源文件,将新的源代码与新的类文件捆绑到一个新的应用程序,然后生成新的应用程序。
要么
-
将反编译器与应用程序捆绑在一起。 该应用程序将在其自己的类文件上运行反编译器以生成新的源代码文件,编译新的源文件,将反编译器与新的类文件捆绑到新的应用程序中,然后生成新的应用程序。