Java解惑笔记(一)
《java解惑》是由Joshua Bloch(美)、Neal Gafter(美)编著,陈昊鹏翻译的计算机语言类丛书,该书由人民邮电出版社2006年发行出版。该书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺陷。
这里是笔记,所以不要脸的贴上原创标签,但是一切荣誉与属于原作者,我只是个读者和搬运工。
之前的没有做笔记的部分,那就算了吧,有兴趣的可以去看书,是表达式之谜,字符之谜。
TEST20:类名转换1
public class Test20 {
public static void main(String[] args) {
System.out.println(
Test20.class.getName().replaceAll(".", "/")+"class"
);
}
}
console://class
原因:String.replaceAll接受了一个正则表达式作为它的第一个参数。
正则表达式”.”可以匹配任何一个字符,因此String中每一个字符都被替换成”/“
Test20.class.getName().replaceAll("\\.", "/")+".class"
TEST21:类名转换2
public class Test21 {
public static void main(String[] args) {
System.out.println(
Test21.class.getName().
replaceAll("\\.", File.separator)+".class"
);
}
}
consle:Exception in thread “main” java.lang.IllegalArgumentException: character to be escaped is missing
原因:在windows平台上 File.separator 为 “/” ,单独出现一个 “\” 是无效的。
解决办法:String.replace(CharSequence,CharSequence),她将两个参数都作为字符常量处理。
Test21.class.getName().
replace(".", File.separator)+".class"
TEST22 :URL的愚弄
public class Test22 {
public static void main(String[] args) {
System.out.print("iexplore:");
http://www.baidu.com;
System.out.println(":maximize");
}
}
consle:iexplore::maximize
原因:程序中出现的URL是一个语句标号(statement label)后面跟着尾注释。它是合法的,但是没有什么用处。
仔细的写注释,并让他跟上朝代。
TEST23:不劳而获
package com.test;
import java.io.File;
import java.util.Random;
public class Test23 {
private static Random random = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch (random.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);
}
}
consle:只能输出ain
原因:栅栏柱错误,Random.nextInt(2),只可能取到0,1两个随机数。
:case没有后续的break语句时,将会后续的case。
:new StringBuffer(‘M’),不能实现预期目标。StringBuffer有三个构造器:无参,接受一个String作为字符串缓冲区初始内容的构造器,一个接受一个int作为缓冲器初始容量的构造器。new StringBuffer(‘M’)将被编译器作为int构造器,通过扩宽原生类型把M转换为int数值77.
public class Test23 {
private static Random random = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch (random.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);
}
}
更优雅的版本:
public class Test23 {
private static Random random = new Random();
public static void main(String[] args) {
System.out.println("PGM".charAt(random.nextInt(3))+"ain");
}
}
更通用的版本:
public class Test23 {
private static Random random = new Random();
public static void main(String[] args) {
String a[] = {"Pain","Gain","Main"};
System.out.println(randomElement(a));
}
public static String randomElement(String[] a) {
return a[random.nextInt(a.length)];
}
}
循环之谜
TEST24:Byte
public class Test24 {
public static void main(String[] args) {
for(byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++){
if (b == 0x90) {
System.out.println("JOY");
}
}
}
}
输出为空
原因:0x90超出了Byte的数值范围。Byte为8bit,但是是一个有符号数,其最高位是符号位取值范围为-128到+127。常量0x90(十六进制)是一个最高位被占位的8位int数值,其值为+144。
解决方法:避免混合类型比较
if (b == (byte)0x90)
用一个屏蔽码来消除符号扩展的影响
if ((b & 0xff)== 0x90)
:用声明的常量代替 0x90
private static final byte Target = (byte)0x90;
TEST25:无情的增量操作
public class Test25{
public static void main(String[] args) {
int j = 0;
for(int i = 0; i < 100; i++) {
j = j++;
}
System.out.println(j);
}
}
consle:0
原因:j = j++;没有达到预期的效果。j++的值等于j在执行增量操作之前的初始值。
解决方法:
for(int i = 0; i < 100; i++)
j++;
不要在单个表达式中对相同的变量赋值超过一次。
TEST26:在循环中
public class TEST {
public static final int END = Integer.MAX_VALUE;
public static final int START = END - 100;
public static void main (String[] args){
int count = 0;
for (int i = START; i <= END; i++){
count++;
}
System.out.print(count);
}
}
输出count结果:不输出
原因:陷入无限循环
当i达到Integer.MAX_VALUE,时,再次执行增量操作。它将绕回到Integer.MIN_VALUE.
解决办法:long i
TEST29:循环者的新娘
提供一个声明,将下面的循环转变为无限循环
while(i != i){}
解决办法:NaN(not a number):没有良好的数字定义的浮点数计算的值。
NaN不等于任何浮点数值,包括它自身在内
任何浮点操作,只要它的一个或多个操作数为NaN,那么其结果为NaN
一旦一个计算产生了NaN,它就被损坏了,且无法被修复。
double i = Double.NaN;
或 double i = 0.0 / 0.0;
TEST30:循环者的爱子
提供一个声明,将下面的循环转变为无限循环。但不可使用浮点数
while(i != i + 0){}
解决办法:将i定义为String类型。
唯一的“+”操作符有定义的非数值类型就是String。其执行字符串的连接。
TEST32:循环者的诅咒
提供对i,j的声明,使得循环变成无限
while(i <= j && j <= i && i != j){}
一波:
< ,<=,> ,>=,两侧的操作数类型必须可以转换成原始的数字类型(byte,char,short,int,和double)
那被包装的数值类型有(Byte,Character,Short,Integer,Long,Float,Double)
对于包装类,< ,<=,> ,>=,,操作将解包转换。而 = 是引用ID比较,即是判断否是同一个对象。
解决办法:
Integer i = new Integer(0);
Integer j = new Integer(0);
TEST33:循环者遇到狼人
提供一个对i的声明,将下面的循环转变为无限
while(i != 0 && i == -i)
1,i不可为NaN,因为 ”-i“中的”-“是一元符号,则i必须是数字类型数值,否则不合法。
2,有符号数的整数雷鑫使用二进制补码运算,取一个数的复值,要对其中每一位取反,然后加一,其中0有唯一的表示。
3,正和负数的个数不一样。至少有一个int数值,其负值不能正确的表示为int数值。
4,Interger.MIN_VALUE:-2的31次方。它的十六进制表示为0x80000000,为这个值取负,得到:0x7fffffff+1即0x80000000.满足条件。
5,同理,Long.MIN_VALUE.也可。
java使用2的补码的算术运算是不对称的,对于每一种有符号数,负数比正数要多出一个。
TEST34:被计数击倒
count的值为多少。
public class TEST {
public static void main (String[] args){
int count = 0;
final int START = 2000000000;
for (float f = START; f < START + 50; f++){
count++;
}
System.out.print((float)(START));
System.out.print((float)(START+50));
System.out.print(count);
}
}
解:
count为0,不循环。
(float)(START)==(float)(START+50)==2.0E9。
原因:float只提供到24位精度。占满。
使用浮点数,最佳选择是double。