java的值传递
内容选自尚硅谷
值传递过程中的常见问题
方法中的变量叫做局部变量,局部变量都存在在栈中,对于基本数据类型,栈中存的是局部变量的值,而非地址,对于引用数据类型,栈中存的是地址,属性变量放在堆中。java中,引用类型的变量只有两种值,要么是null,要么是地址值。
在实参向形参赋值的时候,如果实参是基本数据类型,那么形参从实参接收到的数据就是基本数据类型的值
如果实参是引用数据类型,那么形参从实参接收到的数据就是指向堆空间的地址值。
在编程实现交换两个变量的值的时候
package come.atguigu.java;
public class ValueTransferTest1 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m = "+ m + ",n = " + n);
ValueTransferTest1 test = new ValueTransferTest1();
test.swap(m, n);
System.out.println("m = "+ m + ",n = " + n);
}
public void swap(int m,int n){
int temp = m;
m = n;
n = temp;
}
}
运行结果为
m = 10,n = 20
m = 10,n = 20
以上用法是有问题的,因为形参的m,n确实是继承了实参的值,且形参的变量在栈空间中进行了数据交换过后,马上就被清空(出栈)了,没有把交换后的结果返回。
上图选自尚硅谷
正确的用法
想要实现两个变量的值呼唤,采用基本数据类型的变量行不通,采用引用数据类型的变量,将要交换的变量定义为类的属性,代码如下
package come.atguigu.java;
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = "+data.m+",n = " + data.n);
ValueTransferTest2 test = new ValueTransferTest2();
test.swap(data);
System.out.println("m = "+data.m+",n = " + data.n);
}
public void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
运行结果正确
m = 10,n = 20
m = 20,n = 10
实参向形参赋值的时候,在栈空间又创建了一个data变量指向堆空间的对象,在swap方法中,又创建了一个临时的局部变量temp,当方法执行完毕后,上面那个data变量和temp变量出栈,底部的data变量仍然指向堆空间的对象,对象有指向它的变量,不会被当作垃圾而回收。
ps:当实参向形参赋值的时候,会在栈空间中建立形参的变量
方法执行完毕后,会把方法内的局部变量和对象出栈,一旦堆空间对象没有栈空间的变量所指向,堆空间的对象会被当作垃圾而回收掉。
若想要对数组进行冒泡排序
public void sort(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// int temp = arr[j];
// arr[j] = arr[j + 1];
// arr[j + 1] = temp;
//错误的:
// swap(arr[j],arr[j + 1]);
//正确的:
swap(arr,j,j + 1);
}
}
}
}
public void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
ps:在main方法中使用swap方法时,首先要创建包含swap方法的对象,因为main方法有static的修饰符,如果调用swap的方法不包含static的修饰符,可以不用创建对象,直接调用。如上面的代码所示,sort方法没有加static的修饰符,在调用swap方法时,不用创建swap方法的对象。
字符串的值传递机制
代码如下
package com.atguigu.java;
public class ValueTransferTest {
public static void main(String[] args) {
String s1 = "hello"; //栈空间新建一个s1的变量,指向字符串常量池的地址
ValueTransferTest test = new ValueTransferTest();
test.change(s1);
System.out.println(s1);
}
public void change(String s){
s="hi~~";
}
}
结果为
hello
String为引用 数据类型
String为引用数据类型中的类,既然是类,就必须new一个对象出来,字符串String类型比较特殊,既可以像基本数据类型一样直接赋值,也可以用new的方式给字符串赋值。
String类型的字符串的值存储在字符串常量池当中,用一个char[]型的数组存放,s1存放的是数组首地址值,在java1.8中字符串常量池一般认为在堆空间,而非在方法区当中。
String s1 = "hello"; 、
栈空间新建一个s1的变量,指向字符串常量池的地址.
public void change(String s){
s="hi~~";
}
先在栈空间新建一个s的变量,依然指向’hello’字符串,再在字符串常量池中新建了一个’hi~~’的对象,把该字符串的首地址值赋给s,因此s1仍然指向hello。