java in puzzles summary

第二章 Expressive Puzzles

第三章 with Character

Puzzle 11 The Last Laugh

执行下面的代码

1
2
3
4
5
6
public class LastLaugh {
public static void main(String args[]) {
System.out.print("H" + "a");
System.out.print(’H’ + ’a’);
}
}

预期的打印结果是

1
HaHa

但是实际情况如下

1
Ha169

第二个打印语句实际上是将两个字符的ASCII码值相加的结果

下面的语句是可以将两个字符连起来的

1
2
System.out.print(""+'H' + 'a');
// 输出Ha

下面的例子中 不会输出两个数字相加的结果,而是将数字相加的操作作为字符串打印出来

1
2
3
System.out.println("2 + 2 = " + 2+2);
// 输出2 + 2 = 22
显然我们期望的结果是 2 + 2 = 4

从 Java1.5 开始,下面的语句也可以将 两个 char ,作为字符串链接输出

1
System.out.printf("%c%c", 'H', 'a');

结论

the + operator performs string concatenation if and only if at least one of its operands is of type String; otherwise, it performs addition.

当且仅当运算符中的至少一个是字符串类型时,+ 运算符才执行字符串连接,在其他情况下执行加操作

如果需要进行字符串连接的几个值之间没有一个是 String 类型,那么有以下几个选择:

  1. 增加一个空字符串 ""
  2. 使用 String.valueOf() 进行显式转换;
  3. 使用 StringBuffer/StringBuilder
  4. 使用上面提到的 System.out.printf("%c%c", 'H', 'a');的写法

Puzzle12 ABC

运行下面的代码

1
2
3
String letters = "ABC";
char[] numbers = { '1', '2', '3' };
System.out.println(letters + " easy as " + numbers);

预期输出

1
ABC easy as 123

实际输出

1
ABC easy as [C@5e2de80c

之所以会出现上面的结果是因为 在打印的时候 numbers 调用了 ObjecttoString 方法:

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

所以会导致 出现上面的字符串

为了解决上面的问题,可以调用 String.valueOf(char[] data) 方法,该方法会答应出字符数组内所有的字符:

1
2
3
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + String.valueOf(numbers));

上面的代码输出

1
ABC easy as 123

也可以使用下面的分开输出的方法

调用 println(char[] data) 方法:

1
2
3
4
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.print(letters + " easy as ");
System.out.println(numbers);

总结

char arrays are not strings. To convert a char array to astring, invoke String.valueOf(char[]) .char

数组不是字符串。 要将char数组转换为字符串,请调用String.valueOf(char [])。

Puzzle 14 Escape Rout

这里实际上是使用 Unicode 标识 ASCII 字符

1
System.out.println("a\u0022.length() + \u0022b".length());

打印结果是 2

等同于

1
System.out.println("a".length() + "b".length());

使用转义之后,即可打印字符串的长度

1
2
System.out.println("a\".length() + \"b".length());
// 16

结论

In summary, prefer escape sequences to Unicode escapes in string and character literals.

总之,相对于字符串和字符文字中的Unicode转义,更喜欢转义序列。

Do not use Unicode escapes to represent ASCII characters.

不要使用Unicode转义符表示ASCII字符。

Puzzle 20 What’s My Class?

下面的代码是将字符串中的所有点号转换成斜杠

1
2
3
4
5
public static void main(String[] args) {
System.out.println(MyTest.class.getName());
System.out.println(
MyTest.class.getName().replaceAll(".", "/") + ".class");
}

最后打印的结果是

1
2
com.bruceliu.algorithms.puzzles.MyTest
//////////////////////////////////////.class

出现这个结果的原因是 replaceAll()方法的第一个参数是一个正则表达式(regular expression),而不是一个字符串序列

正则表达式“。” 匹配任何单个字符,因此类名的每个字符都用斜杠替换,产生我们看到的输出。

1
2
3
4
5
6
public static void main(String[] args) {
System.out.println(MyTest.class.getName());
System.out.println(
MyTest.class.getName().replaceAll("\\.", "/") + ".class");
}

JAVA 1.5 开始,也可以使用以下代码

1
2
System.out.println(MyTest.class.getName().
replaceAll(Pattern.quote("."), "/") + ".class");

要仅匹配句点字符,正则表达式中的句点必须通过在其前面加上反斜杠(\)进行转义。 因为反斜杠字符在字符串文字中有特殊含义-它开始一个转义序列-后面的斜杠本身必须用第二个反斜杠转义。

Puzzle: 21

执行下面的代码

1
System.out.println(MeToo.class.getName().replaceAll("\\.", File.separator) + ".class");

这段代码在 windows 平台会报错

File.separatorwindows 平台是反斜杠 backslash,反斜杠会对其之后的字符进行转义。而在上面的例子中,反斜杠后面没有其他字符了,所以第二个参数是一个无效的输入。

java 1.5 之后有两种方法解决这个问题,方法一:

1
System.out.println(MyTest.class.getName().replaceAll("\\.", Matcher.quoteReplacement(File.separator))+ ".class");

方法二:

1
2
System.out.println(MyTest.class.getName().replace(".", File.separator) + ".class");
注意 replace 方法的第一个参数是 字符序列 而不是正则表达式

java 1.5 之前同样也有解决方法

1
System.out.println(MyTest.class.getName().replace('.', File.separatorChar) + ".class");

Puzzle: 23 No Pain,No Gain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
}
}
上面代码的目的是 打印出 Pain,Gain或者Main
但是运行结果是 只打印 `ain`

有如下三个bug

  1. rnd.nextInt(2) 的取值范围是[0,2),2是永远不可能取到的,这里应该改成 rnd.nexInt(3);
  2. switch 语句没有 break,应该给每一个分支增加 break;
  3. new StringBuffer(char) 实际调用的是 new StringBuffer(int capacity),即给 StringBuffer 初始化大小,并不是 new StingBuffer(String str) 初始化字符串

可以进行如下的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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(3)) {
case 1:
word = new StringBuffer("P");
break;
case 2:
word = new StringBuffer("G");
break;
default:
word = new StringBuffer("M");
break;
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}

这个代码还可以写得更加优雅

1
System.out.println("PGM".charAt(rnd.nextInt(3)) + "ain");

第四章 Loopy Puzzlers

Puzzle 24: A Big Delight in Every Byte

作者

Bruce Liu

发布于

2020-06-03

更新于

2022-11-12

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.