Java - equals 与 == 的区别

概述

首先理解 == 的概念,== 是 Java 语言中的比较运算符,用于比较两边的值是否相等。当对象使用 == 比较时,比较的是对象的地址是否相等。

创建一个测试类:

public class MyData {
    int a = 10;
}

比较两个对象是否相等:

@Test
void test() {
    MyData data1 = new MyData();
    MyData data2= new MyData();
    System.out.println(data1 == data2);			// false
    System.out.println(data1.equals(data2));	// false
}

分析

在 Java 中,所有的类都默认继承 Object 类,在源码中有介绍:

在这里插入图片描述
Object 类的 equals 方法默认使用 == 进行比较,所以如果没有重写 equals 方法的话,默认使用 == 进行比较:

public boolean equals(Object obj) {
 	return (this == obj);
}

上面的例子中,我们重写 MyData 类的 equals 方法:

public class MyData {
    int a = 10;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof MyData) {
            MyData myData = (MyData) obj;
            return myData.a == this.a;
        }
        return false;
    }
}

输出比较结果:

@Test
void test() {
    MyData data1 = new MyData();
    MyData data2= new MyData();
    System.out.println(data1 == data2);			// false
    System.out.println(data1.equals(data2));	// true
}

== 比较的是对象的地址,而 equals 方法默认使用 == 作为比较运算符,但是可以重写 equals 方法,自定义比较规则。

总结

  • == 常用于比较原生类型
  • 对象之间比较的是引用地址(对象在栈中的引用地址)是否相等,当引用地址相等时,== 返回 true,而 equals 方法可以返回 true 或者 false 取决于重写方法的实现逻辑。

拓展

String 类型的比较

通过上面的例子,我们知道包装类型使用 == 比较的是引用地址,String 作为一个特殊的包装类型,由于 JVM 提供了常量池,所以需要单独考虑:

@Test
void testBaseType() {
    String s1 = "dev";
    String s2 = "dev";
    System.out.println(s1 == s2);	// true
}

在上面的代码中,由于定义 s1 的时候,会把值给存放到常量池里面;当定义 s2 的时候,会先去常量池里面找 123 这个值是否已经定义过,如果存在会把引用地址赋值给 s2,所以这里 s1 的引用地址等于 s2 的引用地址。

当使用构造函数创建 String 对象的时候:

@Test
void testBaseType() {
    String s1 = "dev";
    String s2 = "dev";
    String s3 = new String("dev");
    System.out.println(s1 == s2);	// true
    System.out.println(s1 == s3);	// false
    System.out.println(s1.equals(s3));	// true
}

使用构造函数创建的 String 对象是直接在堆中创建的,引用地址不一样,所以 == 比较的结果为 false。而 String 类型重写了 equals 方法:

public boolean equals(Object anObject) {
	// 先判断地址是否相等
    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) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

可以看出,重写后的 equals 方法比较的是字符串的 ,所以 s1.equals(s3) 结果为 true。

Integer 与 int 类型的比较

首先来看一下 Integer 类型的比较:

@Test
void testInteger() {
    Integer i1 = 100;
    Integer i2 = 100;
    Integer i3 = new Integer(100);

    System.out.println(i1 == i2);	// true
    System.out.println(i1 == i3);	// false
    System.out.println(i1.equals(i3));	// true

	Integer i4 = 128;
    Integer i5 = 128;
    System.out.println(i4 == i5);	// false
    System.out.println(i4.equals(i5));	// true
}

为什么会出现这种情况呢?首先,我们要知道:Integer i4 = 128; 这种定义方式会调用 IntegervalueOf() 方法。查看 valueOf 方法的源码:

public static Integer valueOf(int i) {
 if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        // 实例化并缓存起来了
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

可以看到,这里使用了内部类 IntegerCache-128~127(最大值可自定义) 的整数都给提前实例化了,所以无论创建多少个这个范围内的 Integer 对象,都是同一个对象。

再来看一下 Integer 重写的 equals 方法:

public boolean equals(Object obj) {
   if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

可以看到 Integer 之间的比较,是调用 Integer.ineValue() 获取 int 值然后使用 == 比较的。

而 int 和 Integer 之间的比较,Integer 类型会自动拆箱成 int 类型,所以可以直接使用 == 进行比较:

Integer i5 = 128;
int i6 = 128;
System.out.println(i5 == i6);	// true

总结

  • 如果 Integer 类型的两个数相等,如果范围在 -128 ~ 127 ,那么用 “==” 返回 true;其余的返回 false。
  • 两个基本类型 int 进行相等比较,直接用 “==” 即可。
  • 一个基本类型 int 和一个包装类型 Integer 比较,用“==” 也可,这个时候,Integer 类型做了拆箱操作。
  • Integer 类型比较大小,要么调用 Integer.intValue() 转为基本类型用 “==” 比较,要么直接用 equals 比较。

拓展

Long 和 Short 类型也做了相同的处理,只不过最大值是不可调的。

参考 Long 源码:

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

参考 Short 源码:

public static Short valueOf(short s) {
    final int offset = 128;
    int sAsInt = s;
    if (sAsInt >= -128 && sAsInt <= 127) { // must cache
        return ShortCache.cache[sAsInt + offset];
    }
    return new Short(s);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值