深入理解Java引用类型和值调用-变量在内存里存在哪里
引言
什么是数据类型?在计算机科学和计算机编程中,数据类型或简单的类型是数据的一个属性,它告诉编译器或解释器程序员打算如何使用数据。
每一种编程语言都有属于自己的数据类型,可能不尽相同,但万变不离其宗。Java中当然也有一套属于自己的数据类型,分为两大类:基础数据类型和引用数据类型,它们各自又有不同的分类。
在C语言中,关于函数的参数传递有两个专业术语:按值调用(call by value)和按引用调用(call by reference),它们和数据类型关系密切,但是,Java中只有按值调用。小编楼兰胡杨的这篇文章就和老铁们探索一下Java中的各种数据类型的分类、用途以及之间的关系等,探索java热门基础面试题按值调用(值传递)。
数据类型分类
在Java里面,整体上把数据类型分为两大类——四类八种基础类型(primitive types) 和 3种引用类型(reference types) ,分类图如下图所示:
接下来,分别介绍基本类型和引用类型。
基本类型
基本类型是Java语言预定义的类型,用相应的保留关键字来表示,具有明确的取值范围和数学行为,表示了真实的数字、字符和整数。基本类型的数据都是单个值,而不是复杂的对象,所以基本类型并不是面向对象的,这主要是出于效率方面的考虑。
基本类型包括布尔类型和数值类型,数值类型又分为整型和浮点型。八种基本数据类型分类如下:
1、整型:byte、short、int、long
2、字符型:char(本质上是一种特殊的int)
3、浮点型:float、double
4、布尔型:boolean
与此同时,Java语言也为基本类型提供了对应的对象版本,即基本类型的包装类(wrapper),具体信息如下表所示:
如果类的成员变量(字段)是基本类型,那么在类初始化时,每个成员变量将会被赋予一个如上表中默认值列所述的默认值。若为了防止因默认值引起不必要的麻烦,推荐用基本类型的包装类。
引用类型
引用类型(The value of reference types are references to objects)中的引用,一般是指某个对象的内存地址,其中对象是动态创建的类实例或者数组等。简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小占用不相等的内存空间。
另外,Java语言本身不支持C++中的结构体(struct) 或联合体(union) 等数据类型,这种复合数据类型一般都是通过类或接口进行构造。Java中,除了基础类型外,其余的类型都是引用类型,诸如Java自带的String
、BigDecimal
、BigInteger
以及自定义的业务类等,如下所示:
User user; //类引用类型
List<Object> list; //接口引用类型
Integer[] array; //数组引用类型
和基础数据类型变量不同的是,引用类型的变量可以被赋值为null,如下所示:
User user = null; //类引用类型
List<Object> list = null; //接口引用类型
Integer[] array = null; //数组引用类型
Java是一门面向对象的编程语言,除了基本数据类型以外,它要求每种数据类型都必须是一个类。面向对象的编程思想力图使在计算机语言中对事物的刻画与现实世界中该事物的本来面目尽可能地一致,类(class)和对象(object)就是面向对象方法的核心概念。
类是对某一类事物的描述,是抽象的、概念上的定义;对象是实际存在的该类事物的个体,因而也称为实例(Instance)。类和对象就如同概念和实物之间的关系一样,类就好比是一个模板,而对象就是该模板下的一个实例。
引用分类
引用有哪些分类?【划重点】在Java中引用包括Final reference 强引用、Soft reference 软引用、Weak reference 弱引用 和 Phantom reference 虚引用。那么,为什么会提供这四种引用?主要原因如下:
-
利于 JVM 进行垃圾回收;
-
利于开发人员灵活的决定某些对象的生命周期。
如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间(stack,栈空间)来保存。但是它们的值是相同的,都指向同一个对象在堆内存(heap,堆空间)中的位置,比如:
String a="Word";
String b="Word Two";
String b=a;
如图,b = "Word Two"; b = a;
不是改变了 "Word" 这一对象的值,初始化时,b 的值为绿线所指向的“Word Two”,然后 b=a
使 b 的引用变更为红线所指向的”Word“。但要注意,String 对象的值本身是不可更改的。
数据存在哪
Java是面向对象语言,其概念为一切皆为对象,但基本数据类型特立独行。基本数据类型大多是面向机器底层的类型,它是 “值” 而不是一个对象,存放于“栈”中而非“堆”中,但Java一切皆为对象的概念绝非说说而已,它为每一个基本数据类型都提供了相应的包装类(封装器类)。包装类就是一个对象,它存放于“堆”中,下面介绍一下栈内存和堆内存的区别。
程序是运行在内存中的,也就是我们常说的电脑16g还是8g的内存。而内存空间又划分为栈内存
和堆内存
。
栈内存
分配速度快,内存空间小。Java的基本类型
和引用类型的对象引用
存在栈内存
中。堆内存
分配速度稍慢,内存空间大。Java的引用类型指向的具体对象
存在堆内存
中。
基本类型使用频繁而且占用空间小,放在栈内存
中。引用类型的对象引用
就是堆内存
中对象存放的地址。打个比喻:藏宝图里面记录着宝藏的位置,可以根据藏宝图上面标记的位置找到这些宝藏,这里藏宝图就是栈内存
,存放着对象引用,而具体埋放宝藏的地方就是堆内存
。
基本类型与引用类型的区别
不论是基本类型还是引用类型,都会先在栈中分配一块内存。对于基本类型来说,这块内存区域中包含的是基本类型的具体数据内容;对于引用类型来说,这块内存区域中包含的是指向真正内容的引用,而真正的内容则被分配在堆上。
所有的类型在内存中都会分配一定的存储空间,基本类型只有一块存储空间(分配在stack中), 而引用类型有两块存储空间(一块在stack中,一块在heap中);形参在使用的时候也会分配存储空间,方法调用完成之后进行垃圾回收。
值传递
首先要强调的是java中只存在值传递,只存在值传递!!! 然而我们经常看到对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是这样理解是一叶障目,希望老铁们不要被此假象所蒙蔽,实际上传入函数的值是对象引用的拷贝,即传递的是引用的地址值,故依然是按值传递。
函数的形参类型如果是引用类型,则调用函数时传过来的就是实参的副本,这个副本存放的是实参的引用地址。如果是基本类型,那么传过来的也是实参的副本,但此时副本存放的是实参的值,如果在函数中改变了副本的值不会改变原始的值。
为什么Java中只有按值调用,没有按引用调用?Java不支持指针,所以Java不支持引用调用。但是C/C++支持指针,故这些语言支持引用调用。
Java 方法的参数是简单类型的时候,是值传递的。这一点我们可以通过一个简单的例子来说明:
package test;
public class Test {
//交换两个形参的值
public static void swap(int a,int b){
int c=a;
a=b;
b=c;
System.out.println("a: "+a);
System.out.println("b: "+b);
}
public static void main(String[] args){
int c=10; // 实参
int d=20; // 实参
swap(c,d);
System.out.println("After swap:");
System.out.println("c: "+d);
System.out.println("d: "+c);
}
}
运行结果:
a: 20
b: 10
After swap:
c: 20
d: 10
不难看出,虽然在 swap (a,b) 方法中改变了形参的值,但对实参本身并没有影响,即对 main(String[]) 方法里的 a,b 变量没有影响。故参数类型是基本类型的时候,是按值传递的,实际上是将参数的值作了一个拷贝传进函数的,无论在函数里怎么改变其值,其结果都是只改变了拷贝的值,而不影响源值。
引用对象传递之例外null
众所周知,java中除基本类型外,参数传递的都是引用的地址。但是,有一个例外,就是当为引用类型的实参赋值null时,它依然是值传递。也就是说,传参为null的时候退化为值传递,不管函数体内用这个参数做了什么,跳出函数体后该参数依然是null。
这里澄清一些误解,null既不是对象也不是一种类型,它仅仅是一个特殊的值;你可以将其赋予任何引用类型变量,即可以将其转化成任何类型。再进一步剖析,其实是基本类型和指向null的非基本类型的引用都存在栈而非堆中,而引用类型传递的是堆内存地址。还有一种常见的、引用失效的场景是引用被改变,示例如下:
User wiener = new User();
wiener.setUserName("Wiener");
wiener.setAge(19);
wiener = new User(); // 覆盖了原来的引用
wiener.setAge(18);
log.info(wiener); // 此时打印出来时,控制台没有姓名只有年龄为18
Reference

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南