1)equals和==:
总结来说:
(1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的“值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址。
(2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地
址;诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对
象的内容。所以equals比较的内容取决于是否重写。
2)hashCode和equals:
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,如果用于equals比较的对象信息没有被修改的话,那么,多次调用x.equals(y)也一定返回true。
与null的比较。对于任意的非空引用值x,x.equals(null)一定返回false。
equals方法重写:
● 检查是否为同一个对象的引用,如果是直接返回 true;
● 检查是否是同一个类型,如果不是,直接返回 false;
● 将 Object 对象进行转型;
● 判断每个关键域是否相等。
public class EqualExample {
private int x;
private int y;
private int z;
public EqualExample(intx, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public boolean equals(Object o) {
if (this == o)return true;
if (o == null || getClass() != o.getClass()) return false;
EqualExample that = (EqualExample) o;
if (x != that.x)return false;
if (y != that.y)return false;
return z == that.z;
}
}
String类中equals方法实现:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if(v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。
下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。
EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2
hashCode原则:
第一:在某个运行时期间,只要对象的(字段的)变化不会影响equals方法的决策结果,那么在这个期间,无论调用多少 次hashCode,都必须返回同一个散列码。
第二:通过equals调用返回true的2个对象的hashCode一定一样。
第三:通过equasl返回false的2个对象的散列码不需要不同,也就是他们的hashCode方法的返回值允许出现相同的情况。
总结一句话:等价的(调用equals返回true)对象必须产生相同的散列码。不等价的对象,不要求产生的散列码不相同。
需要在上述EqualExample类中重写hashCode方法,如下所示:
@Override
public int hashCode() {
int result = 17;
result = 31 * result +x;
result = 31 * result +y;
result = 31 * result +z;
return result;
}
String类中的hashCode方法实现:
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
3)clone深拷贝与浅拷贝
复制对象 or 复制引用



浅拷贝:对象中的基本类型进行值拷贝,引用类型对引用地址进行拷贝。
深拷贝:对象中的基本类型和引用类型对应的值都进行拷贝。
如果想要深拷贝一个对象, 这个对象必须要实现Cloneable接口,实现clone方法,并且在clone方法内部,把该对象引用的其他对象也要clone一份 , 这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。
如果实现彻底的深拷贝,需要使用序列化的方法。
static class Body implements Cloneable{
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable{
public Face face;
public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1) );
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
打印结果为:
body == body1 : false
body.head == body1.head :false
4)wait与notify/notifyAll
可参见Java多线程通信部分。