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);    //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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL json相关函数详解

MySQL提供了一系列的JSON函数,用于解析、提取、修改和操作JSON数据。以下是一些常用的JSON函数及其功能。 以下所有操作都使用该表(zone_test)用来演示: 一:JSON_OBJECT(key1,value1,key2,value2) 1、作用:…

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

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

Read Completion Boundary (RCB)切分规则

Read Completion Boundary(RCB) 切分规则 Read Completion Boundary(RCB) 简介 当Read Completion 包含multi-completions时,RCB 规定了多个Completions地址的align规则。Spec中规定RCB可以是64 Byte或者128 Byte,该值可以在link_control register中得…

Unity随笔:C#运行时

Unity是如何编译运行C#的 (1)Unity会通过编译器将C#脚本编译成IL指令。 Unity会通过Roslyn来对C#代码进行编译,生成中间IL指令集。 当我们每次修改或者添加新的C#代码文件,Unity界面的右下角会出现短暂的“转圈”现象。这就意味…

PTE-RA总结

目录 FIBW刷题记录 1.The Plains Indians were people who did not like 2.An economic depression is a period of sustained 3.Pidgins are languages that are born after contact between 4.It is tempting to try to prove that good looks 5.The stock of Austral…

chrome安装vue devtools

不能访问应用商店 如果可以访问应用商店可以往下看 插件源代码 选择shell-chrome,这是官方的插件源码 下载源代码打包 参考教程 点击扩展按钮->管理扩展程序->打开开发者模式->把crx文件拖拽进去即可 可以访问chrome应用商店 插件地址 官方文档地址 选…

15.数组逆置【不是为啥我第四行不太对呢?】

#include<stdio.h>int fun(int a[4][4]){int i,j;int b[4][3];} int main(){int i,j;int a[3][4], b[4][3];for (i0;i<3;i)for(j0;j<4;j)scanf("%d",&a[i][j]);for (i0;i<3;i)for(j0;j<4;j)b[j][i]a[i][j];printf("逆置后&#xff1a;\n&…

Voice Control for ChatGPT简单高效的与ChatGPT进行交流学习。

快捷又不失灵活性 日常生活中&#xff0c;我们与亲人朋友沟通交流一般都是喜欢语音的形式来完成的&#xff0c;毕竟相对于文字来说语音就不会显的那么的苍白无力&#xff0c;同时最大的好处就是能解放我们的双手吧&#xff0c;能更快实现两者间的对话&#xff0c;沟通便更高效…

STM32 堆栈空间分布

参考 运行时访问__initial_sp和__heap_base 无RTOS时的情况 在以上配置的情况下&#xff0c;生成工程。在工程的startup.s文件中&#xff0c;由如下代码&#xff1a; Stack_Size EQU 0x400AREA STACK, NOINIT, READWRITE, ALIGN3 __Stack_top ; 自己添加 Stack_Mem…

MySQL代码子查询续集

dept表&#xff1a; emp表&#xff1a; -- 查询每个部门的信息(包括&#xff1a;部门名&#xff0c;编号&#xff0c;地址)和人员数量 -- 1.将两张表结合起来&#xff0c;筛选出部门名&#xff0c;编号&#xff0c;地址 tips: 表.* 表示将该表所有列都显示出来&#xff…

获取请求IP以及IP解析成省份

某些业务需要获取请求IP以及将IP解析成省份之类的&#xff0c;于是我写了一个工具类&#xff0c;可以直接COPY /*** IP工具类* author xxl* since 2023/11/9*/ Slf4j public class IPUtils {/*** 过滤本地地址*/public static final String LOCAL_ADDRESS "127.0.0.1&quo…

VUE获取当前日期的周日和周六

<template><div><div @click="handleLast()">上一周</div><div @click="handleNext()">下一周</div><el-calendarref="monChild"v-model="value":first-day-of-week="7":range=&q…

React进阶之路(二)-- 组件通信、组件进阶

文章目录 组件通信组件通信的意义父传子实现props说明子传父实现兄弟组件通信跨组件通信Context通信案例 React组件进阶children属性props校验组件生命周期 组件通信 组件通信的意义 组件是独立且封闭的单元&#xff0c;默认情况下组件只能使用自己的数据&#xff08;state&a…

2022年接口测试总结【温故知新系列】

本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xff1…

Spring Gateway基础知识总结

本文主要总结Spring Gateway的基础用法&#xff0c;内容包括网关、Spring Gateway工作流程、Spring Cloud Gateway搭建、路由配置方式、负载均衡实现、断言工厂这几个部分 目录 1. 网关 1.1 网关介绍 1.2 网关对比 1.3 Spring Gateway 1.4 核心概念 1.6 总结 2. Spring …

什么GAN生成对抗网络?生成对抗网络可以干什么?

生成对抗网络(Generative Adversarial Nets,简称GAN)。神经网络分很多种,有普通的前向传播网络,有分析图片的CNN卷积神经网络,有分析系列化数据比如语言、文字的RNN循环神经网络,这些神经网络都是用来输入数据,得到想要的结果,我们看中的是这些神经网络中很好地将数据与…

SpringCloud——消息总线——Bus

1.什么是总线&#xff1f; 我们在微服务的项目中&#xff0c;通常会构建一个共同的消息主题&#xff0c;然后需要的服务可以连接上来&#xff0c;该主题中产生的消息会被监听和消费&#xff0c;这种我们称为消息总线。 SpringCloud Bus 配合SpringCloud Config使用可以实现配置…

RestTemplate配置和使用

在项目中&#xff0c;如果要调用第三方的http服务&#xff0c;就需要发起http请求&#xff0c;常用的请求方式&#xff1a;第一种&#xff0c;使用java原生发起http请求&#xff0c;这种方式不需要引入第三方库&#xff0c;但是连接不可复用&#xff0c;如果要实现连接复用&…

基础课26——业务流程分析方法论

基础课25中我们提到业务流程分析方法包括以下几种&#xff1a; 价值链分析法&#xff1a;主要是找出或设计出哪些业务能够使得客户满意&#xff0c;实现客户价值最大化的业务流程。要进行价值链分析的时候可以从企业具体的活动进行细分&#xff0c;细分的具体方面可以从生产指…

运行springboot时提示:源值 7 已过时,将在未来版本中删除,并且提示java.time not exist, LocaDateTime类找不到。

运行springboot时提示&#xff1a;源值 7 已过时&#xff0c;将在未来版本中删除&#xff0c;并且提示 java.time not exist, LocaDateTime类找不到。 解决方法&#xff1a; 方式一&#xff1a;通过IDEA修改这几个地方的JDK版本 1&#xff09;打开ProjectStructure->Proj…