上篇文章谈了equals与==的区别,其实对于引用类型,如果equals没有重写,二者比较的都是内存地址,重写了就变成比较内容的了,也没什么好说的。那其实‘==’在包装类和String里那真是变化莫测啊,必须得详细谈一下的。
1.Integer与int
直接上代码分析最清楚了。参考此文:https://www.cnblogs.com/liuling/archive/2013/05/05/intAndInteger.html
public class TestInteger {
public static void main(String[] args) {
int i = 128;
Integer i2 = 128;
Integer i3 = new Integer(128);
// Integer会自动拆箱为int,所以为true
System.out.println(i == i2);
System.out.println(i == i3);
Integer i5 = 127;// java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
Integer i6 = 127;
System.out.println(i5 == i6);// true
/*
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i5 == i6);//false
*/
Integer ii5 = new Integer(127);
System.out.println(i5 == ii5); // false
Integer i7 = new Integer(128);
Integer i8 = new Integer(123);
System.out.println(i7 == i8); // false
}
}
首先,Integer和int比都会自动拆箱,所以第一个和第二个输出都是true。
我们先来分析第五个输出吧,Integer i5 = 127,指向专门存放它的内存(常量池);Integer ii5 = new Integer(127),指向堆。内存地址不一样,肯定是false的。
再分析第六个输出,无论值相等不,new出来的对象是不一样的,所以指向不一样,所以输出为false。
最后,第三个输出为true,而第四个注释的为false,这儿很多人都不懂。其实java在编译Integer i5 = 127的时候,被翻译成-> Integer i5 = Integer.valueOf(127);所以关键就是看valueOf()函数了。只要看看valueOf()函数的源码就会明白了。JDK源码的valueOf函数式这样的:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
如果以(Integer i = ~ )这样去初始化时,对于-128到127之间的数,会进行缓存,也就是放到常量池中。Integer i5 = 127时,会将127进行缓存,当Integer i6 = 127时,就会直接从缓存中取。所以i5,i6指向的都是常量池的同一个对象。
但是,一旦超过了127是128呢,相当于new Integer了,注释的i5,i6相当于在堆里new了俩对象啦,肯定地址不一样的啊。
那么关于Integer的总结如下:
①int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比。
②Integer和new Integer不可能相等的。在-128到127之间的数,前者指向池子,后者指向堆;在这区间之外的数,前者相当于new Integer,new出来的对象指向都会不一样。
③两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
④两个都是new出来的,都为false
2、String
除了没有自动拆箱机制,觉得跟Integer多多少少还是差不多的。还是贴代码分析吧。
private static void testStringPool() {
String a = "abc";//字面量形式
String b = "abc";//字面量形式
String c = new String("abc");//使用new标准的构造对象
String d = "ab" + "c";//字面量形式
System.out.println("a == b " + (a == b));//true
System.out.println("a == c " + (a == c));//false
System.out.println("a == d " + (a == d));//true
System.out.println("b == c " + (b == c));//false
System.out.println("b == d " + (b == d));//true
System.out.println("c == d " + (c == d));//false
System.out.println("-----------------");
System.out.println("abc" == ("ab" + "c"));//true
System.out.println("-----------------");
String e = c.intern();//将new出来的字符串对象加入字符串常量池
System.out.println(a == e);//true
}
java创建字符串对象有两种方式:字面量形式(常量池形式)、标准的new方法
一般来说,我们采取字面量形式创建比较多,原因是JVM为了减少字符串对象的重复创建,维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池。
工作原理: https://blog.csdn.net/qq_36381855/article/details/79686812
当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查。
如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回。
否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
那上面的结果就好解释了,a b d的指向都是指向常量池的"abc",故相互比较都是true。然后c是new出来的,它指向的是堆里的地址,所以跟字面量形式创建的对象比都是false。但是最后手动把new出来的字符串对象加入字符串常量池,返回该引用,跟a指向比较,又是true了。
盗张图:
ff是字符串直接拼接出来的,还是相当于指向常量池中的“hello2”,所以与aa比较为true;而ee是经过了dd中间变量创建的对象,dd+2 通过StringBuilder的最后一步toString()方法还原一个新的String对象”hello2”,因此堆中开辟一块空间存放此对象,那么引用ee指向堆中(dd+2)所还原的新String对象,aa指向的是常量池中的"hello2",显然结果是false。那么aa与cc比较咋又是true了呢?
这就是final的作用了,由于变量bb被final修饰,因此会被当做编译器常量,所以在使用到bb的地方会直接将变量bb替换为它的值。这是不是跟C语言中的宏定义有异曲同工之妙呢?
最后,这个链接的东东也可以看看:
https://blog.csdn.net/qq_27657429/article/details/77882191