一.“==”的作用
在java中,“==”的作用:比较内存地址是否相同
- 对于引用数据类型:直接比较两个引用对象引用的堆内存地址是否相同;如:比较两个User对象,比较的是两个User对象在堆内存中执行的是否是同一个地址。
- 对于基本数据类型:比较基本数据类型的“值”是否相等;如:比较两个int类型的变量,比较的是两个变量的“值”是否相等。注意:对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的,对于"123" 这样的字符串也是相同的道理,在常量池中,一个常量只会对应一个地址,因此不管是再多的123或者"123" 这样的数据都只会存储一个地址,所以所有他们的引用都是指向的同一块地址,因此基本数据类型和String常量也是可以直接通过==来直接比较的。常常有人会说对于基本数据类型,"=="比较的是“值”相等,其实也是由于他们的内存地址相等。
- 对于基本数据类型的包装类型:(Byte, Short, Character,Integer,Float, Double,Long, Boolean)除了Float和Double之外,其他的六种都是实现了常量池的,因此对于这些数据类型而言,一般我们也可以直接通过==来判断是否相等。
int a = 128; int b = 128; System.out.println(a == b); //结果为:true Integer c = 1; Integer d = 1; System.out.println(c == d); //结果为:true Integer c11 = new Integer(127); Integer d11 = new Integer(127); System.out.println(c11 == d11); //结果为:false Integer c1 = 127; Integer d1 = 127; System.out.println(c1 == d1); //结果为:true Integer c2 = 128; Integer d2 = 128; System.out.println(c2 == d2); //结果为:false,为什么呢? 因为 Integer 在常量池中的存储范围为[-128,127],127在这范围内,因此是直接存储于常量池的,而128不在这范围内, 所以会在堆内存中创建一个新的对象来保存这个值,所以c2,d2分别指向了两个不同的堆内存地址,故而导致了不相等。
二.equals的作用
equals方法源于Object类,源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,在Object类中equals方法也是直接通过==来比较的,和==是没有任何区别的。
那么为什么又要说equlas和==的区别呢?是因为equals方法是可以由我们自己重写的,例如:java.lang.String 类,重写equals方法。
三.重写equals方法
在java.lang.String类中重写了equals方法,源码如下:
public boolean equals(Object anObject) {
if (this == anObject) { //使用==,判断内存地址是否相等
return 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中的equals方法比较的是字符串的内容是否一样。也就是说如果像String、Date这些重写equals的类,使用其equals方法的时候会和Object的不一样,比较的内容也不尽相同。
//测试 String str1 = "123"; String str2 = new String("123"); String str3 = str2; System.out.println(str1 == str2);//false System.out.println(str1 == str3);//false System.out.println(str2 == str3);//true System.out.println(str1.equals(str2));//true System.out.println(str1.equals(str3));//true System.out.println(str2.equals(str3));//true
内存解释:
new String("abc"),在对象创建字符串,内存地址不一样;
使用str1.intern()方法,编译器会将“abc”字符串添加到常量池中,并返回指向该常量的引用;如果此后再调用str2.intern(),因为常量池中已经存在了“abc”,因此直接返回常量池中“abc”的引用。
通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。
常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用。
对于final字段,编译期直接进行了常量替换(而对于非final字段则是在运行期进行赋值处理的)。
final String str1=”ja”;
final String str2=”va”;
String str3=str1+str2;
在编译时,直接替换成了String str3=”ja”+”va”.常量字符串和变量拼接时(如:String str3=baseStr + “01”;)会调用stringBuilder.append()在堆上创建新的对象。
String 的 intern() 方法返回一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。