Effective Java
2017-01-23 17:17:09 0 举报不是float和double的基本类型域:直接使用==
引用域:递归调用equals方法
float域:Float.compare方法
double域:Double.compare方法
1、 把某个非零的常数值,比如说17(值17是任选的,因为即使2.a步骤中计算出的散列值为0初始域也会影响到散列值,这样会大大的避免了冲突的可能性,所以这个一般是一个非零的常数值),保存在一个名为result的int的类型变量中。
2、 对于对象中每个键域f(指equals方法中涉及的每个域),完成以下步骤:
a. 为该域计算int类型的散列码c:
I、 如果该域是boolean类型,则计算(f ? 1 : 0)。
II、 如果该域是byte、char、short或者int类型,则计算(int)f。
III、如果该域是long类型,则计算(int)(f ^ (f >>> 32))。
IV、 如果该域是float类型,则计算Float.floatToIntBits(f),即将内存中的浮点数二进制位看作是整型的二进制,并将返回整型结果。
V、 如果该域是dobule类型,则计算Double.doubleToLongBits(f),然后按照步骤2.a.III。
VI、 如果该域是一个对象引用,并且该类的equals方法通过递归地调用equals方式来比较这个域,则同样为这个域递归地调用hashCode。如果这个域的值为null,则返回0。
VII、如果该域是一个数组,则要把每一个元素当做单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤2.b中的做法把这些散列值组合起来。如果数组的每个元素都需要求,则可以使用1.5版本发行的Arrays.hashCode方法。
b. 按照下面的公式,把步骤2.a中计算得到的散列码c合并到result中:
result = 31 * result + c;
步骤2.b中的乘法部分使得散列值依赖于域的顺序,如果一个类包含多个包含多个相似的域,这样的乘法运算就会产生一个更好的散列函数。例如,如果String散列函数省略了这个乘法部分,那么只要组成该字符串的字符是一样的,而不管它们的排列的顺序,则会导致只要有相同字符内容的字符串就会相等的问题,而String的equals方法是与字符排序顺序有关的。另外,之所以选择31,是因为它是一个奇素数。如果乘数是偶数,并且乘法溢出的话,信息就会全丢失,因为与2相乘等价于移位运算。31还有个很好的特性,即用移位和减法来代替乘法,可以得到更好的性能:31 * i = i ^32 - i = (i << 5) – i,现代的VM可以自动完成这种优化。
3、 返回result。
4、 写完了hashCode方法后,问问自己“相等的实例是否都具有相等的散列码”。
类的可访问性,缩小到最小,除非有必须暴露这部分的理由,否则就该是private,Effective c++中也提到过。
组 合 关 系 | 继 承 关 系 |
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 | 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
优点:具有较好的可扩展性 | 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 | 缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 | 缺点:子类不能改变父类的接口 |
缺点:整体类不能自动获得和局部类同样的接口 | 优点:子类能自动继承父类的接口 |
缺点:创建整体类的对象时,需要创建所有局部类的对象 | 优点:创建子类的对象时,无须创建父类的对象 |
下面是摘自java.util.AbstractCollection的规范:
public boolean remove(Object o):
从此 collection 中移除指定元素的单个实例(如果存在)(可选操作)。更正式地说,如果该 collection 包含一个或多个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则移除 e。如果该 collection 包含指定的元素(或等价元素,如果该 collection 随调用的结果发生变化),则返回 true。
此实现在该 collection 上进行迭代,查找指定的元素。如果找到该元素,那么它会使用迭代器的 remove 方法从该 collection 中移除该元素。注意,如果此 collection 的 iterator 方法所返回的迭代器无法实现 remove 方法,并且此 collection 包含指定的对象,那么此实现会抛出 UnsupportedOperationException。
该文档清楚地说明了,覆盖iterator方法将会影响remove方法的行为。而且,它确切地描述了iterator方法返回的Iterator的行为将会臬影响remove方法的行为。与此相反,在第16条的情形中,程序员在子类化HashSet时,并没有说明覆盖add方法是否会影响addAll方法的行为。
Set<Lark> exalation = new HashSet();
warning : unchecked conversion
found : HashSet, required Set<Lark>
只在这种情况下使用@SuppressWarnings("unchecked")注解来禁止这条警告
Object[] objectArray = new Long[1];
objectArray[0] = "not fit"; //Throws ArrayStoreException
List<Object> ol = new ArrayList<Long>();
ol.add("not fit");
如创建泛型、参数化类型或者类型参数的数组是
非法的(new List<E>[]、new List<String>[]和new E[]),
这些在编译时会导致一个generic array creation错误
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int ORANGE_NAVEL = 0;
public static final int APPLE_TEMPLE = 1;
public enum Apple {NAVEL, TEMPLE}
一个@return标签,除了void
抛出的每一个异常都有一个@throws标签
...
doSomething(e);
}
就要使用显示的迭代器,以便调用它的remove方法。
就需要列表迭代器或者数组索引,以便设定元素的值。
就需要显示的控制迭代器或索引变量,以便所有迭代器或者索引变量能够同步前移。
jdk1.5 java.util.concurrent加入了一组并发实用工具
(primitive)
(reference type)
(boxed primitive)
还有个非功能值:null
否则转换成对应的int、float,
以及“是-或者-否”转换成boolean
如果存在适当的值类型,不管是基本类型还是对象引用,大多应该使用这种类型
如果不存在这样的类型,就应该编写一个类型
包括异常检查,如果程序调用了不存在或者不可访问的方法,在运行时它将会失败,除非做了特别的预防措施。
——Donald E. Knuth[Knuth74]
规则1:不要进行优化。
规则2(仅针对专家):还是不要进行优化——也就是说,在你还没有绝对清晰的优化方案之前,请不要进行优化。 ——M.A.Jackson[Jackson75]
因此,并发集合中不可能排除并发活动,将它锁定没有什么作用,只会使程序的速度变慢
最常见的synchronizer是Countdown Latch和semaphore
较不常用的是CyclicBarrier和Exchanger
Countdown Latch的构造器带有一个int类型的参数,这个int是指允许所有在等待的线程被处理前,必须在锁存器上调用Countdown方法的次数。
一个类为了可被多个线程安全地使用,就要在文档中清楚地说明它所支持的线程安全性级别。
这样的例子包括String、Long、BigInteger(见第15条)。
所以,它的实例可以被并发使用,无需任何外部同步。
其例子包括Random、ConcurrentHashMap
这样的例子包括Collections.synchronized包装返回的集合,它们的迭代器(iterator)要求外部同步。
这样的例子包括通用的集合实现,如ArrayList和HashMap。
线程对立的根源通常在于,没有同步地修改静态数据。
没有人会有意地编写一个线程对立的类,这种类是由于没有考虑到并发性而产生的后果。
幸运的是在Java类库中,线程对立的类或者方法非常少,System.runFinalizersExit方法是线程对立的,但已经被废除了。
如果永远不需要这个值,这个域就永远不会被初始化。这种方法即适用于静态域,也适用于实力域。
如果两个或多个线程共享一个延迟初始化的域,采用某种形式的同步是很重要的,否则就可能造成严重的Bug(见第66条)。
这种模式避免了在域被初始化之后访问这个域时的锁定开销(见第67条)。
多大多数人来说,Thread.yield的唯一用途是在测试期间人为地增加程序的并发性。
正因为太容易了,所以存在一种误解,认为程序员毫不费力就可以实现序列化。
实际的情形复杂的多。
虽然使一个类可序列化的直接开销非常低,但是为了序列化而付出的长期开销是实实在在的。
这不符合“最低限度地访问域”(见第13条)的实践标准。