目录
1.说明
2.基础数据类型
3.基础数据类型的包装类
4.对象,数组,集合
java中只有值传递,当作为参数传递时,传递的是基础数据类型的值的副本,及引用类型的引用的副本。
①基础数据类型的内存分配
基础数据类型是在栈内存中分配,当你声明一个基本数据类型变量时,会直接在栈上分配空间,栈内存用于存储局部变量和方法调用时的临时变量,这种内存的分配和释放速度是非常快的。
②栈的说明
栈:栈是一种数据结构,遵循后进先出原则,其元素的插入和删除都发生在同一端,这个端被称为栈顶,也就是说最新加入的元素总是最先被移除,从而实现了后进先出的特性。
实际应用:
函数调用:当一个函数调用时,系统会将上下文信息保存在栈中,当函数执行完毕,栈顶的信息会被弹出,恢复之前的状态。
递归:每次递归调用都会将当前状态保存到栈中,以便在递归调用结束能够恢复之前的状态。
③将基础类型作为参数传递至方法中
当一个基础数据类型作为参数传递至方法中时,传递的是值的副本。
int a = 1;
int b = 1;
比如说将a作为参数传递到方法中,会将和a一样值的副本,即b传递给方法,a和b在内存中是这样体现的。
首先是内存分配,每当一个方法被调用时,JVM 会为该方法创建一个新的栈帧。a和b作为基础数据类型会存储在栈中,当方法执行到 int a = 1;
时,JVM 在当前栈帧中为 a
分配内存(通常是 4 字节,因为 int 类型占用 4 字节),并将值 1
存储在这个内存位置。当调用方法时,会创建b,JVM 为 b
分配另一个 4 字节的内存空间,并将值 1
存储在这个位置。a和b各自占用各自的内存空间,所以,在方法中修改参数的内容,实际修改的b的内容,方法外面的a的内容不受影响。布局如下:
+-------------------+
| 返回地址 | ←(方法结束后返回的地址)
+-------------------+
| b | ← b 的值(4 字节)
| ----------------- |
| a | ← a 的值(4 字节)
+-------------------+
(栈帧顶部)
④示例
@Test
public void testParam1(){
int num1 = 1;
changeNum(num1);
System.out.println(num1);
}
public void changeNum(int num){
num = 23;
}
输出结果为1
①包装类的不可变性
Java中的基础数据类型的包装类(如 Integer
、Boolean
、Double
等)都是不可变的(immutable)。
不可变类是指一旦创建对象后,其状态(即其属性值)不能被改变。对于不可变类,所有的方法都不会修改对象本身,而是返回一个新的对象。由于不可变性,这些包装类在多线程环境中是安全的,不会出现数据竞争的问题。
int
→ Integer
boolean
→ Boolean
char
→ Character
byte
→ Byte
short
→ Short
long
→ Long
float
→ Float
double
→ Double
②将基本数据类型的包装类传递至方法中
将基础数据类型的包装类传递至方法中时,传递的是引用的副本
Integer a = 1;
Integer b = 1;
比如说将a传递至方法中,会将和a一样的值的副本b传递至方法中。a和b在内存中是这样体现的。
当您将一个整数值(如 1
)赋给 Integer
类型时,Java 会进行装箱,将基本类型 int
转换为 Integer
对象。Integer
对象会分配到堆内存中,而不是栈内存。JVM 会在堆中创建一个 Integer
对象来表示值 1
。对于小整数(通常是 -128 到 127 范围内的数字),Java 会利用缓存机制,即对这些常用的 Integer
对象进行复用(称为“缓存”)。因此a和b在栈上是两个对象,指向堆内存中的同一个对象。如果在方法中修改b的内容,如修改为2,则栈内存中的b会指向堆内存中的2,不会影响a的内容。内存布局如下:
堆内存
+-------------------+
| Integer Object | ← (堆内存中存储的 Integer 对象)
| value = 1 |
+-------------------+
栈内存
+-------------------+
| 栈内存 |
+-------------------+
| a → (堆中的 Integer 对象) |
| b → (堆中的 Integer 对象) |
+-------------------+
@Test
public void testParam2(){
Integer num1 = 2;
changeNum1(num1);
System.out.println(num1);
}
public void changeNum1(Integer num){
num = 24;
}
输出结果为2
将对象,数组或者集合传递至方法中时,传递的也是引用的副本,方法外的变量和方法内的参数都是执行同一个堆内存中的对象。在方法中对参数内容进行修改时,会直接修改堆内存中的内容,方法外的变量的内容也会随之改变。如果你在方法中修改了参数的引用,方法外的变量的内容则不受影响。
在方法中直接修改内容的示例
@Test
public void testBean(){
SysConfig sysConfig = new SysConfig();
sysConfig.setConfigId(123L);
setBean(sysConfig);
System.out.println(sysConfig.getConfigId());
}
public void setBean(SysConfig sysConfig){
// sysConfig = new SysConfig();
sysConfig.setConfigId(258L);
}
输出结果为258
在方法中修改引用的示例
@Test
public void testBean2(){
SysConfig sysConfig = new SysConfig();
sysConfig.setConfigId(123L);
setBean2(sysConfig);
System.out.println(sysConfig.getConfigId());
}
public void setBean2(SysConfig sysConfig){
sysConfig = new SysConfig(); 在堆内容中创建了一个对象,将参数的引用指向这个对象,修改了参数的引用
sysConfig.setConfigId(258L);
}
输出结果为123