==与equals比较

目录:
  • 一、比较
    • 自定义类型的比较
    • 系统类型的比较
      • String类的比较
      • 包装类的比较
  • 二、结论
    • ==的比较
    • equals的比较
    • 说明
结论:

先说结论:

  • ==
    基础类型变量:比较两个变量的 是否相等
    引用类型变量:比较的是两个变量在 堆中对象的存储地址 是否相等,即栈中的内容 是否相等。
    ---> 对象地址的比较
  • equals
    比较的是两个变量是否是对同一个对象的引用,即 堆中的内容 是否相等
    ---> 对象内容的比较
  • 说明:
    String类对equals 方法进行了重写,在比较String时,需要使用equals来比较是否相等。
    包装类中也对equals 方法进行了重写,比较时最好也使用此方法。

一、比较:

  • 说明:由于大部分类中都重写了equals方法,所以下面的从两方面来说:系统类的比较和自定义类的比较。

1、自定义类型的比较:

  • 先看例子:
       public class Person {
       
           String name;
       
           public Person(String name) {
               this.name = name;
           }
       
           public String getName() {
               return name;
           }
       
           public void setName(String name) {
               this.name = name;
           }
       
           /*
           @Override
           public boolean equals(Object obj) {
               if (!(obj instanceof Person)) {
                   return false;
               }
               return this.name.equals(((Person) obj).name);
           }
           */
       }
    
        public static void main(String[] args) {
            Person p1 = new Person("Tom");
            Person p2 = new Person("Tom");
      
            //false
            System.out.println("p1==p2? ==>" + (p1==p2));
             //equals方法重写之前:false
             //equals方法重写之后:true
            System.out.println("p1.equals(p2)? ==>" + (p1.equals(p2)));
        }
    

例子很简单,但凡懂Java/Android的人知道,用==比较这两个Person绝对是不等的,因为是new的两个对象嘛,好比你有两个女盆友:前任对象和现任对象,那绝对不是一个人!

而在重写equals方法之前,两个对象也是不等的,起始从Person类的父类Object中可以看到,equals方法中用的就是==的比较。

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

但当我重写了equals方法之后,就不一样了,两个new出来的对象是相等的!(难道我的前任女友和现任女友是一个人?姓名都不一样嘛!)

这就是系统中的很多类都重写equals 的原因,一般的,如果两个对象中的内容完全一样,就认为这两者是同一个对象,所以我在这里,认为两个名字相同的Person对象就是同一个。(当然,实际开发中根据情况而定)

2、系统类型的比较:

  • 说明:主要是针对String和包装类(如Integer等)来说。
2.1、String类的比较
  • 先看例子:

        public static void main(String[] args) {
            String a = "hello";
            String b = "hello";
    
            String c = new String("hello");
            String d = c.intern();
    
            //a==b?==>true
            System.out.println("a==b?==>" + (a==b));
            //a.equals(b)?==>true
            System.out.println("a.equals(b)?==>" + (a.equals(b)));
    
            //a==c?==>false
            System.out.println("a==c?==>" + (a==c));
            //a.equals(c)?==>true
            System.out.println("a.equals(c)?==>" + (a.equals(c)));
    
            //d==c?==>false
            System.out.println("d==c?==>" + (d==c));
            //d.equals(c)?==>true
            System.out.println("d.equals(c)?==>" + (d.equals(c)));
    
            //d==a?==>true
            System.out.println("d==a?==>" + (d==a));
            //d.equals(a)?==>true
            System.out.println("d.equals(a)?==>" + (d.equals(a)));
        }
    
  • 1、分析:

    • ab 指向同一个字符串,是相等的,这个母庸质疑。
    • ac== 比较是不等的,这是因为c 是通过new 出来的,所以会新申请一个内存地址,是一个新对象,所以其栈内存放的地址是和aa 的对象在常量池红) 不同的。
    • 再看最后两组比较,你会发现用== 比较,c是和d不等的,而a竟然和d相等,这个就涉及到intern方法的返回值含义了。insert方法是用来 返回字符串对象的规范化表现形式 的,具体在下面解释。
  • 2、概念:
    在得出结论之前,先说一下内存中的堆和栈,以及常量池。

    • 栈(Stack):存放 基本类型的数据对象的引用。对象本身并不存放在栈中。
    • 堆(Heap):存放由new创建的 对象和数组
    • 常量池(Constant Pool):JVM必须为每个被装载的类型维护一个常量池。
      常量池就是该类型所用到常量的一个有序集合,包括直接常量(基本类型、String)和对其他类型、字段和方法的符号引用。
  • 3、结论:
    了解了上面的概念,就好得出结论了,先看图:


    String内存赋值示意图
    • 第一步定义a的时候,会在常量池中放入字符串"hello"

    • 定义b的时候,由于常量池中已经有了字符串常量"hello",就直接指向了它。

    • 当创建字符串对象c的时候,会先去常量池中检查是否有字符串"hello",发现有了,就直接new 一个对象。
      这里注意了,为什么说要先去找常量池中是否有创建的字符串呢?如果ca 之前定义,则会多一步操作,就是发现常量池中没有创建的字符串,会先在常量池中创建一个字符串,然后在new 对象。这应该是为了共享。

    • 通过示意图和上面的结论可以得出:
      使用== 比较,比较两个是否相等。
      而由于String类重写了equals 方法,则不管是否是两个对象,只要其中的内容是相等的,就是相等的。

  • 4、面试题:String a = new String("hello"); 创建了几个对象?
    对于这个问题,就是我上面所分析的,创建几个对象,就要看其中的字符串(hello)是否存在于常量池中(准确的说是StringPool这个池子),如果常量池中没有这个字符串,则会先在常量池中创建一个字符串常量对象,然后在堆内存中new对象,那就是两个对象。否则会直接在堆内存中new对象,那就是一个对象。

2.2、包装类的比较
  • 先看例子:
    例子1:
        public static void main(String[] args) {
            Integer a = 127;
            Integer b = 127;
    
            Integer c = new Integer(127);
            int d = c.intValue();
            int e = 127;
    
            //a==b?==>true
            System.out.println("a==b?==>" + (a==b));
            //a.equals(b)?==>true
            System.out.println("a.equals(b)?==>" + (a.equals(b)));
    
            //a==c?==>false
            System.out.println("a==c?==>" + (a==c));
            //a.equals(c)?==>true
            System.out.println("a.equals(c)?==>" + (a.equals(c)));
    
            //d==c?==>true
            System.out.println("d==c?==>" + (d==c));
            //d==a?==>true
            System.out.println("d==a?==>" + (d==a));
            //e==c?==>true
            System.out.println("e==c?==>" + (e==c));
        }
    
    例子2:
        public static void main(String[] args) {
            Integer a = 128;
            Integer b = 128;
      
            Integer c = new Integer(128);
            int d = c.intValue();
            int e = 128;
      
            //a==b?==>false   <----看这里
            System.out.println("a==b?==>" + (a==b));
            //a.equals(b)?==>true
            System.out.println("a.equals(b)?==>" + (a.equals(b)));
    
            //a==c?==>false
            System.out.println("a==c?==>" + (a==c));
            //a.equals(c)?==>true
            System.out.println("a.equals(c)?==>" + (a.equals(c)));
    
            //d==c?==>true
            System.out.println("d==c?==>" + (d==c));
            //d==a?==>true
            System.out.println("d==a?==>" + (d==a));
            //e==c?==>true
            System.out.println("e==c?==>" + (e==c));
        }`
    

这里都是用int的包装类Integer 来说明的,其他的几种包装类都一样。

  • 1、分析:

    • 先说比较容易理解的,就是用==比较ac不相等,因为是两个对象,所以内存地址是不等的。

    • equals 比较的,都是相等的,这个是由于包装类Integer等都重写了Objectequals 方法,比较的是其中的值是否相等。

    • 从两个例子可以看出,a==b 的结果不同,是不是觉得很奇怪呢?这也是由于共享数据的原因,Integer共享的范围是-128 ~ 127。这个可以看一下Integer的源码中的valueOf方法。

    • 再看两个例子中的最后三个比较,intInteger 的比较,只要是值相等,就都是相等的。这里涉及到一个自动拆箱的过程。

  • 2、概念:

    • 共享数据:java为了优化内存,提高性能,就单开了一片内存池(pool),也就是说,在这里共享了那些固定不变的数据(我个人理解),如数字和字符串等等,这也是一种对象。

      重点说Integerint,在内存池中定义他们的范围是 -128 ~ 127,这里的数是共享的,其实共享的是地址,就是变量指向的地址。(题外话:变量其实都是指向的地址,地址才是代表一块内存的空间的。)java为了提高效率,初始化了-128--127之间的整数对象,所以,如果写Integer a =100;的话,是在内存池中创建了一个变量为a的对象,再写b=100,就共享了100这个数据,其实是指向了相同地址。但是如果超过了这个范围的话,这数据就不是共享的了,指向的不是相同地址。所以就相等了。

    • 自动封箱和自动拆箱:
      先看一个小栗子:

           public static void main(String [] args) {  
               Integer x = 4;  
               x = x + 2;  
               System.out.println("x" + x);  
           }  
      

      这里面就涉及到了自动拆箱和自动封箱的过程:
      1、自动封箱:其中Integer x = 4; 就是自动封箱的过程,即Integer x = new Integer(4);
      2、自动拆箱:其中x+2 就是进行了自动拆箱的过程,将x 变为int 类型,再和2进行加法运算,在拆箱的时候,要先进行 x.intValue() 的判断,是否xnull,不可为null ,否则编译失败。
      3、自动封箱:再将求的和进行封装,赋值给x

  • 3、结论:

    • 使用equals 比较的包装类,比较的是其中的 是否相等。
    • 使用== 比较的包装类,如果在共享范围内-128~127,则比较的是两个 是否相等。否则比较的是对象的 存储地址 是否相等。

二、结论

1、== 的比较:
  • 基础类型变量:比较两个变量的 是否相等
  • 引用类型变量:比较的是两个变量在 堆中对象的存储地址 是否相等,即 栈中的内容 是否相等。---> 对象地址的比较
2、equals的比较:
  • 比较的是两个变量是否是对同一个对象的引用,即 堆中的内容 是否相等。
    ---> 对象内容的比较
3、说明:
  • String类对equals 方法进行了重写,在比较String时,需要使用equals来比较是否相等。
  • 包装类中也对equals 方法进行了重写,比较时最好也使用此方法。
  • 对象的比较,基本都是用equals来比较是否相等。
  • 在自定义的类中,要依据情况来决定是否需要重写equals方法。
  • 如果无法判断两个对象(比如a和b)是否某一个为null时,可以通过Objects.equals(a, b) 来进行比较,其内部是对a 进行null 值判断,然后才比较的a.equals(b)
  • 在Java规范中说明,在比较两个枚举类型的值时,永远不需要调用equals 方法,而直接使用== 就可以了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 828评论 1 6
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,580评论 1 114
  • DAY 05 1、 public classArrayDemo { public static void mai...
    周书达阅读 654评论 0 0
  • (一) 想你时 我骨瘦如柴 躲在黑暗的角落里 自己抱紧自己 我的快乐 风干在回忆里 我的忧伤 折叠在夕阳的余晖里 ...
    雪人童话阅读 176评论 4 7
  • 第4部分 激发潜能 第1节受惊让人生病,抗寒使人更强 保持身体健康也意味着不生病。那怎样才能有效地预防疾病?我一直...
    待闲一天阅读 1,344评论 1 0