乐趣区

关于java:Java中只有值传递

参数传递

在咱们日常编写代码的过程中,调用函数可能是最常见的操作了。那么,在调用函数时,参数是怎么样传递的呢?

值传递

置信有很多人都是学 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 中变量分为根本变量和对象,咱们无妨别离探讨。

根本变量类型

首先,对于 intchar 等根本类型,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…

退出移动版