Java数据的基本(原始)类型和引用类型的特点差别

本文作为“Java数据类型”一文的补充https://blog.csdn.net/cnds123/article/details/110517272

Java的数据类型可以分为基本类型(primitive types)和引用类型(reference types)两大类。在实际编程中,要根据需求选择合适的数据类型,并注意数据类型的转换和运算规则。

基本类型包括八种:byte, short, int, long, float, double, char, boolean。

这些类型的数据直接存储在内存中,它们的值是实际的数据。【https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html】

数据类型

大小/位

可表示数据范围

默认值

byte(字节型)

8

-128~127

0

short(短整型)

16

-32768~32767

0

int(整型)

32

-2147483648~2147483647

0

long(长整型)

64

-9223372036854775808~9223372036854775807

0

float(单精度)

32

-3.4E38~3.4E38

0.0

double(双精度)

64

-1.7E308~1.7E308

0.0

char(字符)

16

0~255

'\u0000'

boolean(布尔)

-

true或false

false

引用类型则包括类(class)、接口(interface)、数组(array)等。Java的引用数据类型(Reference Types)包括以下几种:

类(Class):这是最基本的引用类型,例如用户自定义的类,以及Java库中的类,如String、Scanner、ArrayList等。

【说明:

String:是一类,用于创建和操作字符串

Scanner:这是Java的一个工具类,它用于获取用户的输入,或者从文件和字符串中读取数据。

ArrayList:这是Java的一个实现了List接口的类,是一个动态数组,可以自动增长和缩小。它可以存储任何类型的对象,包括null。】

接口(Interface):接口是一种引用类型,它是方法的集合。一个类可以实现(implement)一个或多个接口。

数组(Array):数组是一种引用类型,它可以存储固定数量的同一类型的值。

枚举(Enum):枚举是一种特殊的类,它有一组预定义的常量。

引用类型的变量存储的是一个地址,这个地址指向内存中的一个对象。这个对象可以是类的实例,也可以是数组。

Java基本类型(primitive types)和引用类型(reference types)的特点差别:

☆ 存储位置:基本类型的数据直接存储在栈内存中,存储的是实际的值。而引用类型的数据存储在堆内存中,变量实际上存储的是一个指向对象的地址或者引用,而不是对象本身。

计算机内存中的栈(Stack)【栈内存(Stack Memory)】和堆(Heap)【堆内存(Heap Memory)】主要是根据内存分配和管理方式来进行区分的。栈和堆的管理是由操作系统和编程语言的运行时(Runtime)共同控制的。它们各自有不同的用途和特点,栈内存是事先分配好的,遵循后进先出原则;而堆内存是动态分配的,可以根据程序需求进行调整。两者都是由操作系统和编程语言的运行时共同管理的。

变量赋值:对于基本类型,变量赋值是值的复制,而对于引用类型,变量赋值是引用的复制。

对于基本类型,变量赋值是直接将一个值赋给另一个变量。例如:

public class Main {public static void main(String[] args) {int a = 10;int b = a;System.out.println("Initial value of b: " + b);a = 20;System.out.println("Value of b after changing a: " + b);}
}

输出:

b的初值: 10
改变a后b的值: 10

解析如下:

在这个例子中,

int a = 10;

int b = a;

我们首先声明了一个变量a并赋值为10,然后我们声明了一个变量b并将a的值赋给b。这时,变量赋值是值的复制,b的值也是10。然后,如果我们改变a的值,例如:

a = 20;

这时,b的值仍然是10,因为b是在赋值时获取的a的值,而不是a本身。示意图示如下:

对于引用类型,变量赋值是将一个引用赋给另一个变量。StringBuilder是Java中的一个可变字符串类,它允许你在不创建新的字符串对象的情况下修改字符串内容。

例如:

public class Main {public static void main(String[] args) {StringBuilder a = new StringBuilder("Hello");StringBuilder b = a;System.out.println("b的初值:" + b);a.append(" world");System.out.println("改变a后b的值: " + b);a = new StringBuilder("Hi");System.out.println("改变a的引用后b的值:" + b);}
}

输出:

b的初值: Hello
改变a后b的值: Hello world
改变a的引用后b的值: Hello world

解析如下:

在这个例子中,

StringBuilder a = new StringBuilder("Hello");

StringBuilder b = a;

我们首先声明了一个StringBuilder对象a,然后我们声明了一个StringBuilder对象b并将a的引用赋给b。这时,a和b指向的是同一个对象。然后,如果我们通过a来修改这个对象的状态,例如:

a.append(" world");

这时,b的状态也会被改变,因为b和a指向的是同一个对象。

然而,如果我们改变a的引用,例如:

a = new StringBuilder("Hello world");

这时,b的状态并没有被改变,因为b和a现在指向的是两个不同的对象。示意图示如下:

需要注意的是,StringBuilder是可变的,所以它的内容可以被修改。与之相对的是String类,它是不可变的,一旦创建就不能被修改。

【StringBuilder和String都是Java中的字符串类,都是引用类型。

StringBuilder是可变的字符串类,它允许你在不创建新的字符串对象的情况下修改字符串内容。你可以通过调用StringBuilder的方法来追加、插入、删除和修改字符串内容。StringBuilder是一个可变的字符序列,它的长度和内容都可以被修改。

String是不可变的字符串类,一旦创建就不能被修改。当你对一个String对象进行修改时,实际上是创建了一个新的String对象。这是因为String类的设计是为了保证字符串的不可变性,这样可以提高字符串的安全性和性能。】

例子:

public class Main {public static void main(String[] args) {String a = "Hello World";String b = a;System.out.println("b的值: " + b);a = "Hi";System.out.println("a的值: " + a);System.out.println("b的值: " + b);}
}

输出:

b的值: Hello
a的值: Hi
b的值: Hello

在Java中字符串(String)是引用类型,为何改变了a的值b 没变?解析:

在Java中,字符串是不可变的。当你创建一个字符串对象时,它的值不能被修改。当你对字符串进行修改时,实际上是创建了一个新的字符串对象,而原始的字符串对象保持不变。

在你的代码中,当你将字符串"a"赋值给变量"b"时,实际上是将"b"指向了同一个字符串对象"Hello"。然后,当你将字符串"a"修改为"Hi"时,实际上是创建了一个新的字符串对象"Hi",并将变量"a"指向了这个新的字符串对象。但是,变量"b"仍然指向原始的字符串对象"Hello",所以它的值没有改变。

这是因为字符串在Java中被设计为不可变的,这样可以提高字符串的安全性和性能。示意图示如下:

参数传递:Java中的参数传递方式确实只有按值传递。无论是基本类型还是引用类型,都是将实际值或引用值复制一份传递给方法。

当说基本类型是按值传递时,意思是将实际的值复制一份传递给方法。如果方法中修改了这个复制的值,原始的值是不会被改变的。

当说引用类型是按值传递时,实际上是将引用的值(也就是对象在内存中的地址)复制一份传递给方法——形参实参指向同一个对象。这意味着方法中可以通过这个复制的引用来修改原始对象的状态,但是如果方法中改变了这个复制的引用(例如指向一个新的对象),原始的引用是不会被改变的。

让我们通过一些例子来理解Java中的参数传递方式。

首先,我们来看一个基本类型的例子:

//基本数据类型作为方法参数被调用
public class PassByValue {public static void main(String[] args){int msg = 100;System.out.println("调用方法前msg的值:"+ msg);    //100fun(msg);System.out.println("调用方法后msg的值:"+ msg);    //100}public static void fun(int temp){temp = 0;}
}

输出:

调用方法前msg的值:100
调用方法后msg的值:100

解释:基本数据类型变量,调用方法时作为参数是按数值传递的,temp方法接收的是mag的副本。temp 是 fun 方法的参数,它是一个基本类型的变量,存储在栈上,当方法结束时,栈帧被自动移除,相关的内存空间也就被释放了。

示意图如下:

接下来,我们来看一个引用类型的例子:

当在Java中传递引用类型时,传递的是引用的值,也就是对象在内存中的地址的副本。这意味着形参和实参指向的是同一个对象,所以在方法内部可以通过这个副本引用来修改原始对象的状态。但是,如果在方法内部改变了这个副本引用的指向,比如将其指向一个新的对象,那么原始的引用并不会改变。

//引用数据类型作为方法参数被调用
class Book{String name;double price;public Book(String name,double price){this.name = name;this.price = price;}public void getInfo(){System.out.println("图书名称:"+ name + ",价格:" + price);}public void setPrice(double price){this.price = price;}
}public class PassByReference{public static void main(String[] args){Book book = new Book("Java开发指南",66.6);book.getInfo();  //第一次getInfo(), 图书名称:Java开发指南,价格:66.6fun(book); //调用了fun()方法,设置新价格book.getInfo();  //第二次getInfo(),图书名称:Java开发指南,价格:99.9}public static void fun(Book temp){temp.setPrice(99.9); //设置新价格}
}

输出:

图书名称:Java开发指南,价格:66.6
图书名称:Java开发指南,价格:99.9

​​​​​​​​​​​​​​

解释:

段代码定义了一个Book类和一个PassByReference类。Book类有两个属性:name和price,分别表示书的名称和价格。Book类还有一个构造函数,用于创建对象时初始化这些属性,以及两个方法:getInfo()用于打印书的信息,setPrice(double price)用于设置书的价格。

PassByReference类包含main方法,这是Java程序的入口点。在main方法中,首先创建了一个Book对象book,初始化时书名为"Java开发指南",价格为66.6。然后调用book.getInfo()方法打印出书的信息。

接下来,main方法调用了fun(Book temp)方法,并将book对象作为参数传递给它。在fun方法内部,调用了temp.setPrice(99.9),这个方法调用实际上改变了传入的Book对象的price属性,将其设置为99.9。

由于Java中的对象引用是按值传递的,所以temp是book的一个副本,但它们都指向同一个Book对象。因此,当temp.setPrice(99.9)被调用时,它实际上改变了book对象的状态。

最后,当控制返回到main方法并再次调用book.getInfo()时,打印出的信息显示书的价格已经被改变为99.9。

temp 是 fun 方法的参数,它是一个引用类型的变量。当 fun 方法执行完毕,栈帧被移除,temp 变量的生命周期结束。在这个例子中,即使 temp 的生命周期结束了,Book 对象仍然通过 main 方法中的 book 变量被引用,因此它不会被垃圾回收。【只有当程序结束或者没有任何引用指向 Book 对象时,垃圾回收器才可能回收这个对象的内存。】

示意图如下:

无论变量是基本类型还是引用类型,作为参数传递给方法的值都会被复制以供被调用的方法使用。对于基本变量,变量的值被传递给方法。对于引用变量,它是一个引用。

为加深认识,下面再补充两个参数传递例子

一个基本类型的例子:

public class Test {public static void change(int value) {value = 55;}public static void main(String[] args) {int value = 22;System.out.println("Before: " + value);change(value);System.out.println("After: " + value);}
}

在这个例子中,我们在main方法中定义了一个变量value,并将其传递给change方法。在change方法中,我们试图修改value的值。然而,当我们运行这个程序时,会发现value的值并没有被改变。这是因为value是按值传递的,change方法接收的是value的一个副本,对这个副本的修改不会影响到原始的value。

一个引用类型的例子:

public class Test {public static void change(StringBuilder builder) {builder.append(" world");}public static void main(String[] args) {StringBuilder builder = new StringBuilder("Hello");System.out.println("Before: " + builder);change(builder);System.out.println("After: " + builder);}
}

在这个例子中,StringBuilder是可变的字符串类。我们在main方法中定义了一个StringBuilder对象,并将其传递给change方法。在change方法中,我们通过这个对象的引用来修改它的状态。当我们运行这个程序时,会发现builder的状态确实被改变了。这是因为builder是按值传递的,change方法接收的是builder的一个副本,这个副本和原始的builder指向的是同一个对象,所以通过这个副本可以修改原始对象的状态。

然而,如果我们试图在change方法中改变builder的引用,例如:

public static void change(StringBuilder builder) {
    builder = new StringBuilder("Hello world");
}

这时,当我们运行程序时,会发现builder的状态并没有被改变。这是因为change方法接收的是builder的一个副本,这个副本和原始的builder指向的是同一个对象,但是当我们在change方法中改变这个副本的引用时,原始的builder的引用并没有被改变,它们现在指向的是两个不同的对象。

总之,无论是基本类型还是引用类型,Java中的参数传递方式都是按值传递。但是由于基本类型和引用类型的特性不同,它们在方法参数传递时的行为看起来是不同的。

生命周期:基本类型的生命周期随着它所在的函数或者对象的生命周期,当函数返回或者对象被销毁时,基本类型的变量也会被销毁。而引用类型的对象,即使没有任何引用指向它,也不会立即被销毁,需要等待垃圾回收器的回收(时间点是不确定的,依赖于垃圾回收器的实现)。

基本类型(Primitive Types)

基本类型包括int、long、short、byte、float、double、boolean和char。这些类型的变量直接存储实际的值,并且通常位于栈(Stack)内存上。栈内存主要用于存储方法调用的上下文和局部变量。

基本类型的生命周期如下:

当基本类型的变量在方法中被声明时,它的生命周期开始。

变量的值存在于方法的栈帧中,这个栈帧对应于调用该方法的线程。

当方法执行完毕,栈帧被移除,所有在该栈帧中的局部基本类型变量也随之被销毁。

对于类的成员变量(字段)来说,基本类型的生命周期与其所属的对象相同。

基本类型的内存空间回收情况:

基本类型的内存空间不需要显式回收,因为它们存储在栈上,当方法结束时,栈帧被自动移除,相关的内存空间也就被释放了。

引用类型(Reference Types)

引用类型包括类(Class)、接口(Interface)、数组(Array)等。引用类型的变量存储的是对象的引用(地址),而对象本身存储在堆(Heap)内存上。

引用类型的生命周期如下:

当创建一个引用类型的对象时,它的生命周期开始。

对象存储在堆内存中,而对象的引用(变量)可以存储在栈上(作为局部变量)或者堆上(作为另一个对象的成员变量)。

如果对象没有任何引用指向它(即不可达),那么它就成为垃圾回收(Garbage Collection, GC)的候选对象。

引用类型的内存空间回收情况:

Java有一个垃圾回收机制来自动管理堆内存的回收。当对象不再被引用时,垃圾回收器可以决定回收这些对象的内存空间。

垃圾回收器的运行通常是不可预测的,它决定何时执行回收操作,这取决于多种因素,如可用内存、GC算法等。

开发者可以通过调用System.gc()来建议虚拟机执行垃圾回收,但是这个调用并不保证垃圾回收器立即执行。

总结来说,基本类型的内存管理是自动的,随着方法的结束而结束。而引用类型的内存管理则依赖于垃圾回收器。

​​​​​​​小结

这种分类的意义在于,基本类型和引用类型在内存管理、参数传递等方面有着不同的行为。基本类型的变量在栈内存中分配,而引用类型的对象在堆内存中分配。在参数传递时,基本类型是传值,也就是将实际的值传递过去;而引用类型是传地址,也就是将对象在内存中的地址传递过去。这种区别决定了它们在编程中的使用方式和效率。

附录

Java中的基本数据类型和引用数据类型的区别https://developer.aliyun.com/article/1123194

Java 到底是值传递还是引用传递?https://www.zhihu.com/question/31203609/answer/50992895

(Java)基本与引用数据类型(Primitive vs. Reference Data Types)https://blog.csdn.net/cnds123/article/details/134266737

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/185792.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5 ip的分配

如上一节所述,需要和其他设备通信,那么需要先配置ip. 1、如何配置ip 1.可以使用 ifconfig,也可以使用 ip addr 2.设置好了以后,用这两个命令,将网卡 up 一下,就可以了 //---------------------------- 使…

简述扫码登录原理及测试要点

扫码登录本质是解决将APP端的用户登录信息(通常是Token)通过扫码的形式安全稳定地同步给Web端。 操作流程: 打开登录页面,展示一个二维码(web);打开APP扫描该二维码后,APP显示确认、取消按钮(app)&#xf…

Flink之状态管理

Flink状态管理 状态概述状态分类 键控、按键分区状态概述值状态 ValueState列表状态 ListStateMap状态 MapState归约状态 ReducingState聚合状态 Aggregating State 算子状态概述列表状态 ListState联合列表状态 UnionListState广播状态 Broadcast State 状态有效期 (TTL)概述S…

pytorch(小土堆)深度学习

第五节课讲项目的创建和对比 第六节:Dataset,Dataloader Dataset提供一种方式区获取数据及其label(如何获取每一个数据及其label,告诉我们总共有多少的数据) Dataloader为后面的网络提供不同的数据形式 第七节:Dataset类代码实战 显示图片 f…

WebSocket在node端和客户端的使用

摘要 如果想要实现一个聊天的功能,就会想到使用WebSocket来搭建。那如果没有WebSocet的时候,我们会以什么样的思路来实现聊天功能呢? 假如有一个A页面 和 B页面进行通信,当A发送信息后,我们可以将信息存储在文件或者…

安防监控EasyCVR视频汇聚平台无法接入Ehome5.0是什么原因?该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放…

2023.11.09 homework (2)

【七年级上数学】 教别人也是教自己,总结下: 13)找规律的题目,累加题目,要整体看,不然不容易算出来,求最大值,那么就是【最大值集群和】减去【最小集群和】就是最大值 9-12&#x…

模态对话框和非模态对话框

创建到堆区这样非模态对话框就不会一闪而过 .exec使程序进入阻塞状态 ()[]{}lambda表达式 55号属性可以在对话框关闭的时候将堆区的内存释放掉从而防止内存泄露

在linux安装单机版hadoop-3.3.6

一、下载hadoop https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/core/hadoop-3.3.6/ 二、配置环境变量 1、配置java环境变量 2、配置hadoop环境变量 export HADOOP_HOME/usr/local/bigdata/hadoop-3.3.6 export HBASE_HOME/usr/local/bigdata/hbase-2.5.6 export JA…

手术训练系统项目

★ 手术训练系统项目 项目描述:手术训练系统,它提供了多项功能,包括账户登录与创建、数据库与账户管理、课程管理、小组管理、成绩统计、证书发布、训练和系统设置。 职责描述: 1、训练功能开发(任务概述、任务指导、评分规则、评…

71 内网安全-域横向网络传输应用层隧道技术

目录 必备知识点:1.代理和隧道技术区别?2.隧道技术为了解决什么?3.隧道技术前期的必备条件? 演示案例:网络传输应用层检测连通性-检测网络层ICMP隧道Ptunnel使用-检测利用传输层转发隧道Portmap使用-检测,利用传输层转发隧道Netcat使用-检测,利用,功能应用层DNS隧…

Jmeter分布式性能测试细节+常见问题解决,资深老鸟带你避坑...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 Jmeter分布式测试…

【TiDB】TiDB CLuster部署

目录 0 大纲 一 集群部署工具TiUP简介 1 TiUP 简介 2 TiUP使用 3 TiUP使用举例 二 TiDB Cluster安装配置需求 1 生产环境硬件需求 2 操作系统需求 三 TIDB部署 1 软硬件需求以及前置检查​编辑 2 安装TiUP 组件 ​3 集群拓扑文件 4 执行部署命令 (1&…

6-爬虫-scrapy解析数据(使用css选择器解析数据、xpath 解析数据)、 配置文件

1 scrapy解析数据 1.1 使用css选择器解析数据 1.2 xpath 解析数据 2 配置文件 3 整站爬取博客–》爬取详情–》数据传递 scrapy 爬虫框架补充 # 1 打码平台---》破解验证码-数字字母:ddddocr-计算题,滑块,成语。。。-云打码,超…

HK WEB3 MONTH Polkadot Hong Kong 火热报名中!

HK Web3 Month 11月除了香港金融科技周外,HK Web3 Month又是一大盛事,从10月29日开始开幕直到11月18日结束。此次将齐聚世界各地的Web3产业从业者、开发者、社群成员和学生来参与本次盛会。除外,超过75位产业知名的讲者与超过50场工作坊将为…

大数据毕业设计选题推荐-农作物观测站综合监控平台-Hadoop-Spark-Hive

✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

linux基础:3.linux基础环境开发工具和配置。

linux基础环境开发工具和配置 一.学习yum工具进行软件安装:1.什么是yum:2.查看软件包:3.安装和删除:4.yum生态: 二.vim的使用:一.快速介绍一下vim二.vim正常模式:2-1:命令模式1.光标…

strtok函数详解:字符串【分割】的利器

目录 一,strtok函数简介 二,strtok函数的用法 三,strtok函数的注意事项 一,strtok函数简介 strtok函数可以帮助我们将一个字符串按照指定的分隔符进行分割,从而得到我们想要的子字符串。 🍂函数头文件&am…

【Qt绘制小猪】以建造者模式绘制小猪

效果 学以致用&#xff0c;使用设计模式之建造者模式绘制小猪。 代码 接口&#xff1a;申明绘制的步骤 PigBuilder.h #ifndef PIGBUILDER_H #define PIGBUILDER_H#include <QObject> #include <QPainter>class PigBuilder : public QObject {Q_OBJECT public:ex…

记录C# WinForm项目调用Rust生成的dll库

一、开发环境 1.RustRover (version&#xff1a;2023.3 EAP) 2.Visual Studio 2019 (version&#xff1a;16.11.30) 3.Windows 10 64位 OS 4.WinR&#xff1a;控制台程序&#xff0c;cmd.exe 二、使用RustRover编译Rust脚本为dll 1.下载安装Rust&#xff0c;https://www.…