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

news2024/11/25 0:25:33

本文作为“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);    //100
       fun(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.6
       fun(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.coloradmin.cn/o/1189896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React路由与导航

目录 前言: 什么是React路由? 导航和页面切换 路由参数和动态路由 路由守卫和权限控制 总结 前言: React是一个流行的JavaScript库,用于构建用户界面。在使用React开发Web应用程序时,路由和导航是必不可少的功能…

理解MySQL的日志 Redo、Undo

理解MySQL的Redo日志和Undo日志 1、MySQL 日志文件解决的问题2、redo 日志2.1、redo log 的组成2.2、redo log 刷盘策略2.3、MySQL 的 redo log解决了哪些问题 3、undo 日志3.1、undo 日志作用3.2、undo log 的类型3.3、undo log 的生命周期3.4、事务回滚相关的几个隐藏字段 1、…

【Mysql】where 条件子句之逻辑运算符

逻辑运算符 and &&or ||not ! student表 一.查询分数在80 - 90之间 and写法 &&写法 区间(between ....and......) 二.查询分数不为88 !写法 not写法 三.查询分数大于88或者年龄小于22 满足其中一个条件即可 or写法 ||写法

操作系统 day08(进程通信)

进程通信的概念 进程间通信是指两个进程之间产生数据交互进程通信需要操作系统的支持,由于进程是分配系统资源(包括内存地址)的单位,因此各进程拥有的内存地址空间相互独立。同时为了保证安全,一个进程不能直接访问另…

django安装和rest接口写法

django安装 确保已经安装了Python。命令行中输入python --version来检查Python的版本。 安装Django。你可以在命令行中使用以下命令来安装Django: pip install django创建一个新的Django项目。在命令行中,进入你想要创建项目的目录,并运行以…

SpringCloud-Gateway无法使用Feign服务(2021.X版本)

Spring Cloud Gateway 2021.x版本,无法使用Feign调用其他服务接口。 问题原因: 在官网的 issue 里面找到了相关的问题。 How to call another micro-service on GatewayFilterFactory ? Issue #1090 spring-cloud/spring-cloud-gateway GitHubHel…

python编程复习系列——week2(Input Output (2))

文章目录 一、多行代码语句二、Escape序列三、字符串格式四、数值运算课后作业 一、多行代码语句 🥞使用反斜杠\来表示在下一行中继续使用一条语句。 subject_code "CSCI111" subject_mark 80 subject_grade "D" result "Subject re…

SOLIDWORKS --电磁仿真篇

什么是 SIMULIA? 基于3DEXPERIENCE平台的品牌 多学科多领域的协同仿真与分析优化 三大核心仿真领域 结构仿真 流体仿真 SIMULIA电磁仿真是什么? 完备的求解技术,支持从静场、低频到高频、光波的电磁仿真,支持全波仿真、混合仿真、多物理场仿真和场路…

支持C#的开源免费、新手友好的数据结构与算法入门教程 - Hello算法

前言 前段时间完成了C#经典十大排序算法(完结)然后有很多小伙伴问想要系统化的学习数据结构和算法,不知道该怎么入门,有无好的教程推荐的。今天给大家推荐一个支持C#的开源免费、新手友好的数据结构与算法入门教程:He…

Python语言:经典例题分析讲解

题1: 通过观察我们可以得出以下结论: 代码实现: """ (3)输入整数n,输出n行的字符图案。如n5时输出以下图案:* *** ***** ******* *********""""" for…

多测师肖sir_高级金牌讲师_ui自动化po框架

ui自动化po框架 一、po框架 1、基本介绍(1)po是page object 的缩写 (2)业务流程与页面元素操作分类的模式, (3)提高测试用例的可维护性、可读性 二、自动化测试框架分层如下: 结构…

MATLAB|不给糖果就捣蛋

目录 扫一扫关注公众号 效果图 代码 绘制南瓜 绘制无脸男小鬼 其中绘制风车代码: 其中 EllipsePlotter类函数代码如下 属性 (properties) 方法 (methods) 扫一扫关注公众号 效果图 代码 绘制南瓜 clc;clear;close all; [X,Y,Z]sphere(200); R1(-(1-mod(0:…

Flink(三)【运行时架构】

前言 今天学习 Flink 的一些原理性的东西,比较偏概念,但是十分重要。有人觉得上来框框敲代码才能学到东西,那是狗屁不通的道理(虽然我以前也这么认为)。个人认为,学习 JavaEE那些框架,你上来就敲…

​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​

软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】 课本里章节里所有蓝色字体的思维导图

049-第三代软件开发-软件部署脚本(一)

第三代软件开发-软件部署脚本(一) 文章目录 第三代软件开发-软件部署脚本(一)项目介绍软件部署脚本(一)其他方式 关键字: Qt、 Qml、 bash、 shell、 脚本 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Object…

探讨m6调控因子与人类癌症之间的因果关系,纯生信也能轻松上5+

今天给同学们分享一篇生信文章“m6A Regulators Is Differently Expressed and Correlated With Immune Response of Esophageal Cancer”,这篇文章发表在Front Cell Dev Biol期刊上,影响因子为5.5。 结果解读: m6A调控因子在基因组中的异常与…

【2023.11.6】OpenAI发布会——近期chatgpt被攻击,不能使用

OpenAI发布会 写在最前面发布会内容GPT-4 Turbo 具有 128K 上下文函数调用更新改进了指令遵循和 JSON 模式可重现的输出和对数概率更新了 GPT-3.5 Turbo 助手 API、检索和代码解释器API 中的新模式GPT-4 Turbo 带视觉DALLE 3文字转语音 (TTS)收听语音样本…

Spark大数据应用实战

系列文章目录 送书第一期 《用户画像:平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 送书第四期 《AI时代项目经理成长之道》 …

【QEMU-tap-windows-Xshell】QEMU 创建 aarch64虚拟机(附有QEMU免费资源)

“从零开始:在Windows上创建aarch64(ARM64)虚拟机” 前言 aarch64(ARM64)架构是一种现代的、基于 ARM 技术的计算架构,具有诸多优点,如低功耗、高性能和广泛应用等。为了在 Windows 平台上体验…

全国5米高程DEM数据及衍生的坡度数据

坡度是地表单元陡缓的程度,通常把坡面的垂直高度和水平距离的比值称为坡度。坡度的表示方法有百分比法、度数法、密位法和分数法四种,其中以百分比法和度数法较为常用。 坡度是地表单元陡缓的程度,通常把坡面的垂直高度和水平距离的比值称为坡…