protected Object clone(); // 创建并返回对象的一个副本
public boolean equals(Object obj); // 判断其他对象是否与本对象“相等”
protected void finalize(); // GC确定不存在更多此对象引用的时候,GC调用此方法回收
public final native Class> getClass(); // 返回此对象运行时的类
public native int hashCode(); // 返回该对象的哈希码值
public final native void notify(); // 唤醒在此对象监视器上等待的单个线程
public final native void notifyAll(); // 唤醒在此对象监视器上等待的所有线程
public public String toString(); // 返回该对象的字符串表示
// 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
由于clone是Object的protected方法,如果不去显示的重写clone,继承类就无法被调用clone方法。同时,如果仅仅显式重写了clone接口未实现Cloneable接口依旧会发生CloneNotSupportedException。
下面是完整重写clone接口
public class CloneTest implements Cloneable{
int a;
@Override
public Object clone throws CloneNotSupportedException{
return super.clone();
}
}
浅拷贝与深拷贝的不同点在于浅拷贝的拷贝对象与原始类型引用的是同一个对象,而深拷贝的拷贝对象与原始类型引用的不同对象,即对类中的每一个对象进行一次拷贝。主要实现的区别在于对clone函数的重写方法不同。
// 浅拷贝
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException {
return (ShallowCloneExample) super.clone();
}
}
// 深拷贝
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
// 对类中的成员也进行一次拷贝
@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
由于clone需要处理异常以及需要做类型转化,在真正需要拷贝的时候可以考虑使用拷贝构造函数来进行拷贝。
在阅读ArrayList源码的时候,我们可以看到其clone()函数,其本质是浅拷贝,在此贴出源码:
public Object clone() {
try {
ArrayList> v = (ArrayList>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
然后再贴出Arrays的copyOf()源码:
public static T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
可见其最终调用了System的arraycopy函数,而arraycopy其实就是浅拷贝。
关于equals,java API中:
指示其他某个对象是否与此对象“相等”。
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) 始终返回 true
或始终返回 false
,前提是对象上 equals
比较中所用的信息没有被修改。x
,x.equals(null)
都应返回 false
。Object
类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x
和 y
,当且仅当 x
和 y
引用同一个对象时,此方法才返回 true
(x == y
具有值 true
)。
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
我们可以自己实现equals,通过以下步骤:
public class Equal{
int a;
int b;
public Equal(int a,int b){
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object obj){
if(this == o) return true;
if(obj == null&& this.getClass() != obj.getClass()) return flase;
Equal o = (Equal) obj; //类型转化
if(a != o.a) return false;
return b == o.b;
}
在java API中有说在重写equals时通常有必要重写hashCode,不然容易导致下面这种情况的发生:
Equal e1 = new Equal(1, 1);
Equal e2 = new Equal(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
如上,若不重写hashmap方法在hash表中就会插入两个一样的对象(因为他们的hash值是不同的,在默认的hashCode中自动返回对应的在JVM中的地址)。
重写hashCode:
@Override
public int hashCode() {
int result = 17;
result = 31 * result + a;
result = 31 * result + b;
return result;
}