是否可以制作一个Java程序,将其源代码打印到新文件中,并编译它,并运行已编译的程序?
答案 0 :(得分:23)
好吧,不妨让它自动运行。享受疯狂。自行承担风险。
是的,这是可能的,因为我实际上已经写完了。 它不执行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
的区别在于格式化和3x replace
。 次要的区别在于Quine$.java
将out
设置为Quine$$
。Quine$
将生成,编译并运行Quine$$
Quine$$
将生成,编译并运行Quine$$$
Quine$$$
将生成,编译并运行Quine$$$$
请注意,这不会通过阅读.java
源代码等进行任何逆向工程或作弊。Quine
是一个quine生成器,因为它生成不同格式的不同代码,但Quine$
几乎是一个真正独立的quine:它确实重现了它,它只是重新标记它Quine$$
(它自我复制并重新标记为Quine$$$
等。)
从技术上讲,没有无限循环:当文件系统无法处理另一个$
时,它最终会停止。 我能够通过强行删除所有Quine$*
文件手动停止疯狂,但运行风险自负!!!
答案 1 :(得分:11)
是的,有可能。 一个简单的实现是:让源代码在字符串中包含自己,将字符串保存到文件中并用相同的字符串填充自己的字符串(否则,初始字符串将具有无限大小,因为它的递归方式实现),编译文件,然后运行编译后的文件(反过来也会这样做)。
非平凡的实施要困难得多。
答案 2 :(得分:6)
确实有效 - 看看rosetta code并导航到Quine,这是一个自助参与程序,可以在没有任何外部访问的情况下输出自己的源。
Java中有一个例子。
答案 3 :(得分:4)
复制自身或自我复制程序的程序称为Quine Programs
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);}}
答案 4 :(得分:2)
您可以使用Java Compiler API(JSR-199)。下面是来自JSR-199的代码,它编译来自String的代码(略微修改以使其编译)。代码实际上将String
中的源代码编译成一个字节数组(即它不写入磁盘),加载它然后通过反射执行它:
MemoryFileManager.java
:用于将字符串编译为字节数组的文件管理器。ByteArrayClassLoader.java
:从字节数组加载类的类加载器。CompileFromString.java
:将所有内容组合在一起的类。这可能是一个起点(原作者Peter VanderAhé的作品)。
顺便说一下,你当然需要一个JDK来使用这个API。答案 5 :(得分:1)
我不知道你想要什么,但我认为你可以使用BeanShell。 BeanShell是一个解释器。你可以运行未编译的Java代码(所以你给它一个包含代码的字符串并运行它)。
当然如果你真的想做你写的东西,运行程序的机器需要一个JDK来编译你的程序。
希望这有帮助
答案 6 :(得分:0)
我认为它不适用于Java。这不会涉及覆盖正在运行的类文件。
假设您的程序在Quine.java中编译为Quine.class。
现在Quine.class将尝试将其输出写入Quine.java(到目前为止一直很好),并将其编译为Quine.class。这将是一个问题,因为Quine.class已经在运行
答案 7 :(得分:0)
是 - 不要忘记使用JDK而不是JRE:
将应用的源代码文件与应用捆绑在一起。该应用程序会将源文件复制到一组新的源代码文件,编译新的源文件,将新的源代码与新的类文件捆绑到一个新的应用程序中,然后生成新的应用程序。
或
将反编译器与应用程序捆绑在一起。该应用程序将在其自己的类文件上运行反编译器以生成新的源代码文件,编译新的源文件,将反编译器与新的类文件捆绑到新的应用程序中,然后生成新的应用程序。
答案 8 :(得分:0)
这是使用预览文本块功能(-source 13 --enable-preview)的Java Quine,其中输出的格式与输入相同:
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f ="""
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f =""%c%c%s%1$c"";
System.out.printf(f, 34, 10, f);
}
}
""";
System.out.printf(f, 34, 10, f);
}
}
输出:
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f ="""
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f =""%c%c%s%1$c"";
System.out.printf(f, 34, 10, f);
}
}
""";
System.out.printf(f, 34, 10, f);
}
}
评论严重的版本:
package org.sample.quine;
public class Quine
{
public static void main(String...args)
{
// Inside text block use "" followed by token or token followed by "" so that we don't prematurely close text block
String f ="""
package org.sample.quine;
public class Quine
{
public static void main(String...args)
{
// Inside text block use "" followed by token or token followed by "" so that we don't prematurely close text block
String f =""%c%c%s%1$c"";
/* Tokens in template text block, each prefixed with percent symbol
* 1(c) third quote (34) of open block delimiter
* 2(c) new line (10) of open block delimiter
* 3(s) String f text block that goes between two text block delimiters
* 4(1$c) first quote (34) of close block delimiter,
* 1$ means first argument after template argument
* 2$ would be second argument after template argument
*/
// Arguments - 1 template (String f); 2 "; 3 newline; 4 template again without replacing tokens
System.out.printf(f, 34, 10, f);
}
}
""";
/* Tokens in template text block, each prefixed with percent symbol
* 1(c) third quote (34) of open block delimiter
* 2(c) new line (10) of open block delimiter
* 3(s) String f text block that goes between two text block delimiters
* 4(1$c) first quote (34) of close block delimiter,
* 1$ means first argument after template argument
* 2$ would be second argument after template argument
*/
// Arguments - 1 template (String f); 2 "; 3 newline; 4 template again without replacing tokens
System.out.printf(f, 34, 10, f);
}
}