是否可以制作一个 Java 程序,将其源代码打印到一个新文件中,然后编译它,然后运行编译后的程序?
9 回答
更新:
好吧,还不如让它自动运行。享受疯狂。运行风险自负。
是的,这是可能的,因为我实际上是写出来的。它不执行 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
已out
设置为Quine$$
。 Quine$
将产生、编译和运行Quine$$
Quine$$
将产生、编译和运行Quine$$$
Quine$$$
将产生、编译和运行Quine$$$$
- ...
请注意,这不会通过阅读.java
源代码进行任何逆向工程或作弊,等等。Quine
它是一个 quine 生成器,因为它产生不同格式的不同代码,但Quine$
几乎是一个真正的独立 quine:它确实重现本身,它只是重新标记它Quine$$
(复制自身并重新标记Quine$$$
等)。
所以从技术上讲,没有无限循环:当文件系统无法处理另一个$
. 我能够通过强制删除所有Quine$*
文件来手动停止这种疯狂,但运行风险自负!!!
是的,有可能。一个简单的实现是:将源代码包含在一个字符串中,将字符串保存到一个文件中并用相同的字符串填充它自己的字符串(否则,由于递归方式,初始字符串将是无限大小的实现),编译文件,然后运行编译后的文件(这反过来也会做同样的事情)。
非平凡的实现要困难得多。
确定有效 - 查看Rosetta 代码并导航到 Quine,这是一个自引用程序,无需任何外部访问即可输出其自己的源代码。
Java中有一个quine的例子。
自我复制的程序或自我复制程序称为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
成一个字节数组(即它不写入磁盘),加载它然后通过反射执行它:
MemoryFileManager.java
:用于将字符串编译为字节数组的文件管理器。ByteArrayClassLoader.java
:从字节数组加载类的类加载器。CompileFromString.java
:将所有内容包装在一起的类。
这可能是一个起点(感谢原作者 Peter Van der Ahé)。
顺便说一句,你当然需要一个 JDK 来使用这个 API。
我不知道你到底想要什么,但我认为BeanShell是你可以使用的东西。BeanShell 是一个解释器。你可以运行未编译的Java代码(所以你给它一个带有代码的字符串然后他运行它)。
当然如果你真的想做你写的东西,运行程序的机器需要一个JDK来编译你的程序。
希望这可以帮助
我不认为它会在 Java 中工作。这不会涉及覆盖正在运行的类文件。
假设您的程序在 Quine.java 中编译为 Quine.class。
现在 Quine.class 将尝试将其输出写入 Quine.java(到目前为止一切顺利),并将其编译为 Quine.class。这将是一个问题,因为 Quine.class 已经在运行
是的 - 不要忘记使用 JDK 而不是 JRE:
将应用程序的源代码文件与应用程序捆绑在一起。该应用程序会将源文件复制到一组新的源代码文件,编译新的源文件,将新的源代码与新的类文件捆绑到一个新的应用程序中,然后生成新的应用程序。
或者
将反编译器与应用程序捆绑在一起。该应用程序将在其自己的类文件上运行反编译器以生成新的源代码文件,编译新的源文件,将反编译器与新的类文件捆绑到一个新的应用程序中,然后生成新的应用程序。
这是一个使用预览文本块功能(-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);
}
}