今天翻看以前的博客,偶然发现以前写过一篇C++的Quine(点我前往),也就是输出自身源代码。闲暇之下,仔细思考了下用Java实现的方法,经过分析和错误尝试,最终得到一个我认为是最简短的Java实现Quine的模式,在此分享我的思考和分析历程。
1.分析Java实现Quine的代码形式
- 一个Java程序通常是这样的:
package xxx;
public class Xxx{
public static void main(String[] args) {
...
}
}
- 大致构成就是:packge语句,class申明,main方法,方法体,括号,括号。
- 这些都是相对静态的语句。如果要输出程序自身的话,这些肯定是最好输出的。
- 输出这些语句的方式呢?是直接在
System.out.println()
里面输出吗?不妨先尝试一下这种方法:
package xxx;
public class Xxx{
public static void main(String[] args) {
System.out.println("package xxx;");
}
}
- 才写第一条输出语句,是不是发现了什么?如果这样输出的话,那么这条输出语句又怎么输出出来呢?在下面再写一条输出语句输出这一条?一次类推,就无限套娃了,Hhhh。
- 既然不能直接输出,那么最好的方法就是:把这些语句写到一个字符串数组里,通过这个数组来输出。
- 这种输出方式就给了输出自身语句一个空间,只要把自身的语句写到字符串数组里,最后统一输出,不久可以了嘛。
- 于是,能够大概想象到这个程序应该是这个样子的。、
package xxx;
public class Xxx{
public static void main(String[] args) {
String s[]= {
"package Quine;",
" ",
"public class Main {",
" public static void main(String[] args) {",
" String s[]= {",
}
for(int i=0;i<s.length;i++)
System.out.println(s[i]);
}
}
- 当你写到这里的时候,是不是发现了一个问题,这个字符串数组又该怎么输出出来呢?看起来好像又陷入无限循环了。
- 但是仔细一下,造成我们需要用无限套娃方法来输出这个字符串本身的原因是什么?原因是,我们想通过一条输出语句把这个数组全部输出出来,所以才会遇到这个套娃的困惑。如果我们分开输出,第一次输出先输出前面相对静态的语句(直到创建字符串数组那行),然后再来一个输出语句,把这个字符串数组输出,最后我们还需要一条语句把输出语句的自身输出出来。
- 综上分析,我们Java实现Quine的代码形式应该是:
package xxx;
public class Xxx{
public static void main(String[] args) {
String s[]= {
"package Quine;",
" ",
"public class Main {",
" public static void main(String[] args) {",
" String s[]= {",
}
//输出字符串数组的前面部分语句
//输出字符串数组本身的语句
//输出 输出语句本身
}
}
2.完善细节,步步突破
- 通过上面的分析,我们的重点就转到如何写字符串数组和输出语句上了。
- 第一部分,输出字符串数组的前面部分:只需要将前面部分全部写入字符串数组,然后第一次输出的时候按下标输出,只输出前面部分。具体代码应该是:
package xxx;
public class Xxx{
public static void main(String[] args) {
String s[]= {
"package Quine;",
" ",
"public class Main {",
" public static void main(String[] args) {",
" String s[]= {",
}
for(int i=0;i<=5;i++)
System.out.println(s[i]);
}
}
- 输出结果:可以看到以及达到预期目的了。
-
第二部分,输出字符串数组:这里面就有几个小细节了。
- 第一个细节:里面的双引号要想办法输出出来,这个是字符串数组里面没有的。不可能把这个引号写到字符串数组里转义,不然会影响到其它部分的输出。那么最好的办法就是使用一个char型变量来代替这个引号,在输出的时候将引号一并输出。
- 第二个细节:字符串数组里面每个字符串在源代码的格式里面前面都是有空格的,要想办法把这几个空格输出出来。把输出空格写到输出语句里面不太好,这样后面输出自身的时候又会涉及到转义,最好的办法还是在字符串数组里面提前把那几个空格写好,直接用字符串数组里面的。可以就写到
String s[]= {
的下面。 - 第三个细节:注意每个字符串数组里面,每个字符串结束后的逗号,也要一并输出出来。
-
具体代码应该是:
package Quine;
public class Main {
public static void main(String[] args) {
char c=34;
String s[]= {
"package Quine;",
" ",
"public class Main {",
" public static void main(String[] args) {",
" char c=34;",
" String s[]= {",
" ",
" };",
" }",
"}"
};
for(int i=0;i<6;i++)
System.out.println(s[i]);
for(int i=0;i<s.length;i++)
System.out.println(s[6]+c+s[i]+c+',');
}
}
- 输出结果:成功输出了字符串数组本身。
- 第三部分:输出 输出语句 自身:这个就很简单了,只需要代码写完后,将其复制到字符串数组中就行了,注意输出的格式。
3.最终实现代码(极简)
package Quine;
public class Main {
public static void main(String[] args) {
char c=34;
String s[]= {
"package Quine;",
" ",
"public class Main {",
" public static void main(String[] args) {",
" char c=34;",
" String s[]= {",
" ",
" };",
" for(int i=0;i<6;i++)",
" System.out.println(s[i]);",
" for(int i=0;i<s.length;i++)",
" System.out.println(s[6]+c+s[i]+c+',');",
" for(int i=7;i<s.length;i++)",
" System.out.println(s[i]);",
" }",
"}"
};
for(int i=0;i<6;i++)
System.out.println(s[i]);
for(int i=0;i<s.length;i++)
System.out.println(s[6]+c+s[i]+c+',');
for(int i=7;i<s.length;i++)
System.out.println(s[i]);
}
}
思考实现Quine的过程真的比较有趣,建议把所有会的语言的Quine实现方式思考一遍,hhh。