Java中的传值与传引用

java的数据类型分为:

  1. 基本数据类型
  2. 引用数据类型

1. 基本数据类型

  • 整型: int, short, long, byte
  • 浮点型:float, double
  • 字符型:char
  • 布尔型:boolean

2. 引用数据类型

  • 数组
  • 接口

方法的参数分为:

  • 实际参数
  • 形式参数

形式参数:定义方法时写的参数。

实际参数:调用方法时写的具体数值。

一般情况下,在数据作为参数传递的时,传递的参数是基本数据类型的是值传递,传递的参数是引用数据类型的是引用传递(地址传递)。

值传递

 1 public class Test {
 2 
 3     //主函数
 4 
 5     public static void main(String[] args) {
 6 
 7     int n1 = 1;
 8 
 9     int n2 = 2;
10 
11     swap(n1, n2);
12 
13     System.out.println("n1 = " + n1);
14 
15     System.out.println("n2 = " + n2);
16 
17 }
18 
19 //定义一个方法,用来交换两个整数
20 
21 public static void swap(int a, int b) {
22 
23     int temp;
24 
25     temp = a;
26 
27     a = b;
28 
29     b = temp;
30 
31     System.out.println("a = " + a);
32 
33     System.out.println("b = " + b);
34 
35     }
36 
37 }
View Code 

运行的结果是:

a = 20

b = 10

n1 = 10

n2 = 20

分析:

  1. 主函数进栈,n1、n2初始化。
  2. 调用swap方法,swap( )进栈,将n1和n2的值,复制一份给a和b。
  3. swap方法中对a、b的值进行交换。
  4. swap方法运行完毕,a、b的值已经交换。
  5. swap方法弹栈。
  6. 主函数弹栈。

在swap方法中,a、b的值进行交换,并不会影响到n1、n2。因为,a、b中的值,只是从n1、n2的复制过来的。 也就是说,a、b相当于n1、n2的副本,副本的内容无论怎么修改,都不会影响到原件本身。

引用传递

 1 public class Test {
 2 
 3     //主函数
 4 
 5     public static void main(String[] args) {
 6 
 7     int[] arr = {5,5,5};
 8 
 9     change(arr);
10 
11     System.out.println(arr[0]);
12 
13 }
14 
15 //将数组的第一个元素变为1
16 
17 public static void change(int[] array) {
18 
19     int arr_length = array.length;
20 
21     array[0] = 1;
22 
23     }
24 
25 }
View Code

运行的结果是:

1

分析:

  1. 主函数进栈,int[] arr初始化。
  2. 调用change方法,change( )进栈,将arr的地址值,复制一份给array。
  3. change方法中,根据地址值找到堆中的数组,并将第一个元素的值改为1。
  4. change方法运行完毕,数组中第一个元素的值已经改变。
  5. change方法弹栈。
  6. 主函数弹栈。

调用change()的时候,形参array接收的是arr地址值的副本。并在change方法中,通过地址值对数组进行操作。change方法弹栈以后,数组中的值已经改变。main方法中,打印出来的arr[0]也就从原来的5变成了1.

不管是主函数,还是change方法,操作的都是同一个地址值对应的数组。 就好比你把办公室的钥匙给了其他人,其他人拿着钥匙进去把办公室的一本书拿走了,你再进到办公室,里面的那本书已经没有了。 这里的钥匙就相当于地址值,办公室就相当于数组本身。

String类型传递

 1 public class Test {
 2 
 3         //主函数
 4 
 5         public static void main(String[] args) {
 6 
 7         String str = "ABC";
 8 
 9         change(str);
10 
11         System.out.println(str);
12 
13     }
14 
15     public static void change(String s) {
16 
17       s = "XYZ";
18 
19     }
20 
21 }
View Code

运行的结果是:

ABC

这里的String是很特殊的。

String是一个类,类是引用数据类型,做为参数传递的时候,应该是引用传递。但是从结果看起来却是值传递。

分析:

String的API中有这么一句话:“their values cannot be changed after they are created”,意思就是说String的值在创建之后不能被更改。

API中还有一段:

String str = "abc";

等效于:

char data[] = {'a', 'b', 'c'};

String str = new String(data);

也就是说:对String对象str的任何修改 等同于重新创建一个对象,并将新的地址值赋值给str。

这样的话,上面的代码就可以写成:

 1 public class Test {
 2 
 3         //主函数
 4 
 5         public static void main(String[] args) {
 6 
 7         String str1 = "ABC";
 8 
 9         change(str1);
10 
11         System.out.println(str1);
12 
13     }
14 
15     public static void change(String s) {
16 
17         char data[] = {'a', 'b', 'c'};
18 
19         String str = new String(data);
20     
21         s = str;
22 
23         }
24 
25 }
View Code

分析:

  1. 主函数进栈,str1初始化。
  2. 调用change方法,change( )进栈,将str1的地址值,复制一份给s。
  3. change方法中,重新创建了一个String对象”abc”,并将s指向了新的地址值。
  4. change方法运行完毕,s所指向的地址值已经改变。
  5. change方法弹栈。
  6. 主函数弹栈。

String对象做为参数传递时,走的依然是引用传递,只不过String这个类比较特殊。

String对象一旦创建,内容不可更改。每一次内容的更改都是重现创建出来的新对象。

当change方法执行完毕时,s所指向的地址值已经改变。而s本来的地址值就是copy过来的副本,所以并不能改变str1的值。

String类型类似情况:

 1 class Person {
 2 
 3     String name;
 4 
 5      public Person(String name) {
 6 
 7     this.name = name;
 8 
 9     }
10 }
11 
12 public class Test {
13 
14     public static void main(String[] args) {
15 
16         Person p = new Person("张三");
17     
18         change(p);
19 
20         System.out.println(p.name);
21 
22     }
23 
24  
25     public static void change(Person p) {
26 
27         Person person = new Person("李四");
28 
29         p = person;
30 
31     }
32 
33 }                
View Code

运行的结果是:

张三

总结

值传递的时候,将实参的值,复制一份给形参。

引用传递的时候,将实参的地址值,复制一份给形参。

也就是说,不管是值传递还是引用传递,形参拿到的仅仅是实参的副本,而不是实参本身。

原文地址:https://www.cnblogs.com/ciaociao/p/6567873.html