`

Java教训记录--《Java Puzzle》读书笔记

    博客分类:
  • Java
阅读更多
--表达式谜题

谜题7:
在单个的表达式中不要对相同的变量赋值两次。

谜题8:
如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。换句话说,你可以通过绕过混合类型的计算来避免大麻烦。如果一个操作数的类型是 T,T 表示 byte、short 或 char,而另一个操作数是一个 int 类型的常量表达式,它的值是可以用类型 T 表示的,那么条件表达式的类型就是 T。否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。

谜题9:
复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。 如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原始类型转换。

谜题10:
复合赋值操作符要求两个操作数都是原始类型的,例如 int,或包装了的原始类型,例如 Integer,但是有一个例外:如果在+=操作符左侧的操作数是 String类型的,那么它允许右侧的操作数是任意类型,在这种情况下,该操作符执行的是字符串连接操作。

--字符谜题

谜题11:
编译器在计算常量表达式'H'+'a'时,是通过我们熟知的拓宽原始类型转换将两个具有字符型数值的操作数('H'和'a')提升为 int 数值而实现的。从 char 到int 的拓宽原始类型转换是将 16 位的 char 数值零扩展到 32 位的 int。所以:
System.out.print('H'+'a'); 打印出的是169.

谜题12:
·那在一个非空 char 数组上面调用 toString 方法会产生什么样的行为呢?数组是从 Object 那里继承的 toString 方法[JLS 10.7],规范中描述到:“返回一个字符串,它包含了该对象所属类的名字,'@'符号,以及表示对象散列码的一个无符号十六进制整数”[Java-API]。有关 Class.getName 的规范描述到:在char[]类型的类对象上调用该方法的结果为字符串"[C"。
·总之,char 数组不是字符串。要想将一个 char 数组转换成一个字符串,就要调用 String.valueOf(char[])方法。

谜题13:
字符串连接[+]的优先级不应该和加法一样。
+ 操作符,不论是用作加法还是字符串连接操作,它都比 == 操作符的优先级高.
e.g. System.out. println("Animals are equal: " + pig == dog);

谜题14:
在字符串和字符字面常量中要优先选择的是转义字符序列,而不是Unicode 转义字符。Unicode 转义字符可能会因为它们在编译序列中被处理得过早而引起混乱。不要使用 Unicode 转义字符来表示 ASCII 字符。在字符串和字符字面常量中,应该使用转义字符序列;对于除这些字面常量之外的情况,应该直接将 ASCII 字符插入到源文件中。 

谜题15:
要确保字符\u 不出现在一个合法的 Unicode 转义字符上下文之外,即使是在注释中也是如此。在机器生成的代码中要特别注意此问题。 

谜题17:
Unicode 转义字符只有在你要向程序中插入用其他任何方式都无法表示的字符时才是必需的,除此之外的任何情况都不应该避免使用它们。Unicode 转义字符降低了程序的清晰度,并且增加了产生 bug 的可能性。 

谜题18:
String(byte[])构造器,有关它的规范描述道:“在通过解码使用平台缺省字符集的指定 byte 数组来构造一个新的 String 时,该新String 的长度是字符集的一个函数,因此,它可能不等于 byte 数组的长度。当给定的所有字节在缺省字符集中并非全部有效时,这个构造器的行为是不确定的”[Java-API]。 
幸运的是,你没有被强制要求必须去容忍各种稀奇古怪的缺省字符集。当你在char 序列和 byte 序列之间做转换时,你可以且通常是应该显式地指定字符集。除了接受 byte 数字之外,还可以接受一个字符集名称的 String 构造器就是专为此目的而设计的。如果你用下面的构造器去替换在最初的程序中的 String 构造器,那么不管缺省的字符集是什么,该程序都保证能够按照顺序打印从 0 到 255的整数: 
String str = new String(bytes, "ISO-8859-1");
这个谜题的教训是:每当你要将一个 byte 序列转换成一个 String 时,你都在使用某一个字符集,不管你是否显式地指定了它。如果你想让你的程序的行为是可预知的,那么就请你在每次使用字符集时都明确地指定。

谜题19:
块注释不能可靠地注释掉代码段,应该用单行的注释序列来代替。

谜题20:
正则表达式“.”可以匹配任何单个的字符。要想只匹配句点符号,在正则表达式中的句点必须在其前面添加一个反斜杠(\)进行转义。因为反斜杠字符在字面含义的字符串中具有特殊的含义——它标识转义字符序列的开始——因此反斜杠自身必须用另一个反斜杠来转义,这样就可以产生一个转义字符序列,它可以在字面含义的字符串中生成一个反斜杠。
e.g. Me.class.getName().replaceAll("\\.","/")

谜题23:
栅栏柱错误(fencepost error)。这个名字来源于对下面这个问题最常见的但却是错误的答案,如果你要建造一个100 英尺长的栅栏,其栅栏柱间隔为 10 英尺,那么你需要多少根栅栏柱呢?11根或 9 根都是正确答案,这取决于是否要在栅栏的两端树立栅栏柱,但是 10 根却是错误的。要当心栅栏柱错误,每当你在处理长度、范围或模数的时候,都要仔细确定其端点是否应该被包括在内,并且要确保你的代码的行为要与其相对应。
你可能对 StringBuffer(char)构造器并不熟悉,这很容易解释:它压根就不存在。StringBuffer 有一个无参数的构造器,一个接受一个 String 作为字符串缓冲区初始内容的构造器,以及一个接受一个 int 作为缓冲区初始容量的构造器。在本例中,编译器会选择接受 int 的构造器,通过拓宽原始类型转换把字符数值'M'转换为一个 int 数值 77[JLS  5.1.2]。换句话说,new  StringBuffer('M')返回的是一个具有初始容量 77 的空的字符串缓
冲区。
总结一下:首先,要当心栅栏柱错误。其次,牢记在 switch 语句的每一个case 中都放置一条 break 语句。第三,要使用常用的惯用法和 API,并且当你在离开老路子的时候,一定要参考相关的文档。第四,一个 char 不是一个 String,而是更像一个 int。

--循环谜题

谜题26:
如果你需要的循环会迭代到 int 数值的边界附近时,你最好是使用一个 long 变量作为循环索引。只需将循环索引的类型从 int 改变为 long 就可以解决该问题。

谜题27:
·问题在于(-1  <<  32)等于-1 而不是 0,因为移位操作符之使用其右操作数的5 位作为移位长度。或者是低 6 位,如果其左操作数是一个 long 类数值[JLS15.19]。 
·这条规则作用于全部的三个移位操作符:<<、>>和>>>。移位长度总是介于 0 到31 之间,如果左操作数是 long 类型的,则介于 0 到 63 之间。这个长度是对 32取余的,如果左操作数是 long 类型的,则对 64 取余。如果试图对一个 int 数值移位 32 位,或者是对一个 long 数值移位 64 位,都只能返回这个数值自身的值。没有任何移位长度可以让一个 int 数值丢弃其所有的 32 位,或者是让一个 long数值丢弃其所有的 64 位。 

--异常谜题

谜题36:
在一个 try-finally 语句中,finally 语句块总是在控制权离开 try 语句块时执行的[JLS 14.20.2]。无论 try 语句块是正常结束的,还是意外结束的,情况都是如此。一条语句或一个语句块在它抛出了一个异常,或者对某个封闭型语句执行了一个 break 或 continue,或是象这个程序一样在方法中执行了一个return 时,将发生意外结束。它们之所以被称为意外结束,是因为它们阻止程序去按顺序执行下面的语句。
e.g.
    static boolean decision() { 
        try { 
            return true; 
        } finally { 
            return false; 
        } 
    }  

返回false

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics