记一次Java参数传递时值发生变化问题

本文通过实例探讨了Java参数传递时值变化的问题,分析了值传递和引用传递的概念,以及clone方法在不同数组类型中的应用。针对数组在方法调用中值被改变的情况,提出了深拷贝解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0.前景回顾

代码内容如下:

private boolean dfs(char[][] board, int[][] k, String word, int index, int i, int j) {
    if (index >= word.length()) {
        return true;
    }
    if (i >= board.length || i < 0 || j < 0 || j >= board[0].length) {
        return false;
    }
    // 已使用
    if (k[i][j] == 1 || board[i][j] != word.charAt(index)) {
        return false;
    }
  	// 问题行
    k[i][j] = 1;
    return dfs(board, k, word, index + 1,i - 1, j)
            || dfs(board, k, word, index + 1,i + 1, j)
            || dfs(board, k, word, index + 1,i, j - 1)
            || dfs(board, k, word, index + 1,i, j + 1);
}

上面中所标识的问题行代码主要是想让k数组所在的地方进行标识,然后以相同的状态传递给后续调用的四次dfs方法。

但是,在实际的运行过程中发现最后一次dfs调用时k数组中的值和一开始不一样!!!!!

看见这种现象立马想到的是参数传递问题,子方法的调用将父方法中的值修改了。后面就在方法的调用上将k改为了k.clone(),发现还是不行(之前明明记得clone返回的不会影响原对象)。

1. 值传递 or 引用传递?

这两个概念在用C写代码的时候很常见,但是Java到底是属于哪种呢?

  • 值传递:通俗的讲就是将变量具体的值拿来传递给方法,也可以认为形参是实参的一个副本;
  • 引用传递:传递的是变量的地址,操作形参相当于操作实参一样。

拓展:形参和实参

形参:方法定义中的参数,像最上面例子中board,k,i这些都是形参;

实参:在调用方法时实际传入的。

第一个例子

@Test
public void test() {
    int x = 1;
    System.out.println("main: " + x);
    change(x);
    System.out.println("after change main: " + x);
}

public void change(int x) {
    x = 10;
    System.out.println("change : " + x);
}

结果:

main: 1
change : 10
after change main: 1

可以看到change方法中将参数的值修改成了10,主方法中的值并没有发生变化。

接着将参数类型改为变量:

@Test
public void test() {
    User user = new User(1, "John");
    System.out.println("before change : " + user);
    change(user);
    System.out.println("after change : " + user);

}

public void change(User user) {
    // user = new User(user.id, user.name);
    user.setName("April");
    System.out.println("changer : " + user);

}

返回:

before change : User{id=1, name=‘John’}
changer : User{id=1, name=‘April’}
after change : User{id=1, name=‘April’}

这里的值被change给修改了,难道就是引用传递?

将上段代码中注释掉的行取消注释;

返回

before change : User{id=1, name=‘John’}
changer : User{id=1, name=‘April’}
after change : User{id=1, name=‘John’}

关键来了。。。

如果说对象用的是引用传递,那么在change方法中重新new了一个新的对象(值还是原来的),然后修改新对象的值,主方法中打印出来的对象属性应该和change方法中的一致。

但是,上面明明也可以修改原对象的值,这又是为什么?

可以这么认为,在传递参数的时候,是用原对象的副本(指向实际的地址)来传递,在函数中是可以执行修改对应实际地址中的值,所以主方法中会同样感知到修改。

在这里插入图片描述

但是如果将方法中的参数指向新的地址,比如new 一个新的对象或者将别的对象重新赋值给参数,这样形参和实参指向的就不是同一个地址空间,任何修改都不会引起原对象的变化。

在这里插入图片描述

2.clone()

具体clone的内容这里不作分析,主要看看最上面问题所反映的现象。

下面是相关两个场景的测试用例:

@Test
public void cp1() {
    int[] array = new int[]{1,2,3};
    System.out.println("before :" + Arrays.toString(array));
    int[] cloneArray = array.clone();
    cloneArray[0] = 0;
    System.out.println("after :" + Arrays.toString(array));
}

返回

before :[1, 2, 3]
after :[1, 2, 3]

@Test
public void cp2() {
    int[][] array = new int[][]{{1,2,3}};
    System.out.println("before :" + Arrays.toString(array[0]));
    int[][] cloneArray = array.clone();
    cloneArray[0][0] = 0;
    System.out.println("after :" + Arrays.toString(array[0]));
}

返回

before :[1, 2, 3]
after :[0, 2, 3]

上面两个例子中唯一区别的就是实际参与的对象一个是一维数组,一个是二维数组。从结果中可以看出,一维数组clone出来的对象修改不会影响到原对象,而二维数组会影响。

clone是浅拷贝的,对于多维数组而言,每个数组元素也是一个数组对象,当利用clone进行拷贝的时候是不会对数组中的对象进行拷贝,所以会影响原对象中值。对于多维数组要达到副本的效果就需要对每个数组中的元素(数组)再进行一次clone。

3. 解决思路

目标效果

  • 将数组传递给子方法后,子方法调用结束,原数组中的值不变。

解决方法

  • 每次传递给方法的是数组深拷贝的副本;
  • 在方法中修改完数组中的值后,在方法结束之前恢复原来的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值