读>笔记

 
解惑26:在循环中
无论你在何时使用了一个整数类型,都要意识到其边界条件
解惑27:变幻莫测的i
移位操作符只使用其右操作数的低5位作为移位长度。
如果可能的话,移位长度应该是常量。
解惑28:循环者
用一个double或float数值来表示无穷大是可以的。
将一个很小的浮点数加到一个很大的浮点数上时,将不会改变大浮点数的值。
二进制浮点数自是对实际算数的一种近似。
解惑29:循环者的新娘
NaN不等于任何浮点数值,包括它自身在内。
任何浮点操作,只要它的一个或者多个操作数为NaN,那么其结果为NaN。
一旦一个计算产生了NaN,它就被破坏了,没有任何更进一步的计算可以修复这样的破坏。
解惑30:循环者的爱子
操作符重载是很容易令人误解的。
对于程序的可读性来说,好的变量名、方法名和类名至少与好的注释同等重要。
解惑31:循环者的鬼魂
窄化原生类型转换可能会丢失级数的信息,或者是数值的精度
不要在short、byte或char类型的变量之上使用复合赋值操作。
解惑32:循环者的诅咒
被包装的数字类型上
当两个操作数都是被包装的数字类型时,数值比较操作符和判等操作符的行为存在着更本的差异;数值比较操作符执行的是值比较,而判断操作符执行的是引用标识的比较。
解惑33:循环者遇到了狼人
Integer.MIN_VALUE是它自己的负值
Java实用2的补码的算术运算是不对称的。
千万要当心溢出:就像狼人一样,它是个杀手。
解惑34:被计数击倒了
不要使用浮点数作为循环索引。
在将一个int或者long转换成一个float或double时,可能会丢失精度。
当使用浮点数时,要实用double而不是float。
解惑35:分分钟
用被恰当命名的常量来替代所有的说有的魔幻数字。
千万不要使用空格来表示分组,要使用括号。
 
解惑36:优柔寡断
在try-finally语句中,finally语句块总是在控制权离开try语句块时执行。
当try语句块和finally语句块都意外结束时,在try语句块中引发意外结束的原因将被丢弃,而整个try-finally语句意外结束的原因与finally语句块意外结束的原因相同。
千万不要用return、break、continue、或throw来退出finally语句块,并且千万不要允许让受检查的异常传播到finally语句块之外。
解惑37:极端不可思议
解惑38:不受欢迎的宾客
在程序中,一个空final域只有在它的确未赋过值的地方才可以被赋值。
编译器必须拒绝某些可以证明是安全的程序。
如果必须重构一个程序,以削除由明确赋值规则所引发的错误,那么应该考虑添加一个新方法。
解惑39:你好,再见
System.exit方法将停止当前线程和所有其它当场死亡的程序
当System.exit被调用时,VM在关闭前要执行两项清理工作:
执行所有与关闭挂钩操作,这些挂钩已经注册 到Runtime.addShutdownHook上。务必要为那些必须在VM退出之前发生的行为关闭挂钩。
如果System.runFinalizersOnExit()或者Runtime.runFinalizersOnExit()都被调用,那么VM将在所有还未终结的对象上调用终结符。无论什么原因,永远不要调用System.runFinalizersOnExit()或者Runtime.runFinalizersOnExit():她们属于Java类库中最危险的方法。
解惑40:不情愿的构造器
Exception in thread "main" java.lang.StackOverflowError
    at ch05.Reluctant.<init>(Reluctant.java:12)
       后边还有很多这句话
实例变量的初始化操作将先于构造器的程序而运行。
对于任何在finally语句块中可能抛出的异常都要进行处理,而不是任其传播。
解惑41:域和流
解惑42:异常为循环而抛
不要实用异常控制循环;应该只为异常条件而使用异常
不要实用那些可怕的运用异常而不是显示终止测试的循环惯用法,因为这种惯用法非常不清晰,而且会掩盖bug。要意识到逻辑ANDOR操作符的存在,而且不要因无意识的误用而受害。
解惑43:异常地危险
class.newInstance可以抛出它没有声明过的受检查异常。
泛型信息是在编译器而非运行期检查的。
当你获得了不受检查的转型警告时,应该修改你的程序以削除它,或者可以确信这个转型不会失败。
Java的异常检查机制不是虚拟机强制执行的。
不要忽视编译器给出的警告信息。
解惑44:删除类
java.lang.NoClassDefFoundError这个错误可以在(直接或间接)使用某个类的程序中的任何地方
除了类名外,唯一的差异就是catch语句块所捕获的参数exVM本地变量之间的映射关系不同
astore_1 catch语句块中的捕获异常存储到捕获参数ex中的指令。
要想编写一个能够探测类丢失的程序,请实用反射。
不要对捕获NoClassDefFoundError形成依赖。
捕获Error及其子类型几乎是完全不恰当的。
解惑45:令人疲惫不堪的测验
当遇到StackOverflowError时,会停止,但是VM栈的深度为1024,这样将近要运行1.7×10291后才会停止,所以可以将其视为无限循环。
从技术角度讲,调用图是一颗完全二叉树,它的深度就是VM栈深度的上限。workOut程序的执行过程等价于先序遍历这棵树。
 
解惑46:令人混淆的构造器案例
输出:
double array
Java的重载过程是两阶段运行的:
    ·选取说有可获得而且可应用的方法或构造器。
    ·在第一阶段选取的方法或构造器中选取最精确的一个。
如果一个方法或构造器可以接收给另一个方法或构造器的任何参数,那么我们就说第一个方法比第二个方法缺乏准确性。
在测试哪一个方法或构造器最精确时,这些测试没有使用实参。
要想强制要求编译器选择一个精确的重载版本,需要将实参转型为形参所声明的类型。
避免使用重载。
可以通过将构造函数设置为私有的并提供公有的静态工厂,如此来解决这个问题。如果构造器有许多参数,你可以用Builder模式来减少对重载版本的需求。
如果确实进行了重载,那么请确保所有的重载版本所接受的参数都胡不兼容。
解惑47:啊呀!狸猫变犬子
每一个静态域在声明它的类及所有子类中共享一份单一的拷贝。
当你拿不准时,优先选组合而不是继承。
解惑48:我所得到的都是静态的
对静态方法的调用不存在任何动态的分派机制。当一个程序调用了一个静态方法时,要被调用的方法都是在编译时刻被选定的,而这种选定是基于修饰符的编译期类型而做出的,修饰符的编译期类型就是我们给出的方法调用表达式中原点左边部分的名字。
千万不要用一个表达式来标识一个静态方法调用。
千万不要隐藏静态方法。
对类和事例方法的调用彼此之间看起来应该具有明显的差异:
    ·实现目标的方式是不允许使用表达式作为静态方法的修饰符
    ·区分静态方法和实例方法调用的方式是实用不同的操作符。
    ·通过完全抛弃静态方法这一概念来解决问题。
解惑49:比生命力更大
在final类型的静态域被初始化之前,存在着读取其值的可能。final类型的域只有在其初始化表达式是常量表达式时才是常量。
要想改正一个类初始化循环,需要重新对静态域的初始器进行排序,使得每一个初始器都出现在任何依赖它的初始器之前。
要当心类初始化循环。
解惑50:不是你的类型
instanceof操作符被定义为在其左操作符为null时返回false。
instanceof操作符有这样的要求:如果两个操作符的类型都是类,其中一个必须是另一个的子类型。
解惑51:要点何在
在一个final类型的实例域被赋值之前,存在着取用其值的可能,而此时它包含的仍旧是其所属类型的缺省值。
循环的类初始化是无法避免的灾难,但是循环的实例初始化是可以且总是应该避免的。
千万不要在构造器中调用可覆写的方法。
在任何情况下,你都务必要记住:不要在构造器中调用可覆写的方法。在实例初始化中产生的循环将是致命的。
要么用积极初始化,要么用惰性初始化,千万不要同时使用二者。
如果初始化一个域2的时间和空间代价比较低,或者该域在程序的每一个执行中都需要用到时,那么使用积极初始化是恰当的。如果其代价比较高,或者该域在某些执行中并不会被用到,那么惰性初始化可能是更好的选择。宁外,惰性初始化对于打破类或实例初始化中的循环也可能是必须的。
请考虑类初始化的顺序,特别是当初始化显得很重要时更是如此。
解惑53:做你的事吧
交替构造器调用机制。
私有构造器捕获惯用法是一种非常有用的模式,你应该把它添加到你的技巧库中。
解惑54NullVoid
不仅表达式的值所引用的对象的运行期类型在确定哪一个方法将被调用时并不起任何作用,而且如果对象有标识的话,其标识也不起任何作用。
静态方法调用的限定表达式是可以计算的,但是它的值将被忽略。
要么用某种类型来限定静态方法调用,要么就更本不要限定他们。
解惑55:特创类
一个本地变量声明作为一条语句只能直接出现在一个语句块中。
在实用一个变量来对实例的创建进行计数时,要使用long类型而不是int类型的变量。
 
第七章 库之谜
解惑56:大问题
BigInteger实例是不可变的。String、BigDecimal以及包装器类型:Integer、Long、Short、Byte、Character、Boolean、Float和Double也是如此。
不要被误导,认为不可变类型是可变的。
在命名不可变类型的方法时,应该选择介词和名词,而不是动词。
解惑57:名字里有什么
无论如何,只要你覆写了equals方法,你就必须同时覆写了hashCode方法。
解惑58:产生它的散列码
HashSet类是使用equals(Object)方法来测试元素的等价性的;Name类中声明一个equals(name)方法HashSet不造成任何影响。
重载为错误和混乱提供了机会。
解惑59:差是什么
为了避免无意识地重载,你应该机械地对你想要覆写的每一个超类方法都拷贝其声明。
以0开头的整数字面常量将被解释为八进制数值。
千万不要在一个整数字面常量的前面加上一个0.
解惑60:一行以毙之
A题:
可以通过把集合中的原集合中的元素置于一个Set中将结合中的所有重复元素削除。
LinkedHashSet实现维护其元素插入的顺序,提供的导入性能接近HashMap。
解决方案:
Static <E> List<E> withoutDuplicates(List<E> original) {
       Return new ArrayList<E>(new LinckedHashSet<E>(original));
}
B题:
自1.4版本开始,由于正则表达式被添加到了java平台中(java.util.regex),StringTokenizer开始变得过时了。
Static String[] parse(String string){
       Return string.split(“,//S*”);
}
C题:
Arrays.deepToString方法可以达到这个目的。
D题:
整数类型的包装器类(Integer、Long、Short、Byte和Char)现在支持通用的位处理操作。
了解类库中有些什么可以为你节省大量的时间和精力,并且可以提高你的程序的速度和质量。
解惑61:日期游戏
Date将一月恶表示为0,而Calendar延续了这个错误。
Date.getDday返回的是Date实例所表示的星期日期,而不是月份日期。
在使用Calendar或Date的时候一定要当心,千万记着查阅API文档。
解惑62:名字游戏
相等的字符串常量同时也是相同的。
不要使用IdentityHashMap,除非你需要其基于标识的语义;它不是一个通用目的的Map实现。
在任何时候,程序都应该尽量不依赖予这种行为去保证他们的操作正确。
解惑63:更多同样的问题
不要因为偶尔地添加了一个返回类型,而将一个构造器声明变成一个方法声明。
要遵守标准的命名习惯。
如果其参数等于Integer.MIN_VALUE,那么产生的结果与该参数相同。
解惑64:按余数编组
Math.abs不能保证一定会返回非负的结果。
解惑65:疑似排序的惊人传奇
定长的整数没有达到可以保存任意两个同等长度的整数之差的程度。
不要使用基于减法的比较器,除非你能够准确保证要比较的数值之间的差永远不会大于Integer.MAX_VALUE.
应该避免“聪明”的代码。应该努力去编写清晰正确的代码,不要对它进行任何优化,除非该优化被证明是必须的。

解惑66:一件私事

一个覆写方法的访问修饰符所提供的访问权限与覆写方法的访问修饰符所提供的访问权限相比,至少要多一样。

对一个域来说,当它要隐藏另一个域时,如果隐藏域的访问修饰符提供的访问权限比被隐藏域的少,尽管这么做是不可取的,但是它是合法的。

覆写&隐藏:一旦一个方法在子类中被覆写,你就不能在子类的实例上调用它了(除了在子类内部,通过实用super关键字的方法)。然而,你可以通过将子类实例转换为某一个超类类型来访问被隐藏的域,在这个超类中该域未被隐藏。

避免隐藏。

Liskov置换原则:你能够对基类所做的任何事,都同样能够作用于子类。

解惑67:对字符串上瘾

尽管StrungOut有一个被命名为main的方法,但是它却具有错误的签名。一个main方法必须接受一个单一的字符串组参数。VM努力告诉我们的是StrungOut.main接受的是我们的String类所构成的参数,它无论如何都与java.long.String没有任何联系。

要避免重用平台类的名字,并且千万不要重用java.long中的类名。

要避免避免重用类名,尤其是Java平台类的重名。

解惑68:灰色的阴影

当一个变量和一个类型具有相同的名字,并且它们位于相同作用域时,变量名具有优先权。

遵守标准的Java命名习惯的程序员从来都不会遇上这个问题。

解惑69:黑色的渐隐
我们是可以引用一个被掩盖的类型名的,其技巧就是在某一种特殊的语法上下文环境中使用该名字,在该语法上下文环境中允许出现一个类型但是不允许出现一个变量。

要解决由类型被变量掩盖而引发的问题,需要按照标准的命名习惯来重命名类型和变量。

解惑70:一揽子交易

一个包内私有的方法不能被位于另一个包内的某个方法直接覆写。

解惑71:进口税

本身就属于某个范围的成员在该范围内与静态导入相比具有优先权。

程序中会重复地出现另一个类的静态元素,而每一次用到的时候都进行限定又会使程序乱成一锅粥。在这种情况下,静态导入工具可以显著地提高可读性。

应该有节制的使用静态导入,只有在非常需要的情况下才使用它们。

解惑72:终极危险

Final修饰符对于方法和域而言,意味着某些完全不同的事情。对于方法,final意味着不能被覆写(对于实例方法而言)或者隐藏(对于静态方法而言)。对于域,final意味着该域不能被赋值超过一次。

应该避免在不相关的概念之间重用关键字。

解惑73:隐私在公开

重用名字是危险爱你的:应该避免隐藏、遮蔽和掩盖。

解惑74:同一性的危机

如果同一个方法的两个重载版本都可以应用于某些参数,那么它们应该具有相同的行为。

解惑75:头还是尾?

条件操作符(?:)的行为在5.0版本之前是受限的。当第二个和第三个操作符是引用类型时,条件操作符要求他们其中的一个必须是另一个的子类型。在5.0或者更新的版本中,条件操作符在第二个和第三个操作符是引用类型时总是合法的。

应该升级到最新的Java平台版本上。

 

解惑76:乒乓
当你想调用一个线程的start方法时要多加小心,别错误弄成调用这个线程的run方法了。
解惑77:乱锁之妖
在内部,Thread.join方法在表示正在被链接的那个Thread实例上调用Object.wait方法。这样就在等待期间释放了对象上的锁。
千万不要假设库中的这个类对它的实例或类上的锁会做(或者不会做)某些事情。
如果你需要获得某个锁的完全控制权,那么就要确定没有任何其他人能够访问到它。
解惑78:反射的污染
访问位于其它包中的非公共类型的成员是不合法的。
Object.getClass().getMethod("methodName")这种惯用法虽然很常见,但是却有问题,它不应该被使用。
在使用反射访问某个类型时,请使用表示某种可访问类型的Class对象。
解惑79:狗狗的幸福生活
避免隐藏。
使用ThreadRunnable)构造器来替代对Thread的继承。
解惑80:更深层的反射
ClassnewInstance的文档叙述道:如果那个Class对象“代表了一个抽象类,一个接口,一数组类,一个原生类型,或者是空;或者这个类型没有任何空的构造器;或者实例化由于某些其他原因而失败,那么它就会抛出异常”。
除非你确实是需要一个外围实例,否则你应该优先使用静态成员类而不是非静态成员类。
请避免使用反射来实例化内部类。
解惑81:无法识别的字符化
Writeint)是唯一一个在自动刷新功能开启的情况下不刷新PrintStream的输出方法。
尽可能使用熟悉的惯用法:如果你不得不使用陌生的API,请一定要参考相关的文档。
解惑82:啤酒爆炸
由于某些本地平台只提供有限大小的缓冲区,所以如果不能迅速地读取子进程的输出流,就有可能会导致子进程阻塞,甚至是死锁。
为了确保子进程能够结束,你必须排空它的输出流;对于错误流也是一样。
如果你决定不合并输出流和错误流,你必须并行地排空它们。
API应该设计得更容易做出正确的事,而很难或不可能做出错误的事。
解惑83
一个实现了Serializable的单件类,必须有一个readResolve方法,用以返回它的唯一的实例。
解惑84:嘎然而止
Thread.interrupted方法第一次被调用的时候返回了true,并且清除了线程的中断状态,所以在if-then-else语句的then分支中第二次调用该方法的时候,返回的就是flase
Thread还有一个查询一个线程的中断状态的方法:isInterrpted方法。
不要使用Thread.interrupted方法,除非你想要清除当前线程的中断状态。
解惑85:惰性初始化
在一个线程访问一个类的某个成员的时候,它会去检查这个类是否已经被初始化。在忽略严重错误的情况下,有四种可能的情况:
(1)      这个类尚未被初始化。
(2)      这个类正在被当前线程初始化;这是对初始化的递归请求。
(3)      这个类正在被其他线程而不是当前线程初始化。
(4)      这个类已经被初始化。
要让类的初始化尽可能地简单。
在类的初始化期间等待某个后台线程很可能会造成死锁。
                                                                                                           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值