谜题11:最后的笑声
public class LastLaugh{
public static void main(String[] args){
System.out.print("H"+"a");
System.out.print('H'+'a');
}
}
题目: Ha169 注(a: 97 , A : 65 ,0: 48)
原因:问题在于'H'和'a'是字符型字面常量,因为这两个操作数都不是字符串类型的,所以+ 操作符执行的是加
法而不是字符串连接。
解决办法:使用字符串连接操作符使用格外小心。+ 操作符当且仅当它的操作数中至少有一个是String类型时,
才会执行字符串连接操作;否则,它执行的就是加法。如果要连接的没有一个数值是字符串类型的,那
么你可以有几种选择:
- 预置一个空字符串;
- 将第一个数值用String.valueOf显式地转换成一个字符串;
- 使用一个字符串缓冲区;
- 或者如果你使用的JDK 5.0,可以用printf方法。
谜题12:ABC
public class ABC{
public static void main(String[] args){
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + numbers);
}
}
原因: 数组是从Object那里继承的toString方法[JLS 10.7],规范中描述到:“返回一个字符串, 它包含了该对象所属类的名字,'@'符号,以及表示对象散列码的一个无符号十六进制整数” [Java-API]。
解决办法: char数组不是字符串。要想将一个char数组转换成一个字符串,就要调用String.valueOf
(char[])方法。某些类库中的方法提供了对char数组的类似字符串的支持,通常是提供一个Object
版本的重载方法和一个char[]版本的重载方法,而之后后者才能产生我们想要的行为。 PI]
谜题13:畜牧场
public class AnimalFarm{
public static void main(String[] args){
final String pig = "length: 10";
final String dog = "length: " + pig.length();
System.out. println("Animals are equal: "
+ pig == dog);
}
}
题目:Animal are equal: true
原因:同13
谜题14:转义字符的溃败
public class EscapeRout{
public static void main(String[] args){
// \u0022 是双引号的Unicode转义字符
System.out.println("a\u0022.length()+\u0022b".length());
}
}
题目:输出2
原因:Java对在字符串字面常量中的Unicode转义字符没有提供任何特殊处理。
编译器在将程序解析成各种符号之前,先将Unicode转义字符转换成为它们所表示的字符[JLS3.2]。
因此,程序中的第一个Unicode转义字符将作为一个单字符字符串字面常量("a")的结束引号,
而第二个Unicode转义字符将作为另一个单字符字符串字面常量("b")的开始引号。程序打印的
是表达式"a".length()+"b".length(),即2。
谜题15:令人晕头转向的Hello
/**
* Generated by the IBM IDL-to-Java compiler, version 1.0
* from F:\TestRoot\apps\a1\units\include\PolicyHome.idl
* Wednesday, June 17, 1998 6:44:40 o’clock AM GMT+00:00
*/
public class Test{
public static void main(String[] args){
System.out.print("Hell");
System.out.println("o world");
}
}
题目: 编译不过
原因:问题在于注释的第三行,它包含了字符\units。这些字符以反斜杠(\)以及紧跟着的字母u开头的,
而它(\u)表示的是一个Unicode转义字符的开始。遗憾的是,这些字符后面没有紧跟四个十六进制
的数字,因此,这个Unicode转义字符是病构的,而编译器则被要求拒绝该程序。Unicode转义字符
必须是良构的,即使是出现在注释中也是如此。
谜题16:行打印程序
public class LinePrinter{
public static void main(String[] args){
// Note: \u000A is Unicode representation of linefeed (LF)
char c = 0x000A;
System.out.println(c);
}
}
题目:
原因:在Windows平台上,它是CR字符(回车)和紧随其后的LF字符(换行)组成的,
而在UNIX平台上,通常单独的LF字符被当作换行字符来引用。
谜题17:嗯?
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
略
谜题18:字符串奶酪
public class StringCheese {
public static void main(String[] args) {
byte bytes[] = new byte[256];
for (int i = 0; i < 256; i++)
bytes[i] = (byte)i;
String str = new String(bytes);
for (int i = 0, n = str.length(); i < n; i++)
System.out.println((int)str.charAt(i) + " ");
}
}
原因:到底什么是字符集?从技术角度上讲,它是“被编码的字符集合和字符编码模式的结合物”[Java-API]。
换句话说,字符集是一个包,包含了字符、表示字符的数字编码以及在字符编码序列和字节序列之间
来回转换的方式。转换模式在字符集之间存在着很大的区别:某些是在字符和字节之间做一对一的映射
,但是大多数都不是这样。ISO-8859-1是唯一能够让该程序按顺序打印从0到255的整数的缺省字符集,
它更为大家所熟知的名字是Latin-1[ISO-8859-1]。
解决:String str = new String(bytes, "ISO-8859-1");
谜题19:漂亮的火花
public class Classifier {
public static void main(String[] args) {
System.out.println(
classify('n') + classify('+') + classify('2'));
}
static String classify(char ch) {
if ("0123456789".indexOf(ch) >= 0)
return "NUMERAL ";
if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0)
return "LETTER ";
/* (Operators not supported yet)
if ("+-*/&|!=" >= 0)
return "OPERATOR ";
*/
return "UNKNOWN";
}
}
题目:无法编译
原因: 注释代码
谜题20:我的类是什么?
package com.javapuzzlers;
public class Me {
public static void main(String[] args){
System.out.println(
Me.class.getName().
replaceAll(".","/") + ".class");
}
}
题目:
解决办法 :java.util.regex.Pattern.quote。它接受一个字符串作为参数,并可以添加必需的转义字符,
它将返回一个正则表达式字符串,该字符串将精确匹配输入的字符串。下面是使用该方法
之后的程序:
package com.javapuzzlers;
import java.util.regex.Pattern;
public class Me {
public staticvoid main(String[] args){
System.out.println(Me.class.getName().
replaceAll(Pattern.quote("."),"/")+ ".class");
}
}
谜题21:我的类是什么?II
package com.javapuzzlers;
import java.io.File;
public class MeToo {
public static void main(String[] args){
System.out.println(MeToo.class.getName().
replaceAll("\\.", File.separator) + ".class");
}
}
题目:当你在Windows上运行该程序时,替代字符串是单独的一个反斜杠,它是无效的。不可否认
原因:
解决办法:
System.out.println(MeToo.class.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator))+ ".class");
System.out.println(MeToo.class.getName().replace(".",File.separator) + ".class");
谜题22:URL的愚弄
public class BrowserTest {
public static void main(String[] args) {
System.out.print("iexplore:");
http://www.google.com;
System.out.println(":maximize");
}
}
题目:打印iexplore::maximize。
原因:在Java中很少需要标号,这多亏了Java没有goto语句。在本谜题中所引用的“Java编程语言中很少被
人了解的特性”实际上就是你可以在任何语句前面放置标号。这个程序标注了一个表达式语句,它是
合法的,但是却没什么用处。
注:
提醒你,如果你真的想要使用标号,那么应该用一种更合理的方式来格式化程序:
public class BrowserTest {
public staticvoid main(String[] args) {
System.out.print("iexplore:");
http: //www.google.com;
System.out.println(":maximize");
}
}
谜题23:不劳无获
import java.util.Random;
public class Rhymes {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(2)) {
case 1: word = new StringBuffer('P');
case 2: word = new StringBuffer('G');
default: word = new StringBuffer('M');
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}
题目: 永远输出ain
原因: 第一个bug:是所选取的随机数使得switch语句只能到达其三种情况中的两种。
第二个bug:bug是在不同的情况(case)中没有任何break语句。不论switch
表达式为何值,该程序都将执行其相对应的case以及所有后续的case[JLS14.11]。
第三个bug:也是最微妙的一个bug是表达式newStringBuffer('M'),你可能对StringBuffer(char)
构造器并不熟悉,这很容易解释:它压根就不存在。StringBuffer有一个无参数的构
造器,一个接受一个String作为字符串缓冲区初始内容的构造器,以及一个接受一个
int作为缓冲区初始容量的构造器。在本例中,编译器会选择接受int的构造器,通过
拓宽原始类型转换把字符数值'M'转换为一个int数值77[JLS 5.1.2]。换句话说,
new StringBuffer('M')返回的是一个具有初始容量77的空的字符串缓冲区。该程序余
下的部分将字符a、i和n添加到了这个空字符串缓冲区中,并打印出该字符串缓冲区那
总是ain的内容。
解决方案:
import java.util.Random;
public class Rhymes2 {
private static Random rnd = new Random();
public static void main(String[] args) {
System.out.println("PGM".charAt(rnd.nextInt(3)) + "ain");
}
}