Object通用方法

文章目录

  • equals,hashcode,==
    • equals()实现
    • hashCode()的实现
    • equals和==的区别
    • equals()和hashCode()
  • Clone()
    • 1、cloneable
    • 2、浅拷贝
    • 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,编译器会自动进行这个优化。
Object通用方法_第1张图片

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(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象
Object通用方法_第2张图片

toString()

在这里插入图片描述

其他方法

Object通用方法_第3张图片

你可能感兴趣的:(java基础知识)