Java学习笔记——Object类

方法预览

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()

Cloneable

由于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的浅拷贝与深拷贝

浅拷贝与深拷贝的不同点在于浅拷贝的拷贝对象与原始类型引用的是同一个对象,而深拷贝的拷贝对象与原始类型引用的不同对象,即对类中的每一个对象进行一次拷贝。主要实现的区别在于对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 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和hashcode

equals()

关于equals,java API中:

指示其他某个对象是否与此对象“相等”。

equals 方法在非空对象引用上实现相等关系:

  • 自反性:对于任何非空引用值 xx.equals(x) 都应返回 true
  • 对称性:对于任何非空引用值 xy,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true
  • 传递性:对于任何非空引用值 xyz,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true
  • 一致性:对于任何非空引用值 xy,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
  • 对于任何非空引用值 xx.equals(null) 都应返回 false

Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 xy,当且仅当 xy 引用同一个对象时,此方法才返回 truex == y 具有值 true)。

注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

我们可以自己实现equals,通过以下步骤:

  • 判断是否为同一个对象,若是直接返回true
  • 判断类的类型是否不同,若不同,返回false
  • 对参数做类型转化
  • 判断每一个关键域是否相等
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;
    }

hashCode()

在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;
}

 

你可能感兴趣的:(Java学习笔记)