⽤String类和Integer等基本数据类型包装类进⾏实例化对象时
的⼯作原理
1,关于传递:8种基本数据类型(byte,short,int,long,float,double,char,boolean)进⾏的是值传递;Objiect类的所有⼦类传递时,传递的是值对应的地址。实例化类和实例化对象
2,基本数据类型
<span >  </span>int a = 10;
int b = 10;
System.out.println(a == b);//true
此处,是基本数据类型,⽐较的是a,b的值,都是10,所以结果是true
(另外此处10存进来之后,系统会根据数据的⼤⼩⾃动把它转化为byte类型(如果是200则⾃动转为short型),⽽不是int,int只是规定了这个变量a的最⼤取值范围,这点可以通过javap反编译class字节码⽂件进⾏查看验证)
3,Integer类型
Integer a = 50;
Integer b = 50;
Integer c = 200;
Integer d = 200;
System.out.println(a == b);//true
System.out.println(c == d);//false
此处,是Object类,所以⽐较的是内存地址,为什么⼀个是true,⼀个是false?我们可以看下具体的运⾏步骤:
a1,⾸先创建⼀个Integer类型的a对象
a2,通过Integer.valueof(50)⽅法给a传值,此时要经过⼀个判断,看50是否在-128~127的范围之内,如果在,系统会⾃动return⼀个缓存(Integer⾃动创建好的-128~127数字的堆内存空间),同时把相
应的地址头放在栈内存空间⾥⾯,让a指向它。
a3,此时a的值是50,对应的地址是栈内存空间的地址头,此地址头跟50所在的堆内存空间的地址对应。
b1,然后创建⼀个Integer类型的b对象
b2,通过Integer.valueof(50)⽅法给b传值,此时同样要经过⼀个判断,看50是否在-128~127的范围之内,如果在,系统会⾃动return ⼀个缓存(Integer⾃动创建好的-128~127数字的堆内存空间--此空间跟给a的那个空间是同⼀个空间),同时把相应的地址头放在栈内存空间⾥⾯,让b指向它。
b3,此时b的值是50,对应的地址是栈内存空间的地址头,此地址头跟50所在的堆内存空间的地址对应,即a和b对应的是同⼀块堆内存空间,故⼆者⽐较地址时返回的是true。
c1,然后创建Integer类型的c对象
c2,通过Integer.valueof(200)⽅法给c传值,此时同样要经过⼀个判断,看200是否在-128~127的范围之内,如果不在,系统会通过new Integer()的⽅式,在堆内存空间⾥新建⼀块空间,并通过Integer.valueof(200)⽅法给这个空间传值为200,同时把相应的地址头放在栈内存空间⾥⾯,让c指向它。
c3,此时c的值是200,对应的地址是栈内存空间的地址头,此地址头跟200所在的堆内存空间的地址对应。
d1,然后创建Integer类型的d对象
d2,通过Integer.valueof(200)⽅法给d传值,此时同样要经过⼀个判断,看200是否在-128~127的范围之内,如果不在,系统会通过new Integer()的⽅式,在堆内存空间⾥新建⼀块空间(注意,此处系统并没有检测已有的空间,⽽是⽆条件的另外开辟了⼀个空间),并通过Integer.valueof(200)⽅法给这个空间传值为200,同时把相应的地址头放在栈内存空间⾥⾯,让d指向它。
d3,此时d的值是200,对应的地址是栈内存空间的地址头,此地址头跟另外⼀个200所在的堆内存空间的地址对应。
以上就是原因所在,另外其他基本数据类型的类如Double等与此相同。
4,String类
String类是final类型的,属于常量范畴,对于final类型的数据,系统会在堆内存空间⾥另外开辟出⼀块叫final pool的位置来存储,但对象并不指向该pool,⽽是在堆内存空间⾥另外开辟⼀个内存空间,⼀⽅⾯引⽤final pool⾥的值,另⼀⽅⾯对应的对象指向它。举例解释如下:
String a = "abc";
String b = "abc";
String c = new String("abc");
String d = new String("abc");
String e = "a";
String f = "bc";
String g = e + f;
String h = e + "bc";
<span >  </span>String i = "a" + "bc";
System.out.println(a == b);//true
System.out.println(c == d);//false
System.out.println(a == c);//false
System.out.println(a == g);//false
System.out.println(a == h);//false
<span >  </span>System.out.println(a == i);//true
此处是Object类,同样⽐较的是内存地址,即对象在对应栈内存⾥存储的地址头。
a1,⾸先,创建⼀个String对象a,将“abc”放进常量池final pool。
a2,在堆内存⾥开辟出⼀块内存空间。
a3,该空间引⽤常量池中的“abc”作为值存储起来,建⽴引⽤关系,并将该空间的地址头(内存地址开头的部分,⼀般是⼗六进制码)给a,存在栈内存空间与a对应
b1,⾸先,创建⼀个String对象b,将“abc”放进常量池final pool,因为常量池是个集合,具有斥同性,故只允许⼀个”abc“存在,所以系统会先扫描,发现已存在⼀个”abc“(执⾏a步骤时放⼊的,由于是被引⽤的,不会被jvm清除掉----结合上⼀步,由此我们也可以推出⼀个结论”常量池⾥的数据必定是被引⽤的“)。
b2,由于已到常量池中的”abc“,并顺藤摸⽠可以到引⽤此”abc“的那部分堆内存空间(即a指向的那个内存空间)。
b3,将该空间的地址头给b,并让b也指向它,所以a,b的地址是相同的,指向的是同⼀块堆内存空间,所以结果是true。
c1,⾸先,创建⼀个String对象c,将“abc”放进常量池final pool,发现已存在。
c2,执⾏a = new String()步骤,在堆内存⾥开辟出⼀块内存空间,将”abc“作为参数通过⽅法String.valueof("abc")传到该内存空间,但因为⽅法执⾏时会产⽣对应的栈帧,栈帧是临时的,⽅法执⾏结束后,JVM会⽴即将⽅法对应的那部分栈帧所在的栈空间清除掉,此时常量池中的”abc“与堆内存的这部分之间的关系中断,即⽆法通过常量池引⽤到该部分堆内存空间,(通过常量池引⽤只能到a和b 对应的那块空间,所以可以说,常量池⾥的数据必定是被引⽤的,且对应唯⼀的堆内存空间)。
c3,该空间是通过⽅法新建的空间并且与常量池中的”abc“再⽆联系,所以是块独⽴的空间,与a,b所在的空间不同,所以结果是false。
d,d的执⾏步骤跟c相同,也是通过⽅法在堆内存中新开辟了⼀块内存空间,所以c与d的地址也不相同,故结果也是false;
efg,执⾏完e,f这两个步骤后,常量池中同时具有”a“,”bc“,”abc“这三个常量,并分别对应在堆内存空间有相应的内存位置,并分别被对象e,f,a\b指向。
g2,执⾏e + f,将e,f的值⾮别拿出,运算合成”abc“,同时在堆内存空间new出来⼀个空间和g对应,将此结果直接通过⽅法
String.valueof("abc")直接传给此空间,结束后⽅法和参数被清除,则此空间独⽴存在。所以a与g⽐较结果也是false。
h1,h的执⾏步骤与g类似,是将e的值”a“拿出来,与常量池中的”bc“运算合成”abc“,同时在堆内存空间new出来⼀个空间和h对应,将此结果直接通过⽅法String.valueof("abc")直接传给此空间,结束后⽅法和参数被清除,则此空间独⽴存在。所以a与h⽐较结果也是false。
i1:此步骤稍有不同。在系统编译时(即从.java编译成.class⽂件的过程中)会⾃动将”a“和”bc“合成”abc“,在程序运⾏时的步骤跟a和b⼀模⼀样,所以结果是true。