java中==运算符和equals的区别总结

一、java数据类型和数据的存储方式

1、Java数据类型可以分为基本数据类型和引用数据类型。
 

图1:java中的数据类型

其中,Object类是所有类的父类,每个类都实现Object类的方法。Object类中定义了equals方法,源码如下:

图2:Object类中equals方法定义

可以看到,在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。

2、java中基本数据类型和引用数据类型的存储

(1)基本数据类型不存在“引用”的概念,数据都是直接存储在内存的栈空间中的,数据本身的值就是存储在栈空间里面,Java语言里面八种基本数据类型是这种存储模型;

(2)引用数据类型继承于Object类(也是引用类型)都是按照Java里面存储对象的模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”(存储对象在内存堆上的地址)是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的。

(3)总结一下就是:不论是基本数据类型还是引用类型,他们都会先在栈中分配一块内存,对于基本类型来说,这块区域包含的是基本类型的内容;而对于引用类型来说,这块区域包含的是指向真正内容的指针,真正的内容被手动的分配在堆上。

知道以上内容,接下来再来看一些经典的关于==运算符和equals的程序输出就不会难以理解了。

二、==运算符和equals区别概述
1、==运算符和equals到底比较的是什么?

(1)关于==运算符,下面这个句话是摘自《Java编程思想》一书中的原话:

“关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系”。

说的简单点,双目运算符==就是用来比较两个变量存储的值是否相等。

(2)equals最初定义在Object类,如图2,equals用来比较两个对象的引用是否相等,即是否指向同一个对象。在没有重写父类Object类的equals方法时,这就是其实际比较的内容。

如果此时你觉得对==运算符和equals方法已经掌握的差不多了,那就……大错特错了!

2、关于==运算符和equals的一些经典示例
 

示例1、

  int a=300,b=300;
  System.out.println(a==b);
  //下面注释掉的会报错,equals只能作用于对象,不能做用于基本数据类型
  //System.out.println(a.equals(b));

结果:true

(1)第1条输出语句因为a和b都是基本数据类型, 数据直接存储在栈区,a和b存储的值都300,==用来比较两个变量存储的值是否相等,所以结果为true。

(2)第2条被注释掉的输出语句会报错,因为equals只能作用于对象,不能作用于基本数据类型。

示例2、

Person p1 = new Person(1001, "latiny1");

Person p2 = new Person(1001, "latiny1");

System.out.println(p1 == p2);

System.out.println(p1.equals(p2));

结果:false,false
这里"=="比较的是内存地址,"equals"比较的也是地址,没有重写equals方法的类都是调用的Object类的equals的方法。

p1和p2存储的是引用数据类型(即Person对象)的地址,Person对象存储在堆区,虽然两个对象的值相同,但它们依然是两个不同的对象,分别存储在堆区的不同位置,地址不相同,所以p1和p2存储的值不相等。

示例3、

public class test5 {
    public static void main(String[] args){
        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1==str2);
        System.out.println(str1.equals(str2));
        String str3=str2;
        System.out.println(str1==str3);
        System.out.println(str1.equals(str3));

    }
}

结果:false,true,false,true

第二个结果为true是因为String类继承基类Object类并重写了其equals方法:

图3:String类中equals方法重写

可以看出,String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。

这个程序中,str1和str2分别指向两个不同的hello对象,虽然地址不同,但是存储内容相同。

示例4、

public class test4 {
    public static void main(String[] args){
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}


i1,i2,i3,i4中调用valueOf方法通过自动装箱特性自动将基本数据类型转换为对应的封装类类型,分别生成值为100,100,200,200的Integer对象(Integer是引用数据类型,要和基本数据类型int区分)。

结果:true,false

为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:
 

其中IntegerCache类的实现为:
 

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() {}
    }

 从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。

示例5、

public class test3 {
    public static void main(String[] args){
        Integer i1 = new Integer(10);
        Integer i2 = new Integer(10);
        System.out.println(i1 == i2);
        System.out.println(i1.equals(i2));
    }
}

结果:false,true

是不是很吃惊为什么第一个结果又变成了false?

因为这个例子中使用new Integer(10)创建的对象,不涉及自动装箱以及调用valueOf方法,虽然两个对象值相同,但是存储在堆区不同位置,地址不同,所以false。

示例6、

public class other {
    public static void main(String[] args){
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1==str2);
        System.out.println(str1.equals(str2));
    }

结果:true,true

Java String 和 new String()的区别:

(1)String str1 = "hello"的实现过程:首先栈区创建str1引用,然后在String池(独立于栈和堆而存在,存储不可变量)中寻找其指向的内容为"hello"的对象,如果String池中没有,则创建一个,然后str1指向String池中的对象,如果有,则直接将str1指向"hello"";如果后来又定义了字符串变量 str2 = "hello",则直接将str2引用指向String池中已经存在的“hello”,不再重新创建对象;当str1进行了赋值(str1=“abc”),则str1将不再指向"hello",而是重新指向String池中的"abc",此时如果定义String str3 = "abc",进行str1 == str3操作,返回值为true,因为他们的值一样,地址一样,但是如果内容为"abc"的str1进行了字符串的+连接str1 = str1+"d";此时str1指向的是在堆中新建的内容为"abcd"的对象,即此时进行str1==str2,返回值false,因为地址不一样。

(2)String str3 = new String("abcd")的实现过程:直接在堆中创建对象。如果后来又有String str4 = new String("abcd"),str4不会指向之前的对象,而是重新创建一个对象并指向它,所以如果此时进行str3==str4返回值是false,因为两个对象的地址不一样,如果是str3.equals(str4),返回true,因为内容相同。

示例7、

public class test {
    public static void main(String[] args){
        int a=10;
        long b=10L;
        double c=10.0;
        System.out.println(a==b);
        System.out.println(b==c);
        System.out.println(a==c);
    }
}

结果true,true,true

a,b,c都存储在栈中,值相等,==用来比较两个变量存储的值是否相等。

三、java中==运算符和equals的区别总结
(1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址。

(2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量;

 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

 诸如String、Double,Date,Integer等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

参考文献:

(1)Java基本数据类型和引用类型 - 知乎

(2)Java 基本数据类型 及 == 与 equals 方法的区别 - Latiny - 博客园

(3)浅谈Java中的equals和== - Matrix海子 - 博客园

(4)你知道 JVM 的方法区是干什么用的吗? - 知乎

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值