Object通用方法

    xiaoxiao2025-05-04  16

    文章目录

    equals,hashcode,==equals()实现hashCode()的实现equals和==的区别equals()和hashCode() Clone()1、cloneable2、浅拷贝3、深拷贝4、clone()的替代方案 toString()其他方法

    equals,hashcode,==

    equals()实现

    检查是否同一个对象的引用,如果是直接返回true(默认方法)检查是否是同一个类型(instanceof),不是直接返回false,将Object对象进行转型,判断每个关键域是否相等(需要我们重写)

    hashCode()的实现

    hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。

    在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。

    hashCode()是怎么算出来的?

    理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。

    一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。

    equals和==的区别

    对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。

    equals()和hashCode()

    两个对象equals,这两个对象的hashCode相等;两个对象的hashCode相等,这两个对象未必相等

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

    这两个方法都是Object类的方法,下面是Object类中这两个方法的源码:

    // 直接相等比较,不同对象的话返回false public boolean equals(Object obj) { return (this == obj); } // hashCode为本地方法 public native int hashCode();

    String类中equals()和hashCode()方法的实现

    private final char[] value; // 当且仅当两个字符串的长度和内容相等时才equals public boolean equals(Object anObject) { // 对象相等直接equals 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) { // 只要对应的字符不相等则不equals if (v1[i] != v2[i]) return false; i++; } return true; } // 长度不等则不equals } // 类型不同则不equals return false; } // 哈希值的计算公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],n为字符串长度 // String的哈希值的计算使用了31,主要是以下3个原因: // 1. 质数与其他数字相乘后,计算结果唯一的概率更大,减少哈希冲突的概率 // 2. 质数越大,哈希冲突概率越小,但是计算速度越慢,31是哈希冲突和性能的折中值,是实验观测的结果 // 3. JVM会自动对31进行优化:31 * i == (i << 5) - i 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; }

    例子:下面这个例子里,如果只重写了equals方法,没有重写hashCode方法,则输出为null,这是因为apple1和apple2是两个不同的对象,这两个对象的hashCode不同,定位就不同,所以查不到输出null。添加上重写的hashCode方法后,定位相同,然后按equals方法比较也相等,所以查到了输出10。

    public class Apple { private String color; public Apple(String color) { this.color = color; } public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Apple)) { return false; } if (obj == this) { return true; } return ((Apple) obj).color.equals(this.color); } public int hashCode() { return color.hashCode(); } public static void main(String[] args) { HashMap<Apple, Integer> map = new HashMap<>(); Apple apple1 = new Apple("red"); Apple apple2 = new Apple("red"); map.put(apple1, 10); Integer result = map.get(apple2); System.out.println(result); } }

    Clone()

    1、cloneable

    一个类只有显示的重写了clone()方法且实现了cloneable接口,才能被clone;

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

    2、浅拷贝

    拷贝对象和原始对象的引用类型引用同一个对象。 其实就相当于指向的是同一个内存地址,通过引用A修改的对象内容同样会在引用B上看到,因为AB指向的是同一个对象。

    3、深拷贝

    拷贝对象和原始对象的引用类型引用不同对象。 其实就是通过重写clone方法将原对象的所有属性全部复制到新对象中

    4、clone()的替代方案

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

    toString()

    其他方法

    最新回复(0)