1>谜题1:奇数性
下面的方法意图确定它那唯一的参数是否是一个奇数
public static boolean isOdd(int i){
return i % 2 == 1;
}
因为当 i 是一个负奇数时,i % 2 等于-1而不是1,所以应为:
public static boolean isOdd(int i){
return i % 2 != 0;
}
总之:无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号;
2>谜题2:找零时刻
下面是一个试图解决上述问题的程序,它会打印出什么呢?
public class Change{
public static void main(String args[]){
System.out.println(2.00 - 1.10);
}
}
//打印的是0.8999999999999999
解决方案-->
import java.math.BigDecimal;
public class Change1{
public static void main(String args[]){
System.out.println(new BigDecimal("2.00").
subtract(new BigDecimal("1.10")));
}
}
//打印出0.90
总之, 在需要精确答案的地方,要避免使用float和double;对于货币计算,要使用int、long或BigDecimal
3>谜题3:长整除
这个程序会打印出什么呢?
public class LongDivision{
public static void main(String args[]){
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}
//它打印的是5
解决方案-->
public class LongDivision{
public static void main(String args[ ]){
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}
//打印出100
当你在操作很大的数字时,千万要提防溢出——它可是一个缄默杀手。
4>谜题4:初级问题
System.out.println(12345+5432l);//打印出17777
解决方案:-->System.out.println(12345+5432L);
数字1的水平笔划(称为“臂(arm)”)和垂直笔划(称为“茎(stem)”)之间是一个锐角,而与此相对照的是,小写字母l的臂和茎之间是一个直角。
总之,小写字母l和数字1在大多数打字机字体中都是几乎一样的,所以long型用L;
5>谜题5:十六进制的趣事
这个程序会打印出什么呢?
public class JoyOfHex{
public static void main(String[] args){
System.out.println(
Long.toHexString(0x100000000L + 0xcafebabe));
}
}
//打印的是cafebabe
解决方案:
public class JoyOfHex{
public static void main(String[] args){
System.out.println(
Long.toHexString(0x100000000L + 0xcafebabeL));
}
}
//打印出1cafebabe
总之:混合类型的计算可能会产生混淆,尤其是十六进制和八进制字面常量无需显式的减号符号就可以表示负的数值。为了避免这种窘境,通常最好是避免混合类型的计算
6>谜题6:多重转型
它到底会打印出什么呢?
public class Multicast{
public static void main (String[] args){
System.out.println((int)(char)(byte) -1);
}
}
//它打印出的是65535
有一条很简单的规则能够描述从较窄的整型转换成较宽的整型时的符号扩展行为:如果最初的数值类型是有符号的,那么就执行符号扩展;如果它是char无符号位,那么不管它将要被转换成什么类型,都执行零扩展,所以过程为:-1-->-1-->65535-->65535
7>那么它会打印出什么呢?
public class CleverSwap{
public static void main(String[] args){
int x = 1984; // (0x7c0)
int y = 2001; // (0x7d1)
x^= y^= x^= y;
System.out.println("x= " + x + "; y= " + y);
}
}
//打印的是 x = 0; y = 1984
这个教训很简单:在单个的表达式中不要对相同的变量赋值两次
8>谜题8:Dos Equis
下面的程序将会打印出什么呢?
public class DosEquis{
public static void main(String[] args){
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
}
}
//它打印出来的是X88
如果一个操作数的类型是T,T表示byte、short或char,而另一个操作数是一个int类型的常量表达式,它的值是可以用类型T表示的,那么条件表达式的类型就是T。否则,将对操作数类型运用二进制数字提升,而条件表达式的类型就是第二个和第三个操作数被提升之后的类型
总之,通常最好是在条件表达式中使用类型相同的第二和第三操作数。
9>谜题9:半斤
复合赋值操作符包括 +=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=和|=)Java语言规范中讲到,复合赋值 E1 op= E2等价于简单赋值E1 = (T)((E1)op(E2)),其中T是E1的类型,除非E1只被计算一次
总之,复合赋值操作符会悄悄地产生一个转型
10>谜题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);
}
}
//ABC easy as [C@16f0472
解决方法:System.out.println(letters + " easy as " +String.valueOf(numbers));
总之,char数组不是字符串。要想将一个char数组转换成一个字符串,就要调用String.valueOf(char[])方法。
11>总之,要确保字符\u不出现在一个合法的Unicode转义字符上下文之外,即使是在注释中也是如此。在机器生成的代码中要特别注意此问题。
12>要想将一个char数组转换成一个字符串,就要调用String.valueOf(char[])方法。例如:
char[] numbers = {'1', '2', '3'};
System.out.println(" easy as " +String.valueOf(numbers));//easy as 123;
13>在比较对象引用时,你应该优先使用equals方法而不是 == 操作符,除非你需要比较的是对象的标识而不是对象的值。
14>如果不是必要的话,请不要使用Unicode;
15>千万不要用一个return、break、continue或throw来退出一个finally语句块,并且千万不要允许将一个受检查的异常传播到一个finally语句块之外去。
类之惑:
16>谜题46:令人混淆的构造器案例
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
//double array
Java的重载解析过程是以两阶段运行的。第一阶段选取所有可获得并且可应用的方法或构造器。第二阶段在第一阶段选取的方法或构造器中选取最精确的一个。如果一个方法或构造器可以接受传递给另一个方法或构造器的任何参数,那么我们就说第一个方法比第二个方法缺乏精确性;
17>静态域由声明它的类和子类所共享同一份;
18>在任何情况下,你都务必要记住:不要在构造器中调用可覆 写的方法。在实例初始化中产生的循环将是致命的。
19>名字重用的术语表
覆写(override)
一个实例方法可以覆写(override)在其超类中可访问到的具有相同签名的所有实例方法[JLS 8.4.8.1],从而使能了动态分派(dynamic dispatch);换句话说,VM将基于实例的运行期类型来选择要调用的覆写方法[JLS 15.12.4.4]。覆写是面向对象编程技术的基础,并且是唯一没有被普遍劝阻的名字重用形式:
class Base {
public void f() { }
}
class Derived extends Base {
public void f() { } // overrides Base.f()
}
隐藏(hide)
一个域、静态方法或成员类型可以分别隐藏(hide)在其超类中可访问到的具有相同名字(对方法而言就是相同的方法签名)的所有域、静态方法或成员类型。隐藏一个成员将阻止其被继承[JLS 8.3, 8.4.8.2, 8.5]:
class Base {
public static void f() { }
}
class Derived extends Base {
private static void f() { } // hides Base.f()
}
重载(overload)
在某个类中的方法可以重载(overload)另一个方法,只要它们具有相同的名字和不同的签名。由调用所指定的重载方法是在编译期选定的[JLS 8.4.9, 15.12.2]:
class CircuitBreaker {
public void f(int i) { } // int overloading
public void f(String s) { } // String overloading
}
遮蔽(shadow)
一个变量、方法或类型可以分别遮蔽(shadow)在一个闭合的文本范围内的具有相同名字的所有变量、方法或类型。如果一个实体被遮蔽了,那么你用它的简单名是无法引用到它的;根据实体的不同,有时你根本就无法引用到它[JLS 6.3.1]:
class WhoKnows {
static String sentence = "I don't know.";
public static woid main(String[ ] args) {
String sentence = “I know!”; // shadows static field
System.out.println(sentence); // prints local variable
}
}
尽管遮蔽通常是被劝阻的,但是有一种通用的惯用法确实涉及遮蔽。构造器经常将来自其所在类的某个域名重用为一个参数,以传递这个命名域的值。这种惯用法并不是没有风险,但是大多数Java程序员都认为这种风格带来的实惠要超过其风险:
class Belt {
private final int size;
public Belt(int size) { // Parameter shadows Belt.size
this.size = size;
}
}
遮掩(obscure)
一个变量可以遮掩具有相同名字的一个类型,只要它们都在同一个范围内:如果这个名字被用于变量与类型都被许可的范围,那么它将引用到变量上。相似地,一个变量或一个类型可以遮掩一个包。遮掩是唯一一种两个名字位于不同的名字空间的名字重用形式,这些名字空间包括:变量、包、方法或类型。如果一个类型或一个包被遮掩了,那么你不能通过其简单名引用到它,除非是在这样一个上下文环境中,即语法只允许在其名字空间中出现一种名字。遵守命名习惯就可以极大地消除产生遮掩的可能性[JLS 6.3.2, 6.5]:
public class Obscure {
static String System; // Obscures type java.lang.System
public static void main(String[ ] args) {
// Next line won't compile: System refers to static field
System.out.println(“hello, obscure world!”);
}
}
20>本书小结:
a,long类型应加L;
b,?:操作数时,2,3操作数应使用相同的数据类型。
c,整数类型是不对称的。
d,Integer.MAX_VALUE上终止以int为索引的循环是困难的。(ps:Integer.MAX_VALUE=9223372036854775807,呵呵,够大了吧!!0)
e,如果你想让一个类不可实例化,那么就添加一个私有构造器吧。
f,char类型值只会默认地转换成int,而不是String,想把一个char的类型转换成一个字符串,应该使用String.valueOf(char)。
g,永远不要使用Thread.run()。
h,覆写了equals方法而不覆写hashCode方法可能会引发不定的行为,所以覆写了equals方法就要一并覆写hashCode方法。
分享到:
相关推荐
Google首席工程师Joshua Bloch,作为Java语言和库的主要设计师之一,对API设计有着深厚的理解和丰富的经验。在他的讨论中,我们可以提取出以下几个关键知识点: 1. **明确目标**:API设计首先要明确其目标和用途。...
读书笔记:Effective Java 中文版(第2版)总结 (美)Joshua Bloch 著
在2009年的JavaOne大会上,知名Java专家Joshua Bloch和Neal Gafter共同提出了一系列有趣的编程谜题,旨在挑战参会者对Java语言特性的理解。这些谜题涵盖了一些核心概念,例如字符串处理、集合操作以及迭代器的实现。...
《Java Puzzlers Sampler》是由Java领域的权威专家Joshua Bloch和Neal Gafter共同编著的一本书,书中收录了一系列令人困惑、出乎意料的Java编程问题,旨在帮助开发者更好地理解和避免这些潜在的陷阱。书中的...
Joshua Bloch 所著《Effective Java 第二版》一书摘要这是我对 Joshua Bloch 所著《Effective Java 第 2 版》的总结。我在学习时会用到它,也可以作为快速参考。它并不是这本书的独立替代品,所以如果你真的想学习...
Joshua Bloch和Neal Gafter在加入Google之前,都在Sun Microsystems担任资深工程师,对Java核心和类库开发有着显著贡献。他们共同撰写了《Java Puzzlers》一书,而Bloch还著有广受欢迎的《Effective Java》。他们的...
《Java解惑》是由Joshua Bloch(美)、Neal Gafter(美)编著,陈昊鹏翻译的计算机语言类丛书,该书由人民邮电出版社2006年发行出版。该书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序...
《Java Puzzlers》是Java开发领域的经典之作,由Joshua Bloch和Neal Gafter合著。这本书通过一系列精心设计的编程谜题,揭示了Java语言中容易被忽视的陷阱和误解。这些谜题覆盖了类加载、内存模型、类型转换、异常...
该存储库保存着该项目,该项目实现了一些小程序,这些小程序实现了Joshua Bloch和Neal Gafter的Java Puzzlers书中的精选难题,并且打算用于Code Camp讲座。 可以通过获得《 Java拼图-陷阱,陷阱和特例》一书,如果...
《有效Java》是由著名程序员和Java专家Joshua Bloch撰写的一本经典编程书籍,它为Java开发者提供了许多实用且深入的编程建议,旨在帮助我们编写出更高效、更可靠、更易于维护的Java代码。这本书包含了37个条目,每个...
Java中文版------Java四大名著--------Joshua Bloch Java与模式------------------------------阎宏 ##2、C# C#程序设计-------Charles Petzold“windows编程泰山北斗”---C#语言“倚天屠龙双剑” C# Primer中文版--...
作者:Joshua Bloch Neal Gafter 版本:中文版 主要内容: Java解惑(英文版)—典藏原版书苑 目录 1 introduction 2 expressive puzzlers puzzle 1:oddity puzzle 2:time for a change puzzle 3:long pision ...
9. **JavaPuzzlers概念**:原版的《JavaPuzzlers》由Joshua Bloch和Neal Gafter编写,他们提出了许多关于Java编程的迷惑性示例,中文版可能会对这些经典案例进行翻译和解释。 通过解决这些“Java谜题”,读者不仅...
《Java Puzzlers》是一本深受Java开发者喜爱的书籍,由Joshua Bloch和Neal Gafter合著。这本书专门探讨了Java编程语言中容易让人迷惑的陷阱和常见误解,通过一系列精心设计的“puzzlers”(谜题)来揭示这些陷阱,并...
《Effective Java》是Java编程领域的一本经典著作,由Joshua Bloch撰写,第二版发布于2008年。这本书旨在提供实用的编程指导,帮助开发者写出更高效、更可维护的Java代码。以下是对书中核心知识点的详细解读: 1. *...
- **作者:** Joshua Bloch & Neal Gafter - **主要内容:** 本书通过一系列有趣的编程难题来揭示Java语言中的微妙之处。 - **涵盖知识点:** - Java语言的怪异行为 - 常见陷阱 - 语言特性的深层次理解等。 **2...
How-to-Design-a-Good-API-and-Why-it-Matters The offline pdf already in this repo. ##API的重要性 公司最大的资产 公司最大的负债 ##好的API特征(和一个好的开源框架类似) 易于学习 即使没有文档,易于使用 ...
高级java笔试题 Java 宝典 一切都是逻辑。 十大名言: Talk is cheap, show me code.(知易行难) -- Linus Torvalds(Linux创始人) 源码面前了无秘密。-- 侯捷 《STL源码剖析》作者 不要重复发明轮子。-- Rod Johnson...
Java是一种广泛使用的编程语言,特别适合于跨平台的应用程序开发。以下是一些Java项目开发资源,可以帮助开发者从基础到高级阶段进行学习和... - 《Effective Java》(作者:Joshua Bloch)。 5. **开源项目和库**: