ToB企服应用市场:ToB评测及商务社交产业平台

标题: [Java]关于基本数据类型与引用类型赋值时的底层分析的小结(简述) [打印本页]

作者: 光之使者    时间: 2024-4-7 00:42
标题: [Java]关于基本数据类型与引用类型赋值时的底层分析的小结(简述)
【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/17969159
出自【进步*于辰的博客

目录

1、关于赋值

参考笔记一,P74.1。
一个小结:
所有引用都存于栈,而对象存于堆。引用所指向的可能存于栈,也可能存于方法区常量池。
1.1 基本数据类型赋值

在final int a中a是常量,在int a中a是变量。
示例:int a = 10;
a是变量,10是常量(a与10都存于栈)。令a = 20,是将a的值直接由10改为20。
1.2 String类型赋值

在String s = "abc"中,s是引用(也是变量,存于栈),"abc"是字符串常量,存于方法区的字符串常量池。令s = "123",是将s由指向"abc"转为指向"123",若"abc"没有其他引用指向就会被回收。
2、关于String赋值

参考笔记一,P74.2。
2.1 情形一

以下两种形式的定义的结果都相同,而底层分析略有不同。
形式一:
示例:
  1. String s1 = "csdn", s2 = "csdn";
  2. s2 = "2023";
  3. sout s1;// 打印:csdn
  4. sout s2;// 打印:2023
复制代码
若方法区的字符串常量池中存在字符串常量“csdn”,则不会重新创建,直接将s1和s2指向“csdn”。
令s2 = "2023",同样先判断是否存在"2023"。若不存在,则创建,然后将s2由指向"csdn"转为指向"2023"。
因此,最终s1指向"csdn",s2指向"2023"。
形式二:
示例:
  1. String s1 = "csdn", s2 = s1;
  2. s2 = "2023";
  3. sout s1;// 打印:csdn
  4. sout s2;// 打印:2023
复制代码
若存在“csdn”,则不会重新创建,直接将s1指向“csdn”,然后将s2指向s1的指向。
后续步骤同上,故结果相同。
这就是为何明明s2 = s1,而s2 = "2023",结果s2改变、s1不变的原因。
2.2 情形二

示例:
  1. String s1 = "csdn", s2 = s1 + "2023", s3 = "csdn2023";
  2. s2.equals(s3);// 结果:true
复制代码
令s2 = s1 + "2023",会先获取s1的指向,是"csdn"。
然后为"csdn"复制一个副本,再转为 StringBuffer,后执行append("2023"),最后调用toString(),返回“csdn2023”。
此时再判断字符串常量池中是否存在字符串常量"csdn2023",若不存在,则创建,然后将s2指向csdn2023”。
因此,s2与s3的结果相同。
3、关于String与char[]的比较

如果大家想要了解String类的底层,看这里 → String类。
参考笔记一,P74.3。
示例:
  1. String s1 = "csdn";
  2. char[] arr = {'c', 's', 'd', 'n'};
  3. s1.equals(arr);// 结果:false-------A
  4. s1.toString().equals(arr.toString());// 结果:false-------B
  5. // 注:第一个toString()多余,第二个toString()是在底层调用
  6. // 我这么写是为了方便大家理解
复制代码
String类重写了equals(),在底层会先调用toString(),返回值后再判断;而char[]不是具体类型,不存在重写。当然,仍会调用toString(),即示例中的A与B等效,但调用的是Object类的toString()(返回地址),故不等。
4、不同类型引用分析

参考笔记一,P36.5;笔记二,P38.1。
4.1 int

示例:
  1. int a = 100, b = a;
复制代码
a、b、100都存于栈,令a = 20,是直接将a的值改为20,b的值仍为100。
4.2 Integer

关于八位有符号二进制的表示范围,可查阅博文《关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述》的第1.2项。
Integer 类的“自动装箱”和“自动拆箱”机制是什么?
示例:
关于valueOf(),可参考Integer类的第4.33项。
  1. Integer i1 = 1;
  2. Integer i2 = new Integer(1);
  3. Integer i3 = Integer.valueOf(1);
  4. i1 == i2;// false
  5. i1 == i3;// true
  6. i2 == i3;// false
  7. Integer i4 = 128;
  8. Integer i5 = new Integer(128);
  9. Integer i6 = Integer.valueOf(128);
  10. i4 == i5;// false
  11. i4 == i6;// false
  12. i5 == i6;// false
复制代码
底层分析:
扩展一点:
  1. i1.equals(i2);// true
  2. i1.equals(i3);// true
  3. i2.equals(i3);// true
  4. i4.equals(i5);// true
  5. i4.equals(i6);// true
  6. i5.equals(i6);// true
复制代码
为何结果都为true?
关于equals(),可参考Integer类的第4.6项。
4.3 int[]

示例:
  1. int[] arr1 = {1, 2, 3}, arr2 = arr1;
复制代码
arr1、arr2以及数组内所有的值都存于栈,{1, 2, 3}存储于堆。
令arr1[0] = 10,是直接将1改为10,则arr1是{10, 2, 3};arr2与arr1指向相同,故arr2也是{10, 2, 3}。
4.4 Integer[]

示例:
  1. Integer[] arr1 = {1, 2, 3}, arr2 = arr1;
复制代码
与int[]不同的是,Integer[]内的所有元素都不是直接的值,而是引用,并且是 Integer 引用,指向整型常量池或堆。
不过,无论指向哪里,由于arr2与arr1始终指向相同,因此,令arr1[0] = 10,则arr1是{10, 2, 3},arr2同样是{10, 2, 3}。
5、最后

本文中的例子是为了方便大家理解java基本数据类型和引用类型赋值时的底层而简单举例的,不一定有实用性。
PS:
大家在看完这篇文章后肯定想吐槽:“你说了这么多,示例结果还不是与我们平时使用时的结果无差别,有什么意义。”
哈哈......结果当然相同了,因为我阐述的是底层分析。这些呢在平时工作中一般是用不到的,因此,目的是为了让大家对java基本数据类型和引用类型赋值时的底层有进一步的理解。
本文完结。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4