Java基础面试题3:==和equals的区别

需要注意的是我们自定义的类的equals方法是可以自己重写的,也就是说判断标准可以自己定,不过面试中应该都是针对Object类和String类的equals方法

面试这么回答就可以
  1)对于==,比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
  2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
————————————————
版权声明:本文为CSDN博主「小小鱼儿小小林」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_27471405/article/details/81010094

==和equals在默认的情况下都是比较地址
==比较基本数据类型时,比较的是值是否相等(变量值)
==比较引用类型,比较引用指向的值(堆中内存对象的地址)
==比较的是栈中的值

引用类型至少占两块内存
比较基本数据类型实际上是比较栈中到的数值是否相等(基本数据类型在栈中分配)
比较引用类型比较的是指向的堆中的地址是否相等
引用保存的就是地址值
在这里插入图片描述

对于基本数据类型:(byte,short,char,int,float,double,long,boolean),比较的是值,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的,对于这样的字符串 “123” 也是相同的道理,在常量池中,一个常量只会对应一个地址,因此不管是再多的 123,“123” 这样的数据都只会存储一个地址,所以所有他们的引用都是指向的同一块地址,因此基本数据类型和String常量是可以直接通过==来直接比较的
————————————————
版权声明:本文为CSDN博主「goforitaaa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/goforitaaa/article/details/91416373

equals默认比较的是地址,这个方法最初定义在Object上,默认的实现是比较地址

Object a=new Object();
Object b=new Object();
System.out.println(a.equals(b));//false
System.out.println(a==b);//false

我们看看equals的源码,就是用==实现的

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

对于自定义的类,如果要比较内容是否相等,就需要重写equals方法
String类就重写了equals方法(就是先判断是不是String类,然后把每一个字符取出来判断)

 /**
     * Compares this string to the specified object.  The result is {@code
     * true} if and only if the argument is not {@code null} and is a {@code
     * String} object that represents the same sequence of characters as this
     * object.
     *
     * @param  anObject
     *         The object to compare this {@code String} against
     *
     * @return  {@code true} if the given object represents a {@code String}
     *          equivalent to this string, {@code false} otherwise
     *
     * @see  #compareTo(String)
     * @see  #equalsIgnoreCase(String)
     */
    public boolean equals(Object anObject) {
        if (this == anObject) {//判断当前对象与要比较的对象的地址是否相等
            return true;//地址相等则返回true
        }
        if (anObject instanceof String) {//地址不相等,先判断是不是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;//不是String的实例,直接返回false
    }
 public static void main(String[] args) {
        String s1=new String("123");
        String s2=new String("123");
        System.out.println(s1==s2);//false,两次new了对象,地址不一样
        System.out.println(s1.equals(s2));//true
        String s3="123";
        String s4="123";
        System.out.println(s3==s4);//true,指向常量池的同一个地址
        System.out.println(s3.equals(s4));//true
        System.out.println(s3==s1);//false,s1指向堆中的地址,s3指向常量池的地址
        System.out.println(s3.equals(s1));//true
        String s5="123123";
        String s6=s3+s4;//因为字符串是不可变的对象,做相加会创建新的对象
        System.out.println(s5==s6);//false ,s6拼接字符串会创建对象
        System.out.println(s5.equals(s6));//true
        final String s7="123";
        final String s8="123";
        String s9=s7+s8;//因为加了final,s7和s8是常量,不是变量了
        System.out.println(s5==s9);//true
        System.out.println(s5.equals(s9));//true
        final String s10=s3+s4;//s3和s4是变量,相加就会new,所以下边是false
        System.out.println(s5==s10);//false
        System.out.println(s5.equals(s10));//rue
    }
}

JVM把内存划分成两种:一种是栈内存,一种是堆内存。 当我们创建一个对象(new Object)时,就会调用对象的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用,当我们在后续代码中调用的时候用的都是栈内存中的引用。还需注意的一点,基本数据类型是存储在栈内存中。
在函数中定义的一些基本类型的变量和对象的引用变量(变量名)都在函数的栈内存中分配
②当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
堆内存用来存放由new创建的对象(包括由基本类型包装起来的类:Integer、String、Double,实际上每个基本类型都有他的包装类)和数组
————————————————
版权声明:本文为CSDN博主「goforitaaa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/goforitaaa/article/details/91416373

String s1 = "hello";
String  s2 = "hello";
System.out.println( s1==s2); //true
s1.equals(s2);//true   

上面的判断当执行String s1 = “hello”;这条语句时,会在堆中的字符常量池里找”hello”这个字符串,若没有找到,则将”hello”这个字符串放入字符串常量池中.而在栈中开辟一块名为s1的空间存放”hello”,这块空间的引用.当执行String s2 = “hello”;这条语句时,会在堆中的字符串常量池里找”hello”这个字符串,很显然,可以找到,于是便把字符常量池里”hello”这个字符串的引用地址赋给s2,因此s1与s2存放的都是堆中字符常量池中的同一个”hello”的引用
————————————————
版权声明:本文为CSDN博主「goforitaaa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/goforitaaa/article/details/91416373

==比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是 ==的判断。String s=“abcd"是一种非常特殊的形式,和new 有本质的区别。它是java中唯一不需要new 就可以产生对象的途径。以String s=“abcd”;形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。 这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象, 如果没有,则在常量池中新创建一个"abcd”,下一次如果有String s1 = “abcd”;又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.
   而String s = new String(“abcd”);和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。
也可以这么理解: String str = “hello”; 先在内存中找是不是有"hello"这个对象,如果有,就让str指向那个"hello".
如果内存里没有"hello",就创建一个新的对象保存"hello". String str=new String (“hello”) 就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。
————————————————
版权声明:本文为CSDN博主「MrBoringBigFish」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36522306/article/details/80550210

非String类,例如StringBuffer类 没有重写equals方法,所以不比较内容。==和equals都是比较的内存地址

StringBuffer stringBuffer = new StringBuffer("aaa");
StringBuffer stringBuffer2 = new StringBuffer("aaa");
System.out.println(stringBuffer == stringBuffer2); // false
System.out.println(stringBuffer.equals(stringBuffer2)); // false

可以看到,我点击equals,直接跳转到了Object类下面,这是因为StringBuffer类 没有重写equals方法
在这里插入图片描述

包装类

对于基本数据的包装类型(Byte, Short, Character,Integer,Float, Double,Long, Boolean)除了Float和Double之外,其他的六种都是实现了常量池的,因此对于这些数据类型而言,一般我们也可以直接通过==来判断是否相等。

例1

        Integer a1=127;
        Integer a2=127;
        Integer a3=new Integer(127);
        System.out.println(a1==a2);//true
        System.out.println(a1==a3);//false
        System.out.println(a1.equals(a3));//true

根据Integer的equals方法,可以得出,如果obj是Integer类型且value值相等,那么就返回true

 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

对于a1 == a2为true,是因为定义一个Integer变量时,会默认进行Integer.valueOf(a)操作,high的值为127,low的值为-128,当进行这个方法时如果值在-128-127之间,返回的值也就是地址是相同的,所以a1和a2的地址相同,a1==a2自然为true

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

例2

        Integer a1=128;
        Integer a2=128;
        Integer a3=new Integer(128);
        System.out.println(a1==a2);//false
        System.out.println(a1==a3);//false
        System.out.println(a1.equals(a3));//true

Integer 在常量池中的存储范围为[-128,127],127在这范围内,因此是直接存储于常量池的,而128不在这范围内,所以会在堆内存中创建一个新的对象来保存这个值,所以m,n分别指向了两个不同的对象地址,故而导致了不相等
简要的说就是在Integer类中有一个静态内部类IntegerCache,在IntegerCache类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。

在这里插入图片描述
例3

        int a1=127;
        Integer a2=127;
        System.out.println(a1==a2);//true
        System.out.println(a2.equals(a1));//true
        int a3=128;
        Integer a4=128;
        System.out.println(a3==a4);//true
        System.out.println(a4.equals(a3));//true
intint之间,==比较,肯定为true,基本数据类型没有equals方法
int和integer比较,Integer会自动拆箱,==和equals结果都为true
intnew Integer之间比较,Integer会自动拆箱,调用intValue方法,所以==和equals结果都为true
Integer和Integer之间
    1)直接赋值:
      会进行自动装箱,所以当值在[-128,127],在这个区间内赋值不会创建新的对象,而是直接从缓存中获取已经创建好的Integer对象. 而当大于这个区间的时候,就会创建新的对象;
      所以在进行==比较的时候,[-127,128]区间内的值比较结果为true;大于该区间的值比较结果为false;
   2)当Integer与Integer进行equals比较时,由于Integer重写了equals方法,比较的是内容,所以结果为true;
   3)integer和new integer:
      new integer会创建新的对象,存储在堆中,而integer在[-127,128]中是从缓存中取;所以integer和new integer进行 == 比较,结果为false,若进行equals比较,结果为true
   4)new integernew integer之间比较:
      进行 == 比较的时候结果为false,进行equals比较的时候结果为true

一般类的比较

Scanner scanner = new Scanner(System.in);      
Scanner scanner2 = new Scanner(System.in);
System.out.println(scanner.equals(scanner2));       //false
Scanner sc = scanner;
System.out.println(scanner.equals(sc));            //true

执行第一条语句Scanner scanner = new Scanner(System.in); 时在堆中开辟了一块内存存放Scanner对象,在栈内存中开辟一块名为scanenr的内存存放Scanner对象的引用.

执行第二条语句Scanner scanner2 = new Scanner(System.in); 时时在堆中另外开辟了一块内存存放Scanner对象,在栈内存中开辟一块名为scanenr2的内存存放Scanner对象的引用.
因为这里调用的是一般对象的equals方法,因此比较的是两个对象是否属于同一个对象,显然不是同一个对象.
可见在非String类中, ==和equals的作用都是一样的,只不过在String类中重写了equals方法,才会变得这么复杂!!!
在这里插入图片描述

推荐阅读

== 和equals区别
Integer对象范围(-128-127)之间
Integer的自动拆装箱的陷阱(整型数-128到127的值比较问题)
Java面试题 :byte b = (byte)128; 定义变量语句是否正确
int和integer------比较(equals和==)

一道面试题关于Integer的缓存范围(-128~127)所引起的一系列问题记录
优美的讲解equals和==的区别

浅谈Java中equals()和==的区别

关于==和equals的区别和联系,面试这么回答就可以

面试基础整理(一)—Java中==和equals的区别

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值