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。