==与equals比较

作者: 玉圣 | 来源:发表于2019-04-10 17:18 被阅读5次
    目录:
    • 一、比较
      • 自定义类型的比较
      • 系统类型的比较
        • 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方法。

    相关文章

      网友评论

        本文标题:==与equals比较

        本文链接:https://www.haomeiwen.com/subject/hlkgiqtx.html