Java中equals()和==使用|自动拆箱装箱|字符串常量池

我们先归纳一下使用的场景:

1、原始数据类型的等值比较只能用==运算符

2、非基本数据类型等值比较可用==和equals()方法

基本原始数据类型包括byte,short,char,int,long,float,double,boolean,原始数据类型使用==时比较的是两个操作数的值,非基本数据类型的等值比较,也可以说是对象的等值比较规则如下:

1、==运算符比较两个操作数对象指向的目标的地址

2、equals()默认和==一样,但是多数类型重写equals()方法,比较的是操作数对象指向的目标的值,如Integer,String。

下面我们通过例子来实践一下上面的理论:

System.out.println(new Integer(2) == new Integer(2)); // false
System.out.println(new Integer(2).equals(new Integer(2))); // true
System.out.println(new Integer(2) == 2); // true
System.out.println(new Integer(2).equals(2)); // true

上面的几种写法都可能出现在编码过程中,第一行中,两个操作数对象指向不同的目标,所以使用==返回false,第二行中比较的是两个对象指向目标的值,所以返回true,第三行中,先进行拆箱,转换为原始类型比较,返回true,第四行中,进行装箱,结果与第二行相同,返回true。

下面再来看一个例子:

Integer i1=127;
Integer i2=127;
Integer i3=128;
Integer i4=128;

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

看到上面的结果了吧,和你想的可能不一样,这里涉及到了Integer的自动装箱,使用的函数是:

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

这里的low是-128,high通常是127,如果在[-128,127]之间,就会从缓存中取出对象,否则new一个出来,上面的i1和i2就是从缓存中取出的,所以i1和i2指向了同一个实例,使用==运算的话就返回true,而i3和i4指向的是不同的实例,所以使用==运算符就返回false。

再来看一个例子

Double d1 = 1.0;
Double d2 = 1.0;
Double d3 = 2.0;
Double d4 = 2.0;

System.out.println(d1 == d2); // false
System.out.println(d3 == d4); // false

这次你就知道看看Double的valueOf()方法了,

public static Double valueOf(double d) {
    return new Double(d);
}

依次类推,其他包装类型的比较应该就比较容易了。不要乱猜,看看源码才是最准确的。所以你应该知道什么情况下使用形如:

Integer i = 1;
Integer i2 = new Integer(1);

的写法了吧,使用new关键字一定会创建新实例,而使用自动装箱则有可能触发取缓存实例,如果你知道你需要什么,你就知道使用哪种方式了。

下面有个综合的例子:

Integer i1 = 1;
Integer i2 = 2;
Integer i3 = 3;
Long long1 = 3L;
Long long2 = 2L;
System.out.println(i3 == (i1 + i2)); // true
System.out.println(i3.equals(i1 + i2)); // true
System.out.println(long1 == (i1 + i2)); // true
System.out.println(long1.equals(i1 + i2)); // false
System.out.println(long1.equals(i1 + long2)); // true

想要理解上述结果,需要先搞清楚几个规则:

1、不同类型之间运算,会自动进行类型转换,可能方向是byte/short/char->int->long->float->double

2、==的两个操作数中有一个是原始类型,那么比较的是原始类型的值

这样的话,就很好理解第三条和第四条的结果了。

这里延伸一个不同类型进行运算的例子:

System.out.println(1 + '1' + 1 + 1 + "0");

结果字符串"520",具体原因按照不同类型变量进行运算的转化规则来推敲。

下面我们说说字符串使用==和equals()方法的特点,先看个例子:

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true

有了上面的基础,这个应该很好理解,第一个false是因为str1和str2引用的是两个不同是实例,第二个true要看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;
}

可以看出,先使用==运算符,否则进行逐个字符比较,上面第二条返回true的原因显然走的是逐个字符比较的分支。再来看第二个例子:

String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2); // true
System.out.println(str1.equals(str2)); // true

首先说明,Java中的字符串字面值会放在常量池,常量池隶属于JVM永久代(方法区的实现)一部分,且JDK7中所谓的常量池被划分到堆中,JDK8中取消了永久代,新增元空间,常量池依然在堆中,这是有关常量池的背景,所以上面的两个对象指向了同一块空间,使用==运算一定返回true,使用equals()方法一定返回true,再看一个例子:

String str1 = "hello";
String str2 = "he" + "llo";
String str3 = new String("hello");
String str4 = str1 + str2;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str1 == str1.intern()); // true
System.out.println(str4 == "hellohello"); // false

需要注意的是第4行,两个String实例相加时,得到的是第三个实例,所以str4=="hellohello"返回false,String的intern()方法作用是如果常量池中不存在当前实例,则在常量池中创建并返回,上面的结果应该很明确了。

猜你喜欢

转载自blog.csdn.net/tales522/article/details/81225135