Java中函数参数传递是【值传递】还是【引用传递】?

实参和形参

我们声明一个函数时,有函数头和函数体,函数头中需要有:访问修饰符、返回值类型、函数名称、形参列表。也就是说我们声明函数时,函数名后面括号里面的参数,是形式参数,即形参。

形参是在函数定义时使用的变量名,它们用于接收传递给函数的值。形参在函数调用时创建,在函数执行完成后销毁。形参的主要作用是在函数内部提供一个局部变量的名称,用于存储传递进来的值。

我们调用一个函数的时候,直接写对应的函数名(),如果函数需要参数,就传入参数,这时【调用函数时】传入的参数是实际参数,即实参。实参可以是常量、变量或者表达式,它们在调用函数时提供具体的值。

public static void main(String[] args) {
    String greeting = "Hello, World!";
    // 调用printMessage函数,greeting是实际参数
    printMessage(greeting); 
}
// message是形式参数
public void printMessage(String message) { 
    System.out.println(message);
}

函数调用的时候,根据实参和形参之间的关系、如何影响等,可以将参数的传递方式分为值传递、指针传递、引用传递【不同编程语言规定有不同传递方式】。

C/C++的参数传递方式

值传递

值传递(pass by value),即形参拷贝实参的值,形参、实参是两个不同的变量,仅仅只是值相等
在被调用的函数中,操作形式参数,能够修改形参,对实参没有影响。调用结束,形参生命周期也结束了。

void modify(int a) {
    // 这里的修改不会影响原始的变量
    a = 10;
}

int main() {
    int x = 5;
    // 传递x的副本
    modify(x); 
    // 调用结束,x的值仍然是5
    return 0;
}

指针传递

c/c++是更偏底层的语言,提供了指针,可以直接操作内存空间。指针传递(pass by pointer),即将实参的地址传递给形参,后续操作形参,就是直接操作内存空间,进而也会影响实参。【因为它们对应的是同一个地址,同一块内存空间】。

void modify(int *a) {
    if (a != nullptr) {
        // 直接修改内存空间存储的值,这里的修改会影响原始的变量
        *a = 10;
    }
}
int main() {
    int x = 5;
    // 传递x的地址
    modify(&x); 
    // 调用modify函数后,x的值被修改变成10
    return 0;
}

引用传递

使用引用传递(Pass by Reference)时,传递的是变量的别名,而不是变量的副本。这意味着函数内部对参数的任何修改都会反映到原始的变量上。引用传递通常用于大型对象,以避免不必要的复制。

void modify(int &a) {
	// 形参a是实参的别名,对应的同一块内存空间
	// 这里的修改会影响原始的变量
    a = 10;
}

int main() {
    int x = 5;
    // 传递x的引用
    modify(x);
    // x的值现在是10
    return 0;
}

引用其实就是变量的别名,因此使用引用传递时,形参和实参是同一块内存空间的两个名字;和指针传递一样,形参的改变会反应到实参上。

Java中的值传递【只有一种!!】⭐

Java中参数传递只有一种方式——值传递。这里需要注意以下java中的引用和c/c++中的引用的含义。

java的引用和C++引用的区别

  • C++中的引用:
    C++中的引用是一种特殊的别名,一旦声明,就不能再引用其他对象。在函数参数传递中使用引用时,它实际上是通过地址传递的,但是语法上看起来像是直接使用变量本身
  • Java中的引用:
    Java中的引用是指向对象的变量,它本身是一个变量,可以重新赋值指向另一个对象。Java中的所有对象都是通过引用来操作的,基本数据类型则不是。在方法调用中,传递的是引用的一个副本,这个副本和原始引用指向同一个对象。

java的值传递

在Java中,方法参数的传递方式总是按值传递,这意味着不管一个变量是基本数据类型还是引用数据类型,传递的都是变量的一个副本。

对于基本数据类型(如int、float等),传递的是实际值的一个副本。如果在方法中改变了这个值,原始的变量不会受到影响。

对于引用数据类型(如对象、数组等),传递的是引用的一个副本,但原始引用(实参)和引用的副部(形参)指向的是堆内存中的同一个对象。如果在方法中改变了引用指向对象的字段,原始对象将会受到影响,因为两者指向的是同一个对象。但是,如果在方法中将形参引用指向一个新的对象,那么原始引用仍然指向方法外的原始对象


public class TestZhiChuanDi {
    public static void main(String[] args) {
        int num = 10;
        changeValue(num); // 基本数据类型传递
        System.out.println(num); // 输出10,num的值没有改变

        StringBuilder sb = new StringBuilder("Hello");
        changeReference(sb); // 引用数据类型传递
        System.out.println(sb.toString()); // 输出HelloWorld,sb的内容改变了

        StringBuilder sb2 = new StringBuilder("World");
        System.out.println("交换前sb指向的对象是:"+ System.identityHashCode(sb)); // 输出sb指向的对象
        System.out.println("交换前sb2指向的对象是:"+ System.identityHashCode(sb2)); // 输出sb2指向的对象
        swap(sb, sb2); // 尝试交换两个StringBuilder对象
        System.out.println("交换后sb指向的对象是:"+ sb.hashCode()); // 输出sb指向的对象,并没有改变
        System.out.println("交换后sb2指向的对象是:"+ sb2.hashCode()); // 输出sb2指向的对象
        System.out.println(sb.toString()); // 输出HelloWorld,sb指向的对象没有改变
    }

    public static void changeValue(int value) {
        value = 20;
    }

    public static void changeReference(StringBuilder builder) {
        builder.append("World");
    }

    public static void swap(StringBuilder x, StringBuilder y) {
        System.out.println("交换前x指向的对象是:" + x.hashCode() );
        System.out.println("交换前y指向的对象是:" + y.hashCode() );
        StringBuilder temp = x;
        x = y;
        y = temp;
        System.out.println("交换后x指向的对象是:" + x.hashCode() ); // 输出x指向的对象,已经改变
        System.out.println("交换后y指向的对象是:" + y.hashCode() ); // 输出y指向的对象,已经改变
    }
}

输出内容:

10
HelloWorld
交换前sb指向的对象是:1435804085
交换前sb2指向的对象是:328638398
交换前x指向的对象是:1435804085
交换前y指向的对象是:328638398
交换后x指向的对象是:328638398
交换后y指向的对象是:1435804085
交换后sb指向的对象是:1435804085
交换后sb2指向的对象是:328638398
HelloWorld

引用变量,是存在栈中的,其指向的对象,是存在堆中的,引用变量的值其实就是指向的对象在堆中的具体的地址。java中没有指针,不能直接获取地址,但是可以通过hashCode看其指向的对象。如果两个引用类型变量的hashCode一样,说明其指向的就是同一个对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值