面试篇2:== 和 equals的区别(int,byte,long,short,double,Integer,String,char[],collection--set list)

本篇博文分析基本类型,引用类型(包装类型)‘’==‘’和equals的区别和使用过程中的注意事项。


大家好,我是27岁的小学生liu__peng


面试题:== 和 equals 的区别是什么?
答案:
  == 的作用:
  基本类型:比较值是否相等
  引用类型:比较内存地址值是否相等
 equals 的作用:
  引用类型:默认情况下,比较内存地址值是否相等。重写后比较的是地址值。当然,也可以根据自己的需求进行重写。
 

以下对具体的情况进行了说明,回顾相应的内容,也算是对本面试题的扩展与补充。

1.八大基本类型中,==和equals的使用

//基本类型中没有equals的方法,只能用==比较数值的大小是否相等。
//之前知识回顾,byte/short/char 三种比int小的整数可以用范围内的值直接赋值
        byte b=10;
        byte b1=10;
        System.out.println(b==b1);//true

        short s=110;
        short s1=110;
        System.out.println(s==s1);
        
//整数类型字面值是int类型,1000int类型,由小转大隐性转换,转换long类型。如果超过等号右面值超出int类型范围,必须加L或l
        long l=1000;
        long l1=1000;
        System.out.println(l==l1);//true
        long l2=10000000000;//编译错误
        long l2=10000000000;//正确编辑方式


        int i=200;
        int j=200;
        System.out.println(i==j);//true

        double d=3.15;
        double d1=3.15;
        System.out.println(d==d1);//true
        
//浮点型 字面值是double,需要加后缀转换成double类型
        float f=5.78f;
        float f1=5.78f;
        System.out.println(f==f1);//true

        char c=1;
        char c1=1;
        System.out.println(c==c1);//true

//boolean比较特别一些,只有在比较的两个数都是true时才是true
        boolean bo1=false;
        boolean bo2=false;
        System.out.println(bo1=bo2);

        boolean bo3=true;
        boolean bo4=true;
        System.out.println(bo3=bo4);//true


2.引用类型 String,new的对象,包装类型

引用类型等号是比较的地址值;
equals 没重写前比较的是地址值,(实例化没有重写equals的引用类型);
equals 重写后比较的是数据的属性值,(String,Integer,Double,Short,Long, BigDecimal等)。idea开发软件已经重写过了,比较的都是地址值

下面对各种情况进行了具体的分析,篇幅比较长

2.1 String类型
 通过底层代码可以知道,String是一个底层为value的char数组,是final修饰的,是不可变得字符串,存储在常量池中。但再次创建时,会首先从常量池中获取。

//    String类型
        String s="aa";
        String s1="aa";
        System.out.println(s==s1);//true 引用类型比较地址值,地址值相同
        System.out.println(s.equals(s1));//true String类型开发工具改写,比较的是属性值

  第一种情况:当采用字面量(给基本类型变量赋值的方式就叫做字面量或者字面值)创建字符串是,JVM首先会在字符串常量池中查找是否存在“aa”这个对象:
  如果不存在,则在字符串常量池中创建这个对象,然后将这个对象的地址引用返回给引用s;
  如果存在,则直接将这个对象的地址引用传递给引用s1,此时s和s1指向的是同一个常量池中的“aa”对象

       String s1 = new String("abc");
       String s2 = new String ("abc");
       System.out.println(s1 == s2);//返回false

  第二种情况:采用new关键字创建一个字符串对象时,JVM首先在字符串常量池中查找有没有“abc”字符串对象,如果有,则将这个字符串对象的引用传递给引用s1,然后在堆中创建一个"abc"字符串对象,最后将堆中的字符串地址返回给s1;
  如果没有,则先在字符串中创建一个"abc"对象,然后在堆中创建一个"abc"字符串对象,最后将堆中"abc"字符串的地址返回给s1。
  s2则指向堆中创建的另一个"abc"对象,这样s1和s2的内容是相同的,但是指向两个不同的"abc"对象。
就是说,通过new 构造器的方法创建之后,在heap堆中分别分配了两个内存地址。a 和 b 分别指向了堆中的两个不同的对象,不同的对象就会有不同的地址分配。
  这里还有另一个面试题 String s=new String(“abc”)创建了几个对象?,后期会再整理

应用的情况分析
  建议在平时的应用中,应尽量使用String x = "abcd"这种方式来创建字符串,而不是String x = new String(“abcd”); 因为用new构造器的方式肯定会开辟一个新的heap堆空间,而双引号的方式则是采用了String interning(字符串驻留)方式进行了优化,效率会比构造器的方式高。

扩展
判断各种字符串对象相等的情况

        String s = "ab";
        String s1 = "cd";
        String s2 = s + s1;
        String s3 = "abcd";
        String s4 = "ab" + "cd";
        String s5 = s + "cd";
        String s6 = "ab" + new String("cd");
        System.out.println(s2 == s3); // false
        System.out.println(s2 == s4); // false
        System.out.println(s3 == s4); // true
        System.out.println(s3 == s5); // false
        System.out.println(s2 == s5); // false
        System.out.println(s3 == s6); // false

  1)String类型运算时,默认new了StringBuilder对象,然后用append()方法追加字符串,最终以StringBuilder的toString()方法返回,而它的toString()方法是直接new了一个String对象返回的,那么 == 去比较两个String对象的地址,肯定是不相等的。

  2)像"a"+"a"这种没有变量参与则在编译时就能确定,这种拼接会被优化,编译器直接帮你拼好,就不需要new的操作。

        String a="a";
        String b="b";
        String c="c";
        String d=a+b+c;
        //等效于
         String d=new StringBuilder().append(a).append(b).append(c).toString();

任重而道远


**2.2 包装类型 Integer Double Short等 的分析

2.2.1 ===的分析**

//以Integer为例
         //第一种情况
          Integer integer1 = 127;
          Integer integer2 = 127;
          System.out.println(integer1 == integer2);//true
          //第二种情况
          Integer integer3 = 128;
          Integer integer4 = 128;
          System.out.println(integer3 == integer4);//false
          

  Integer类的底层为我们创建了int类型的-128~127一共256个对象。如果在自动装箱的时候给局部变量的int型值是在范围之内,就会直接把之前创建好的对象的地址值赋给等号左边的变量。如果在范围之外,就会重新new对象,两次new的对象就不再指向同一个对象了。在new的过程中并没有检测已有的空间,而是无条件的另外开辟了一个空间。

//比较两个new对象
       Integer a = new Integer(5);//创建对象
       Integer a1 = new Integer(5);//创建对象
       System.out.println(a==a1);//false

  每new一次就会在堆内存开辟空间,new两次就开辟两次空间,所以两次的地址值是不同的。

       Integer b = Integer.valueOf(5);//读取缓存
       Integer c = Integer.valueOf(5);//读取缓存
       System.out.println(b==c);//true
       Integer b = Integer.valueOf(128);//读取缓存
       Integer c = Integer.valueOf(128);//读取缓存
       System.out.println(b==c);//flase

  调用Integer.value()的方法的判定方法和上面情况一致,先看看有没有超出int类型的范围。

总结
1.Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)
2.nteger变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
3.非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
4.对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false。

        Integer i1=128;
        int i2=128;
        Integer i3=Integer.valueOf(128);
        Integer i4=new Integer(128);
        System.out.println(i1 == i2);//true
        System.out.println(i1 == i3);//false
        System.out.println(i3 == i4);//false
        System.out.println(i2 == i4);//true
        //涉及到自动拆箱和自动装箱的问题

2.2.2 equals的对比

  针对于包装类中的比较简单。包装类中的equals的方法都是经过重写的,比较的都是属性值,因此,只要属性值一致就返回true,反之返回false。

//重写后的代码如下
public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

//测试,四种赋值方式情况下,equals的对比
        Integer i1=128;
        int i2=128;
        Integer i3=Integer.valueOf(128);
        Integer i4=new Integer(128);
        System.out.println(i1.equals(i2));//true
        System.out.println(i1.equals(i3));//true
        System.out.println(i3.equals(i4));//true
        System.out.println(i1.equals(i4));//true

你以为你真的会了吗?
看一个面试题

     Integer i1=125;
     Integer i2=125;
     Integer i3=0;
     Integer i4=new Integer(127);
     Integer i5=new Integer(127);
     Integer i6=new Integer(0);
     System.out.println(i1==i2);
     System.out.println(i1==i2+i3);
     System.out.println(i4==i5);
     System.out.println(i4==i5+i6);
     i3=5;
     Integer i7=130;
     System.out.println(i7==i2+i3);
     //如果你做对了,说明你已经正在掌握了相应的知识点。

    对于 i1 == i2 + i3 、 i4 == i5 + i6 和 i7 == i2 + i3 结果为 true,是因为,Java 的数学计算是在内存栈里操作的,Java 会对 i5、i6 进行拆箱操作,其实比较的是基本类型(127=127+0),他们的值相同,因此结果为 true。对 i2+i3 来说,结果是在内存栈中(同 int 基本类型一样),所以不管是与 i1 还是 i7 比较,返回结果都为 true。

//答案
        true
        true
        false
        true
        true

大家好,我是27岁的小学生liu__peng;
2021.01.07 0:21版本

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值