Java基础--关于Object的一些通用方法

Posted ck-blogs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础--关于Object的一些通用方法相关的知识,希望对你有一定的参考价值。

equals(Object obj): 判断两对象是否相同(String类重写了该方法)

1. equals() 与 == 的区别

  • 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
  • 对于引用类型,== 判断两个实例是否引用同一个对象,而 equals() 判断引用的对象是否等价。
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

 

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

//自反性
x.equals(x); // true

//对称性
x.equals(y) == y.equals(x) // true

//传递性
if(x.equals(y) && y.equals(z)) {
    x.equals(z); // true;
}

//一致性:多次调用 equals() 方法结果不变
x.equals(y) == x.equals(y); // true

//与null比较:对于任何非空对象x
x.euqals(null); // false;

 

3.实现

  • 检查是否为同一个对象的引用,如果是直接返回 true;
  • 检查是否是同一个类型,如果不是,直接返回 false;
  • 将 Object 实例进行转型;
  • 判断每个关键域是否相等。
public class EqualExample {
    private int x;
    private int y;
    private int z;

    public EqualExample(int x, 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;
    }
}

 

hashCode():返回对象的散列值

    hashCode()是一个native方法,而且返回值类型是整型;实际上,该native方法将对象在内存中的地址作为哈希码返回,可以保证不同对象的返回值不同。相同散列值不等于实例相同,而两相等的实例散列值必相同。

    JDK中对hashCode()方法的作用,以及实现时的注意事项做了说明:

  • hashCode()在哈希表中起作用,如java.util.HashMap;

  • 如果对象在equals()中使用的信息都没有改变,那么hashCode()值始终不变;

  • 如果两个对象使用equals()方法判断为相等,则hashCode()方法也应该相等;

  • 如果两个对象使用equals()方法判断为不相等,则不要求hashCode()也必须不相等;但是开发人员应该认识到,不相等的对象产生不相同的hashCode可以提高哈希表的性能。

    hashCode()的作用:

        总的来说,hashCode()在哈希表中起作用,如HashSet、HashMap等。

        当我们向哈希表(如HashSet、HashMap等)中添加对象object时,首先调用hashCode()方法计算object的哈希码,通过哈希码可以直接定位object在哈希表中的位置(一般是哈希码对哈希表大小取余)。如果该位置没有对象,可以直接将object插入该位置;如果该位置有对象(可能有多个,通过链表实现),则调用equals()方法比较这些对象与object是否相等,如果相等,则不需要保存object;如果不相等,则将该对象加入到链表中。

        这也就解释了为什么equals()相等,则hashCode()必须相等。如果两个对象equals()相等,则它们在哈希表(如HashSet、HashMap等)中只应该出现一次;如果hashCode()不相等,那么它们会被散列到哈希表的不同位置,哈希表中出现了不止一次。

        实际上,在JVM中,加载的对象在内存中包括三部分:对象头、实例数据、填充。其中,对象头包括指向对象所属类型的指针和MarkWord,而MarkWord中除了包含对象的GC分代年龄信息、加锁状态信息外,还包括了对象的hashcode;对象实例数据是对象真正存储的有效信息;填充部分仅起到占位符的作用, 原因是HotSpot要求对象起始地址必须是8字节的整数倍。

 

toString():默认返回 [email protected] 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。

 

public class ToStringExample {
    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}

 

ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());

 

clone():Object 的受保护方法,这意味着,如果一个类不显式去重写clone() 就没有这个方法。

1.cloneable

 

public class CloneExample {
    private int a;
    private int b;
}

 

CloneExample e1 = new CloneExample();
CloneExample e2 = e1.clone(); // ‘clone()‘ has protected access in ‘java.lang.Object‘

接下来覆盖 Object 的 clone() 得到以下实现:

public class CloneExample {
    private int a;
    private int b;

    @Override
    protected CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}
CloneExample e1 = new CloneExample();
try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
java.lang.CloneNotSupportedException: CloneExample

以上代码抛出CloneNotSupported异常,因为CloneExample 没有实现 Cloneable 接口。

 

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 

应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。

2.深拷贝和浅拷贝

  • 浅拷贝:拷贝实例和原始实例的引用类型引用同一个对象; 
  • 深拷贝:拷贝实例和原始实例的引用类型引用不同对象。
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();
    }
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222

 

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;
    }
}
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。

 

public class CloneConstructorExample {
    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}

 

CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

 

 

 

以上是关于Java基础--关于Object的一些通用方法的主要内容,如果未能解决你的问题,请参考以下文章

java基础之和String相关的一些转换

Java核心技术 卷Ⅰ 基础知识

关于Object.defineProperty 的基础知识

有关java object类的toString方法

Java学习关于Object的clone的一些问题

Java远程过程调用基础:构建可自适应的动态代理对象的通用方法