关于Java中方法调用时参数的传递

本文通过两个具体示例详细解释了Java中方法参数传递的机制,即无论是基本数据类型还是对象引用,都采用传值的方式。并通过图示清晰展示了方法调用前后变量的变化情况。

1.问题说明

       在C++中,函数调用时有传值调用和传址调用两种方式,但在Java中只有传值调用一种方式。Java中的方法参数为那几种基本数据类型的情况跟C++中一样,传入的只是变量的拷贝。而当参数类型为类对象时,则有可能会误以为是传址调用。

2.测试用例

       现在此举出例子并加以说明 
public class AClass {
    
    int data;

}

       该类为作为测试用的类,里面只有一个域。

2.1第一个用例:

public class ParameterTest {

    public static void main(String[] args) {
        
        AClass a = new AClass();
        a.data = 10;
        test1(a);
        System.out.println(a.data);
    }
    
    public static void test1(AClass aClass) {
        aClass.data = 0;
    }

}

       执行过程:

       先构造一个新的实例,并为其赋值为10。再调用方法test1(),在该方法中将data的值设为0。然后程序又回到方法体外面,输出结果为0。

       在此看来,貌似方法调用时传的是地址,因为对data域的改动影响到了方法体外面。那么再来看下一个例子。

2.2第二个用例:

public class ParameterTest {

    public static void main(String[] args) {
        
        AClass a = new AClass();
        a.data = 10;
        test2(a);
        System.out.println(a.data);
    }
    
    public static void test2(AClass aClass) {
        AClass b = new AClass();
        b.data = 100;
        aClass = b;
    }

}

       此时同样构造一个新的实例,并将其数据域赋值为10,再调用方法test2()。与方法test1()不同的是,在该方法中又定义了一个新的实例(其值为100),然后使传入的参数指向该实例。该方法执行完毕后,输出的结果为10。看来在方法体中的操作出了方法后就没有作用了,这样的话又不像是在传址了。那是为什么呢?

3.运行过程说明

3.1 对于第一个测试用例

        构造实例并为其数据赋值后(如下图),产生一个指向实例的引用a:

       调用方法test1()时(如下图),a'为引用a的一个拷贝,它也指向当前的实例:

       当在test1()的方法体中修改数据域的值时(如下图):

     因为在方法体中,传入的引用的拷贝指向没有发生改变,所以它修改了数据域的值后会影响到原来的实例的值。

3.2 对于第二个测试用例:

     同样,构造实例并为其赋值后,再调用方法test2()。在方法体中传入的依然是引用的一个拷贝。如下图:

        在方法test2()中构造一个新的实例并为其赋值100后,如下图:

      在方法体中为传入的引用的拷贝再次赋值后,它指向了方法体中产生的实例b,如下图:

    经过上图,可以看出:在方法体中传入的引用的拷贝指向确实改变了,它指向了新构造的实例b。但是在方法体外面,原来的实例指向还是没变,所以这回的输出还是10。

4.小结

   在Java的方法调用中,方法中的参数是以传值的形式进行的,不管它是什么数据类型。如果是基本数据类型,则就是传入该值的一个拷贝;如果是类类型,则传入的是引用的一个拷贝。归根结底还是传的值。
### Java 方法调用参数传递机制详解 Java 中的方法调用涉及两种主要的参数传递方式:按值传递(Pass by Value)和按引用传递(Pass by Reference)。然而,在 Java 的设计哲学中,所有的参数都是通过 **值传递** 实现的。即使是对象类型的参数也不例外。 #### 值传递的概念 当一个基本数据类型(如 `int`、`double` 等)被作为参数传递方法,实际上传递方法的是该变量的一个副本[^1]。因此,任何对这个副本所做的修改都不会影响原始变量的值。例如: ```java public class PassByValueExample { public static void changePrimitive(int value) { value = 10; } public static void main(String[] args) { int num = 5; changePrimitive(num); System.out.println("After method call: " + num); // 输出仍然是 5 } } ``` 在这个例子中,尽管在 `changePrimitive` 方法内部改变了 `value` 的值,但主程序中的 `num` 并未受到影响[^4]。 --- #### 对象的值传递 对于对象类型的参数,情况略有不同。虽然 Java 使用的是值传递,但在这种情况下,“值”指的是对象的引用地址而不是对象本身。这意味着如果在一个方法中更改了对象的状态(比如修改其字段),这些变化会反映回原对象。但是,如果尝试重新分配一个新的对象给参数,则不会影响外部的对象实例。例如: ```java class Person { String name; public Person(String name) { this.name = name; } } public class PassObjectReference { public static void modifyPerson(Person person) { person.name = "John"; // 修改对象状态 } public static void reassignPerson(Person person) { person = new Person("Doe"); // 尝试重置引用 } public static void main(String[] args) { Person p = new Person("Alice"); modifyPerson(p); System.out.println("Name after modification: " + p.name); // John reassignPerson(p); System.out.println("Name after reassignment: " + p.name); // 仍然为 John } } ``` 这里的关键在于理解 `person` 是指向堆内存中某个位置的引用。即使我们可以在方法内操作它所指代的内容,也无法改变它的指向目标[^4]。 --- #### 方法递归调用参数传递的关系 除了普通的函数调用外,递归也是一种重要的编程技术。每次进入新的递归层,都会创建一组全新的局部变量拷贝[^2]。这进一步验证了无论是在常规还是递归场景下,Java 都严格遵循着基于值复制的原则来进行参数交换。 考虑下面的例子展示了如何利用栈帧保存每一轮迭代的信息直到满足终止条件为止: ```java public class RecursiveParameterTest { public static void recursiveMethod(int level){ if(level<=0){ return ; } System.out.println("Current Level:"+(level--)); recursiveMethod(level); } public static void main(String []args){ recursiveMethod(3); } } ``` 每当 `recursiveMethod()` 被再次调用候,当前级别的数值会被压入系统调用栈之中形成独立的一份记录直至达到基础情形才逐一释放并返回上级呼叫者那里继续执行剩余部分逻辑[^2]. --- ### 总结 综上所述,无论是基本数据类型还是复杂的数据结构,在 Java 编程语言里都采用统一的方式——即按照值来完成参数之间的交互行为。即便是看似涉及到所谓“引用”的场合也不过是对底层存储单元地址进行了相应的转移而已,并不意味着真正意义上的直接操纵源实体自身属性或者链接关系的变化[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值