java中的equals和==

Java中equals和==的区别

(Java中equals和==的区别 - BarneyZhang - 博客园)

1.==的含义

==比较两个变量本身的值,即两个对象在内存中的首地址。

java中,对象的首地址是它在内存中存放的起始地址,它后面的地址是用来存放它所包含的各个属性的地址,所以内存中会用多个内存块来存放对象的各个参数,而通过这个首地址就可以找到该对象,进而可以找到该对象的各个属性。

java中的数据类型,可分为两类:

2.基本数据类型即:原始数据类型

byte,short,char,int,long,float,double,boolean,他们之间的比较,应用双等号(==),比较的是他们的值。

int a =1;
int b =1;
//true
System.out.println(a==b);

3.复合数据类型(对象)

当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们比较后的结果为true,否则比较后结果为false。

JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址。

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

对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。

String的比较

但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。

public class TestString {
    public static void main(String[] args) {
        String s1 = "Monday";
        String s2 = "Monday";
        //true
        if (s1 == s2) {
            System.out.println("s1 == s2");
        } else {
            System.out.println("s1 != s2");
        }
    }
}

编译并运行程序,输出:s1 == s2说明:s1 与 s2 引用同一个 String 对象 -- "Monday"!

public class TestString2 {
    public static void main(String[] args) {
        String s1 = "Monday";
        String s2 = new String("Monday");
        //地址不一样
        if (s1 == s2) {
            System.out.println("s1 == s2");
        } else {
            System.out.println("s1 != s2");
        }
        //内容相同
        if (s1.equals(s2)) {
            System.out.println("s1 equals s2");
        } else {
            System.out.println("s1 not equals s2");
        }
    }
}

s1 != s2 s1 equals s2 说明:s1 s2分别引用了两个"Monday"String对象

原来,程序在运行的时候会创建一个字符串缓冲池。

第一段程序。当使用 s1 = "Monday" 这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,s1先被放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1。然后s2引用s1所引用的对象"Monday"

第二段程序中,使用了 new 操作符,他明白的告诉程序:"我要一个新的!不要旧的!"

于是一个新的"Monday"Sting对象被创建在内存中。他们的值相同,但是位置不同,一个在池中游泳一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?

public class TestString3 {
    public static void main(String[] args) {
        String s1 = "Monday";
        String s2 = new String("Monday");
        s2 = s2.intern();
        if (s1 == s2) {
            System.out.println("s1 == s2");
        } else {
            System.out.println("s1 != s2");
        }
        if (s1.equals(s2)) {
            System.out.println("s1 equals s2");
        } else {
            System.out.println("s1 not equals s2");
        }
    }
}

这次加入:s2 = s2.intern();

程序输出:

s1 == s2
s1 equals s2

原 来,java.lang.String的intern()方法比如"abc".intern()方法的返回值还是字符串"abc",表面上看起来好像这个方 法没什么用处。

但实际上,它做了个小动作:检查字符串池里是否存在"abc"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会 把"abc"添加到字符串池中,然后再返回它的引用。 )

String的比较思考题

public class TestString4 {
    public static void main(String[] args) {
        String s3 = "abc", s4 ="abc" ;
        s1 = new String("abc");
        s2 = new String("abc");
        System.out.println("s1==s2:"+(s1==s2));
        System.out.println("s1==s3:"+(s1==s3));
        System.out.println("s3==s4:"+(s3==s4));
        System.out.println("s1.equals(s2):"+(s1.equals(s2)));
        System.out.println("s1.equals(s3):"+(s1.equals(s3)));
        System.out.println("s3.equals(s4):"+(s3.equals(s4)));
    }
}

java中的equals和==_第1张图片

 

代码示例

package base01;
​
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
​
import java.lang.reflect.Field;
​
public class StringTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        String str1 = "abc";
        String str2 = new String("abc");
        String str3 = str2.intern();
        String str4 = "abc";
        String str5 = new String("abc".intern());
        System.out.println(str1 == str2);
        System.out.println(str2 == str3);
        System.out.println(str1 == str3);
        System.out.println(str1==str4);
        System.out.println(str1==str5);
​
        System.out.println(System.identityHashCode(str1));
        System.out.println(System.identityHashCode(str2));
        System.out.println(System.identityHashCode(str3));
        System.out.println(System.identityHashCode(str4));
        System.out.println(System.identityHashCode(str5));
​
        System.out.println("--------------------------");
​
​
        Class clazz = Class.forName("java.lang.String");
        Field[] declaredFields = clazz.getDeclaredFields();
        Field field = null;
        for (int i = 0; i < declaredFields.length; i++) {
            if (declaredFields[i].getName().equals("value")) {
                field = declaredFields[i];
            }
        }
        field.setAccessible(true);
        //获取str1的value字段类型是char基本类型
        Object o1 = field.get(str1);
        //获取str2的value字段类型是char基本类型
        Object o2 = field.get(str2);
        //获取str2的value字段类型是char基本类型
        Object o3 = field.get(str3);
        System.out.println(o1.hashCode());
        System.out.println(o2.hashCode());
        System.out.println(o3.hashCode());
        System.out.println(System.identityHashCode(o1));
        System.out.println(System.identityHashCode(o2));
        System.out.println(System.identityHashCode(o3));
        System.out.println(o1==o2);
        System.out.println(o2==o3);
​
        char[] a = new char[3];
        a[0] = 'a';
        a[1] = 'b';
        a[2] = 'c';
        System.out.println(a.hashCode());
​
        char[] b = new char[3];
        b[0] = 'a';
        b[1] = 'b';
        b[2] = 'c';
        System.out.println(b.hashCode());
​
        char[] c = {'a','b','c'};
        System.out.println(c.hashCode());
​
​
        char[] d = (char[]) o1;
        System.out.println(d.hashCode());
​
​
        /***
        为什么上面的(o1,o2,o3)hashCode和下面的(a,b,c,d)hashCode不一样?
        注意看String的构造方法:
         public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
         }
        将传入的original的value字段给了当前要创建的String的value
         value的声明如下: private final char value[];
​
         这意味着什么呢?
         String str1 = "abc";
         String str2 = new String("abc");
         String str3 = new String("abc".intern());
         str1创建了1个abc放入了字符串常量池
         str2使用构造方法创建了1个String对象传入的是"abc"
         str3使用构造方法创建了1个String对象传入的是"abc".intern()
         那么str2中的"abc"和str3中的"abc"是1个吗?
         答案是1个
         String str2 = new String("abc");
         相当于
         String str3 = new String("abc".intern());
         所以o1=o2=o3
         */
​
​
    }
}
​

结果输出

false
false
true
true
false
1300109446
1020371697
1300109446
1300109446
789451787
--------------------------
1229416514
1229416514
1229416514
1229416514
1229416514
1229416514
true
true
2016447921
666988784
1414644648
1229416514

String str=“abc”

当代码中使用第一种方式创建字符串对象时,相当于“abc”.intern();

JVM 首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。

new String(“abc”)

String str = new String(“abc”) 这种方式,首先在编译类文件时,常量字符串"abc"将会被放入到常量结构中,在类加载时,“abc"将会在常量池中创建;

其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"abc” 字符串,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象。

这里附上一个你可能会想到的经典反例。

平常编程时,对一个 String 对象 str 赋值“hello”,然后又让 str 值为“world”,这个时候 str 的值变成了“world”。那么 str 值确实改变了,为什么我还说 String 对象不可变呢?

首先,我来解释下什么是对象和对象引用。

Java 初学者往往对此存在误区,在 Java 中要比较两个对象是否相等,往往是用 ==,判断的是两个对象的地址值是否相等。

这是因为 str 只是 String 对象的引用,并不是对象本身。对象在内存中是一块内存地址,str 则是一个指向该内存地址的引用。

所以在刚刚我们说的这个例子中,第一次赋值的时候,创建了一个“hello”对象,str 引用指向“hello”对象的引用地址;第二次赋值的时候,又重新创建了一个对象“world”,str 引用指向了“world”,但“hello”对象依然存在于内存中。

也就是说 str 并不是对象,而只是一个对象引用。真正的对象依然还在内存中,没有被改变。

解析

java中的equals和==_第2张图片

 

String str1= "abc";
//相当于“abc”.intern();会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将
//在常量池中被创建。
String str2= new String("abc");
//在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,“abc"将会在常量池中创建;
//其次,在调用 new 时,JVM 命令将会调用 String 的构造函数new一个String 对象
//String 对象会引用常量池中的"abc”字符串的属性value[]。
//在堆内存中创建一个 String 对象;最后,str2 将引用 String 对象。
//这解释了为什么str1和str2的value的hashcode是一样的1229416514。
//因为char数组是复用的。
//因为char数组是复用的。
通过new对象的方式创建字符串对象str2,引用地址存放在堆内存中,str1则存放在字符串常量池中。
str1 == str2?显然是false
​
​
//会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将
//在常量池中被创建。因为str1已经将字符串常量放入常量池所以 str1==str3
String str3= str2.intern();
​
通过new对象的方式创建字符串对象str2,引用地址存放在堆内存中,abc则存放在字符串常量池中。
str3是放在字符串常量池中str2引用地址存放在堆内存中所以2者不相等
​

你可能感兴趣的:(java基础,java)