Java和C++在细节上的差异(一) (七)

2014-11-24 03:03:11 · 作者: · 浏览: 6
//5. 将otherObject转换为相应的类类型变量
16 Employee other = (Employee)otherObject;
17 //6. 现在开始对所有需要比较的域进行比较了。其中使用==比较基本类型,
         //使用equals比较对象类型。
18 return name.equals(other.name) && salary == other.salary;
19 }
20 }
  注:数组元素的比较可以调用Arrays.equals方法检测。如果子类中重新定义了equals方法,就要在其中包含调用super.equals(other).
  Java在语言规范中给出了自定义equals方法需要遵守的规则:
  自反性: 对于任何非空引用x,x.equals(x)应该返回true。
  对称性: 对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。
  传递性: 对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。
  一致性: 如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
  对于任意非空引用x,x.equals(null)应该返回false。
  2) hashCode: 导出一个经过哈希计算后的整型值,Java对hashCode的缺省实现是返回当前对象的存储地址。一下列出String的hashCode实现方式:
1 public int hashCode() {
2 int hash = 0;
3 for (int i = 0; i < length(); ++i)
4 hash = 31 * hash + charAt(i);
5 return hash;
6 }
  注:自定义类型的equals和hashCode定义必须一致,如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。如果打算实现自定义的hashCode方法,推荐使用在对象构造初始化后就不会再改变的域字段作为hashCode的计算因子。否则一旦使用可变域资源作为hashCode计算因子的一部分,将会导致一些隐藏的问题。比如当Employee对象实例存入HashMap中,但是使用者在存入集合之后,修改了某个参数hashCode计算的域字段的值,此后再在HashMap中查找原有对象时由于hashCode已经改变,因此即使该对象已经存入HashMap中,结果是仍然无法找到最初存入的对象了。数组类型的hashCode,可以通过Arrays.hashCode方法计算得出。
  3) toString: Java中比较推荐的实现方式为:
1 public String toString() {
2 return getClass().getName() +
3 "field1 = " + field1 +
4 "field2 = " + field2;
5 }
  注:C#的Framework中也存在一个类似的Object对象,作为C#所有对象(包括自定义对象)的唯一根类,其中也有对应的3个方法equals、hashCode和toString。Effective C#中针对这3个方法提供了一个很好的建议,既如果自定义类重载了这3个方法中任何一个,那么强烈建议该类也重载另外两个域方法。如对equals和toString而言,如果x.equals(y)返回true,那么x.toString.equals(y.toString)也将返回true,反之亦然。针对equals和hashCode域方法还有一种推荐的实现方式,如下:
1 public bool equals(Object other) {
2 return toString().equals(other.toString());
3 }
4
5 public int hashCode() {
6 return toString().hashCode();
7 }
  3. 包装类和自动打包:
  1) 包装器对象均为不可变对象,如String,既一旦初始化之后其值将不会再被改变。包装器类是final类,不能为继承。
  2) 自动拆包和打包:Integer n = 3; n++; 在执行n++时,Java编译器将自动插入一条拆包指令,然后进行自增计算,最后再将结果打入对象包内。
  3) 自动打包的规范要求boolean, byte, char <= 127, 和介于-128--127之间的short和int被包装到固定的对象中,见如下代码:
1 public void test() {
2 Integer a1 = 1000;
3 Ingeger a2 = 1000;
4 if (a1 == a2)
5 System.out.println(
6 "This won't be printed out because they are greater than 127.");
7
8 Integer a3 = 100;
9 Ingeger a4 = 100;
10 if (a3 == a4)
11 System.out.println(
12 "This will be printed out because they are less then 127.");
13 }
  4) 打包和拆包过程是编译器行为,不是虚拟机行为,是编译器在生成字节码的时候自动插入的指令。
  5) 包装类在容器中的应用。对于Java提供的泛型容器类其类型参数不能是primitive type,如int、float等,如果确实需要添加类似的数据,需要将相应的包装类作为容器类型参数,之后在插入原始类型数据,但是在插入过程中Java的编译器将自动插入打包指令,因此实际插入到容器中的仍然是包装类对象,见如下代码:
1 public static void main(String args[]) {
2 ArrayList l = new ArrayList();
3 for (int i = 0; i < 10; ++i)
4 l.add(i);
5
6 for (int i = 0; i < l.size(); ++i) {
7 System.out.printf("The value is %d.\t",l.get(i));
8 System.out.printf("The class name is %s.\n"
9 , l.get(i).getClass().getName());
10 }
11 }
12 /* 结果如下:
13 The value is 0. The class name is java.lang.Integer.
14 The value is 1. The class name is java.lan