首页 > 编程语言 >[Java]关于基本数据类型与引用类型赋值时的底层分析的小结(简述)

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

时间:2024-01-17 10:13:24浏览次数:33  
标签:Java 常量 指向 s2 s1 数据类型 csdn Integer 小结

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
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 情形一

以下两种形式的定义的结果都相同,而底层分析略有不同。

形式一:
示例:

String s1 = "csdn", s2 = "csdn";
s2 = "2023";
sout s1;// 打印:csdn
sout s2;// 打印:2023

若方法区的字符串常量池中存在字符串常量“csdn”,则不会重新创建,直接将s1s2指向“csdn”
s2 = "2023",同样先判断是否存在"2023"。若不存在,则创建,然后将s2由指向"csdn"转为指向"2023"
因此,最终s1指向"csdn"s2指向"2023"

形式二:
示例:

String s1 = "csdn", s2 = s1;
s2 = "2023";
sout s1;// 打印:csdn
sout s2;// 打印:2023

若存在“csdn”,则不会重新创建,直接将s1指向“csdn”,然后将s2指向s1的指向。
后续步骤同上,故结果相同。

这就是为何明明s2 = s1,而s2 = "2023",结果s2改变、s1不变的原因。

2.2 情形二

示例:

String s1 = "csdn", s2 = s1 + "2023", s3 = "csdn2023";
s2.equals(s3);// 结果:true

s2 = s1 + "2023",会先获取s1的指向,是"csdn"
然后为"csdn"复制一个副本,再转为 StringBuffer,后执行append("2023"),最后调用toString(),返回“csdn2023”
此时再判断字符串常量池中是否存在字符串常量"csdn2023",若不存在,则创建,然后将s2指向csdn2023”
因此,s2s3的结果相同。

3、关于String与char[]的比较

如果大家想要了解String类的底层,看这里 → String类。
参考笔记一,P74.3。

示例:

String s1 = "csdn";
char[] arr = {'c', 's', 'd', 'n'};
s1.equals(arr);// 结果:false-------A
s1.toString().equals(arr.toString());// 结果:false-------B
// 注:第一个toString()多余,第二个toString()是在底层调用
// 我这么写是为了方便大家理解

String类重写了equals(),在底层会先调用toString(),返回值后再判断;而char[]不是具体类型,不存在重写。当然,仍会调用toString(),即示例中的A与B等效,但调用的是Object类的toString()(返回地址),故不等。

4、不同类型引用分析

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

4.1 int

示例:

int a = 100, b = a;

a、b、100都存于栈,令a = 20,是直接将a的值改为20,b的值仍为100。

4.2 Integer

关于八位有符号二进制的表示范围,可查阅博文《关于二进制的原码、补码和反码,以及表示范围、常见位运算符和进制转换的理解与简述》的第1.2项。

Integer 类的“自动装箱”和“自动拆箱”机制是什么?

  1. 包装类都具有自动装箱和自动拆箱的功能(以代码的角度上说,就是会在底层调用某个方法)。
  2. 为包装类Integer赋值时,自动装箱是底层调用了valueOf()。这里存在一个溢出问题。因为整型常量存储于方法区的整型常量池,而整型常量池使用8位有符号二进制表示整型。8位有符号二进制的表示范围是-128 ~ 127。若整型超出此此范围,就是“溢出”。
  3. “溢出”规定:若整型在范围内,valueOf()底层创建整型常量,存储于整型常量池;否则创建 Integer 对象,存储于堆。

示例:

关于valueOf(),可参考Integer类的第4.33项。

Integer i1 = 1;
Integer i2 = new Integer(1);
Integer i3 = Integer.valueOf(1);
i1 == i2;// false
i1 == i3;// true
i2 == i3;// false

Integer i4 = 128;
Integer i5 = new Integer(128);
Integer i6 = Integer.valueOf(128);
i4 == i5;// false
i4 == i6;// false
i5 == i6;// false

底层分析:

  • i1指向整型常量1i2指向new Integer(1);由于1在表示范围内,故valueOf(1)返回整型常量1,则i3也指向整型常量1
  • 由于整型常量池仅能存储-128 ~ 127的整型常量,故i4指向new Integer(128)i5指向new Integer(128);由于128超出表示范围,故valueOf(128)返回new Integer(128)

扩展一点:

i1.equals(i2);// true
i1.equals(i3);// true
i2.equals(i3);// true

i4.equals(i5);// true
i4.equals(i6);// true
i5.equals(i6);// true

为何结果都为true

关于equals(),可参考Integer类的第4.6项。

4.3 int[]

示例:

int[] arr1 = {1, 2, 3}, arr2 = arr1;

arr1arr2以及数组内所有的值都存于栈,{1, 2, 3}存储于堆。
arr1[0] = 10,是直接将1改为10,则arr1是{10, 2, 3};arr2与arr1指向相同,故arr2也是{10, 2, 3}

4.4 Integer[]

示例:

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基本数据类型和引用类型赋值时的底层有进一步的理解。

本文完结。

标签:Java,常量,指向,s2,s1,数据类型,csdn,Integer,小结
From: https://www.cnblogs.com/cnb-yuchen/p/17969159

相关文章

  • 【深入挖掘Java技术】「源码原理体系」盲点问题解析之HashMap工作原理全揭秘(上)
    知识盲点概念介绍HashMap是基于Map接口构建的数据结构,它以键值对的形式存储元素,允许键和值都为null。由于键的唯一性,HashMap中只能有一个键为null。HashMap的特点是元素的无序性和不重复性。注意,HashMap并不是线程安全的。在多线程环境下,如果不进行适当的同步处理,可能会导致数据不......
  • 开发日记3(java面向对象)
    (一)java面向对象1、面向过程和面向对象很久很久以前,系统学习过C语言,但也只是学过,没有产生过除了考试以外的其他价值。后来工作中很多伙伴都在使用java,虽然当时自己不写java,但很久之前就有个概念,这两种语言还是不同的,一个面向过程、一个面向对象。面向过程比较容易理解,基实过程可以理......
  • Java面向对象
    Java面向对象面向对象编程(Object-OrientedProgramming,OOP)面向对象编程本质:以类的方式组织代码,以对象的组织(封装)数据三大特性:1、封装所谓的封装就是把类的属性和方法使用private修饰,不允许类的调用者直接访问,如果想要操作这些属性使用public的get、set方法。封装的作用:不......
  • Java就业学习 Day2 每日一问:我真能找到工作吗/(ㄒoㄒ)/~~
    Java开发能力:今天只看了面向对象这一节的东西,确实有了好多新的理解。①和equals的区别:说实话我现在才弄懂。。比较的其实是地址,所以如果一个变量可以用==,但对象就不行。②重载与重写的区别:重载是构造器重载,构造器的参数不同。而重写是重写方法,形象来说就是父类的方法我不满意,我......
  • Java数组
    Java数组数组一、数组的声明和创建1、声明dataType[]arrayRefVar;//声明数组2、创建dataType[]arrayRefVar=newdataType[dataSize];//创建一个数组二、初始化及内存分析1、初始化//静态初始化:创建加赋值int[]a={1,2,3};Peo......
  • 编译openwrt分支immortalwrt小结
    编译环境:ubuntu20.04LTS,确保能连接github获取必须依赖:sudoaptupdate-ysudoaptfull-upgrade-ysudoaptinstall-yackantlr3asciidocautoconfautomakeautopointbinutilsbisonbuild-essential\bzip2ccacheclangcmakecpiocurldevice-tree-compilere......
  • 不可不看的Java基础知识整理,注释、关键字、运算符
    写在开头万丈高楼平地起,要想学好汉语首先学拼音,想学好英语首先学26个字母,对于编程语言来说,一样的道理,要想学好必须先掌握其基础语法和知识,今天我们就来唠一唠Java语言中那些出现频率极高,又很基础的知识点吧!Java中的注释注释的作用:拨云见日!在日常的工作中,总会遇到很多大段的代......
  • Java动态代理、AOP和装饰器模式
    面向切面编程AOP-AspectOrientedPrograming,主要用于处理核心业务逻辑外的一些东西,比如日志和缓存。这个“切面”可以理解为在代码的某个地方切一刀,在其中加一些东西。装饰器以日志为例,如果没有使用AOP,那么可以使用装饰来实现类似的代码。我们使用装饰器模式来实现一下在执行......
  • java 依赖 stable diffusion
    Java依赖的稳定扩散简介Java的依赖稳定扩散是指将一个Java项目的依赖库从一个环境迁移到另一个环境,确保依赖库在新环境中正常工作。本文将介绍实现这一过程的步骤和相应的代码。流程概览以下是实现Java依赖稳定扩散的步骤概览:步骤描述1.创建空白的Java项目......
  • java实现文心一言流式输出
    Java实现文心一言流式输出教程简介在本教程中,我将教授你如何使用Java实现文心一言流式输出。文心一言是一种短小精悍的句子,适合用于展示在网站的底部或侧边栏等位置。通过流式输出,可以实现每隔一段时间自动更新展示的文心一言。整体流程下面是实现文心一言流式输出的整体流程,可......