用String类和Integer等基本数据类型包装类进行实例化对象时的工作原理

1,关于传递:8种基本数据类型(byte,short,int,long,float,double,char,boolean)进行的是值传递;Objiect类的所有子类传递时,传递的是值对应的地址。

2,基本数据类型

<span style="white-space:pre">		</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 style="white-space:pre">		</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 style="white-space:pre">		</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。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值