参数传递
在咱们日常编写代码的过程中,调用函数可能是最常见的操作了。那么,在调用函数时,参数是怎么样传递的呢?
值传递
置信有很多人都是学C语言入门的,刚开始写代码时,用的最多的就是值传递了。
void plus_one(int a){ a++; printf("a: %d", a);}int main(){ int n = 10; plus_one(n); printf("n:%d", n); return 0;}复制代码
这是一个简略的值传递的例子,无需多言,plus_one
函数的作用就是将传进来的数加一,而后输入。所谓值传递,就是间接将实参n
的值赋给形参a
,赋值实现之后,两者再无瓜葛。
因而,下面的代码能够等效为:
int main(){ int n = 10; // plus_one start int a; a = n; a++; printf("a: %d", a); // plus_one end printf("n:%d", n); return 0;}复制代码
能够看到,值传递简略直观,然而,调用函数并不能扭转实参n
的值。
指针传递
那么,当咱们须要扭转实参的值的时候,咱们就会想到应用指针传递,也就是所谓的地址传递。
void plus_one(int* p){ *p = *p + 1;}int main(){ int n = 10; plus_one(&n); printf("The result is %d", n); return 0;}复制代码
这里,咱们将实参n
的地址传入plus_one
函数,在函数中,间接对指针p
所指向的值,也就是n
做操作,天然就能够扭转实参n
的值了。
实际上,指针传递也是值传递。咱们将下面的代码改写:
int main(){ int n = 10; // plus_one start int* p; p = &n; *p = *p + 1; printf("The result is %d", n); // plus_one end return 0;}复制代码
能够看到,所谓的指针传递,也只不过是将变量n
的地址值赋给指针变量p
,实际上也是值传递。
所以,能够不负责任的概括为,C语言中只有值传递;
援用传递
指针诚然弱小,然而因为代码不易读,难以了解等问题,也是广为诟病。C++作为C语言的超大杯,引入了援用传递来简化指针传递的写法。
void plus_one(int& a){ a++;}int main(){ int n; plus_one(n); printf("The result is %d", n); return 0;}复制代码
C++中,对&运算符进行了重载,实现了援用传递。具体实现为,在调用plus_one
函数时,在函数调用栈中存变量n
的地址,而不是n的值。因而,plus_one
中的变量a
就相当于是n
的"别名",对a
操作时,天然会扭转n
的值。
可见,援用传递的底层也是赋值操作。
Java中的参数传递
那么,在Java中,到底是援用传递,还是值传递呢?
Java中变量分为根本变量和对象,咱们无妨别离探讨。
根本变量类型
首先,对于int
、char
等根本类型,Java是应用值传递的,很容易验证。
static void plusOne(int a){ a++; System.out.println("a: " + a);}public static void main(String[] args){ int n = 10; plusOne(n); System.out.println("n: " + n);}复制代码
显然,与C语言中一样,这里n
的值是不会扭转的。
对象
public class PassObject { public static void main(String[] args) { Dog myDog = new Dog("Test"); foo(myDog); System.out.println(myDog.getName());// TestPlus } public static void foo(Dog dog) { dog.setName("TestPlus"); }}class Dog{ private String name; public Dog(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; }}复制代码
通过下面的例子能够看到,传入对象的援用时,是能够扭转对象的属性变量的。那么Java在传递对象作为参数时,是援用传递吗?
实际上并非如此,Java中,对象的援用,实际上相当于对象的指针。在Java中操作对象,只有通过援用操作这一种路径。某种意义上,Java中是不能间接操作对象的。
也就是说,在上例中传参时,没有对myDog
对象实例做任何操作,只是把myDog
援用值赋给了foo
函数中的本地变量dog
。并没有像援用传递一样,传入对象实体,然而只在栈中保留对象援用的操作。所以,Java中传递对象时,也是值传递。
所以,Java中只有值传递。
值得一提
然而,还是会有一些非凡状况,会让人狐疑上述论断。
数组
下面只剖析了根本变量类型和对象,数组呢?
实际上,Java中的数组也是一种对象,数组类也是继承自Object类。在将数组作为参数时,也是传递的数组的援用,并没有传递数组的实体。
public static void changeContent(int[] arr) { arr[0] = 10;}public static void changeRef(int[] arr) { arr = new int[2]; arr[0] = 15;}public static void main(String[] args) { int [] arr = new int[2]; arr[0] = 4; arr[1] = 5; changeContent(arr); System.out.println(arr[0]); // 10 changeRef(arr); System.out.println(arr[0]); // 10}复制代码
在上例中能够看到,将传入的数组援用赋给一个新的数组后,这个援用就不能操作之前的数组了。
对于援用,英文是reference
,实际上,我自认为,翻译为句柄是更为贴切的,援用就像是一个柄,一个Handler
,你能够用它操作实体,但他并不是实体自身。就像手柄能够操控游戏机,但不是游戏机自身,当你将这个手柄连贯到另一个游戏机的时候, 它就不能操控之前的游戏机了。
包装类和String
public static void main(String[] args) { Integer n = 1; plusOne(n); System.out.println(n); // 1}private static void plusOne(Integer n) { n = n + 1; System.out.println(n);// 2}复制代码
在这段代码中,n
作为Integer
类型实例的句柄,却并没有胜利扭转对象的值,这是为什么呢?
在Integer
类中,存对应值的属性是value
,其申明如下:
private final int value;复制代码
可见,value值是不能改的,那加的操作是怎么实现的呢?
在上述加一的过程中,会从新new
一个Integer
对象,让后将这个对象赋给援用n
。这样以来,之前的对象天然是不会扭转的。
实际上,包装类以及String
类的值,都是final
的,所以在执行+
的过程中,都会从新生成一个对象,而后对它赋值。
参考:《2020最新Java根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/691670...