在学习Java的过程中感觉Java的值传递机制这部分有点绕,所以这篇文章来捋一捋Java的值传递机制,也非常适合像我一样的初学者来理解和学习。
首先先回顾下关于基本数据类型变量的赋值,对于交换两个变量的值的操作,有如下代码:
public class ValueTransferTest {
public static void main(String[] args) {
System.out.println("*******基本数据类型**********");
int m = 10;
int n = m;
System.out.println("m =" + m + ",n =" + n); //输出结果:10,10
n = 20;
System.out.println("m =" + m + ",n =" + n); //输出结果:10,20
}
}
这个结果很容易理解,因为int n = m;相当于将m所保存的值10又复制了一份给n,m和n所保存的值虽然都是10,但两个10是独立的。总结起来就是:如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
那么对于引用数据类型的赋值,有如下代码示例:
public class ValueTransferTest {
public static void main(String[] args) {
System.out.println("*******引用数据类型**********");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1; //o1和o2的地址值相同,都指向堆空间中同一个对象实体
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId =" + o2.orderId);
}
}
class Order{
int orderId;
}
这个结果也很好理解,由于o2不是new出来的对象,所以o1与o2指向的都是对空间中的同一个对象实体,所以如果o1.orderId的值变化的话,二者都会跟着变化。 总结起来就是变量是引用数据类型时,此时赋值的时变量所保存的数据的地址值。
那么对于方法的形参,当形参是基本数据类型时,通过如下代码理解方法的值传递机制。比如定义一个交换两个变量的值的方法:
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); //发现交换完还是m=10,n=20.
System.out.println("m =" + m + ",n =" + n);
}
//交换两个变量值的方法
public void swap(int m,int n) {
int temp = m;
m = n;
n = temp;
}
}
诶,这个时候会发现调用这个方法完事儿m和n的值并没有交换,我们可以通过如下的图来理解其原因。
上述代码实际上是将m和n两个实参的值赋给了swap方法中的形参m和n,因此同上述基本数据类型的赋值一样,也是复制了10和20这两个值给swap方法中的形参,所以当swap方法执行完毕后,其方法体内的m和n是换了,然后这些局部变量就出栈了,但是!!main()方法中的m和n压根就没动!!,你换的是也可以说是m和n的替身,所以造成上述结果。那么如何实现真正交换main方法中m和n的值的方法呢?代码如下:
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;
}
这段代码与上述代码的主要区别是swap()方法内的形参是一个引用数据类型,这是因为如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。我们可以结合如下的内存结构来一起理解。
首先新new了一个对象data,在data栈空间存储的是对象的地址值,在堆空间存储的是相应对象中的属性,其默认值为0,接下来由data.m和data.n对堆空间中的m和n分别复制为10和20。接着对swap()方法实例化test,并将data这一引用数据类型传入形参中,由于此时赋给形参的是0x7788的地址值,形参中的data与main()方法中的data指向的是同一地址值,因此方法执行的步骤为:在栈中生成temp这一局部变量,然后将堆中m的值10赋给temp;将堆空间中n的值20赋给m,再将栈空间中temp的值10赋给堆空间中的n,实现m和n值的交换。完成后temp出栈,此时是真正的对m和n进行操作。
再举个例子,对于数组元素的冒泡排序,因为很常用所以想把其封装成方法。那么错误的示例如下:
public class BubbleSortMethod {
public static void main(String[] args) {
int[] arr = new int[] {43,28,2,-21,28,99,58};
BubbleSortMethod test = new BubbleSortMethod();
test.sort(arr);
test.print(arr);
}
//冒泡排序
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]);
}
}
}
}
//数组打印
public void print(int[] arr) {
for(int i = 0;i < arr.length;i++) {
System.out.print(arr[i] + "\t");
}
}
public void swap(int i,int j) {
int temp = i;
i = j;
j = temp;
}
}
与之前同理,此时对于swap()方法需要传入的也应给是引用数据类型,正确的方法如下:
public class BubbleSortMethod {
public static void main(String[] args) {
int[] arr = new int[] {43,28,2,-21,28,99,58};
BubbleSortMethod test = new BubbleSortMethod();
test.sort(arr);
test.print(arr);
}
//冒泡排序
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,j + 1);
}
}
}
}
//数组打印
public void print(int[] arr) {
for(int i = 0;i < arr.length;i++) {
System.out.print(arr[i] + "\t");
}
}
public void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
补充说明:由于main()方法中由static关键字,所以在调用没有static的方法时必须通过实例化对象来调用,而sort()方法和swap()方法没有static关键字,所以可以直接调。
后记:写的有些墨迹,可能因为楼主比较笨,需要反复叨叨,欢迎补充和错误指正。
图片与知识来源:尚硅谷:Java教程——宋红康