Java 传递对对象的引用

Java 传递对对象的引用,java,Java,我正在检查是否可以使用canForm方法在此boggle板上形成指定的单词。电路板上有一个图形字段,指示相邻的磁贴。我做了一个DFS,并将answer设置为true,如果单词可以组成 我理解为什么下面的代码不起作用:answer是一个原语,它的值在每次递归时都被复制,并且初始的answer(在public方法中)保持为false 例如,如果我将boolean answer更改为Set answer=new HashSet(),则在递归中传递对集合的引用,最终添加成功形成的单词并测试其是否为空,它

我正在检查是否可以使用
canForm
方法在此boggle板上形成指定的单词。电路板上有一个
图形
字段,指示相邻的磁贴。我做了一个DFS,并将
answer
设置为
true
,如果单词可以组成

我理解为什么下面的代码不起作用:
answer
是一个原语,它的值在每次递归时都被复制,并且初始的
answer
(在public方法中)保持为false

例如,如果我将
boolean answer
更改为
Set answer=new HashSet()
,则在递归中传递对集合的引用,最终添加成功形成的单词并测试其是否为空,它就可以工作了

但是,如果我简单地声明
Boolean answer=new Boolean(false)
并传递这个容器,为什么它不起作用呢?它将引用正确地传递给对象,但它会在赋值时神秘地更改引用
answer=true
(通过调试器可以看到),并且初始
answer
不会重置。我不明白

public boolean canForm(String word) {

    boolean answer = false;
    int n = M * N;
    char initial = word.charAt(0);

    // for each tile that is the first letter of word
    for (int u = 0; u < n; u++) {
        char c = getLetter(u / N, u % N);
        if (c == initial) {
            boolean[] marked = new boolean[n];
            marked[u] = true;
            canForm(u, word, 1, marked, answer);
        }
    }

    return !answer;
}

private void canForm(int u, String word, int d, boolean[] marked, boolean answer) {

    if (word.length() == d) {
        answer = true;
        return;
    }

    for (int v : graph.adj(u)) {
        char c = getLetter(v / N, v % N);
        if (c == word.charAt(d) && !marked[v]) {
            marked[v] = true;
            canForm(v, word, d + 1, marked, answer);
        }
    }
}
public boolean canForm(字符串字){
布尔答案=假;
int n=M*n;
字符首字母=word.charAt(0);
//对于单词的第一个字母的每个瓷砖
对于(int u=0;u
啊,你在用Java。这非常重要

Java完全是一种传递值语言。因此,当您调用canForm(int,String,int,boolean[],boolean)时,您正在做两件事:

  • 您正在canForm方法的范围内创建5个新变量
  • 您正在使用调用站点中的值初始化它们
更改您创建的新变量的值不会对调用站点上这些变量的值产生任何影响。方法调用结束时,您所做的任何重新分配都将丢失,并且不会影响调用站点上的值

但是,对于数组或对象,传递的“值”实际上是对对象的引用。这可能有点令人困惑,但这就像调用者和方法都有自己的共享邮箱的个人密钥一样。任何一方都可能丢失其密钥,或者用其他邮箱的密钥替换该密钥,而不会影响另一方访问该邮箱的能力

因此,该方法可以改变引用的值(
marked=newboolean[]
),而不改变调用方的引用。但是,如果该方法更改了引用结构中的内容(
标记为[0]=false
),则调用者将看到该方法。这就像该方法打开了共享邮箱并更改了里面的邮件一样。无论使用哪个键打开,都会看到相同的更改状态

伟大的分析:

一般而言:

  • 如果要从方法返回值,则该值应为该方法的返回值。这使得代码更容易理解,不太可能有副作用,并且可能使编译器更容易优化
  • 如果要返回两个值,请创建一个对象来保存这两个值(在许多语言和框架中都有一个称为“Tuple”的通用类型安全容器)
  • 如果确实需要在函数调用之间移动状态(通常不需要),请使用对象将其包装起来,以获得与按引用传递语义等价的语义。这就是将结果添加到共享集时所做的基本操作。请注意:带有副作用的编程,无论是共享对象还是全局状态,都容易出现缺陷。当你传递一个可变的物体时,很难在你的头脑中保留所有潜在的变化机制。如果只能通过返回值来完成作业,则应尝试这样做。有些人可能会称之为“功能[编程]风格”
  • 当您给两个具有不同意图的方法赋予相同的名称时,这会造成混淆,读者和某些情况下的编译器都会感到混淆。具体点。我们没有角色用完的危险
  • 最后,您可能希望了解尾部递归。由于这个循环,我相信这个实现可能是一个等待发生的堆栈溢出——只要给它一个比堆栈深度更长的字符串

    • 啊,你在用Java。这非常重要

      Java完全是一种传递值语言。因此,当您调用canForm(int,String,int,boolean[],boolean)时,您正在做两件事:

      • 您正在canForm方法的范围内创建5个新变量
      • 您正在使用调用站点中的值初始化它们
      更改您创建的新变量的值不会对调用站点上这些变量的值产生任何影响。方法调用结束时,您所做的任何重新分配都将丢失,并且不会影响调用站点上的值

      但是,对于数组或对象,传递的“值”实际上是对对象的引用。这可能有点令人困惑,但这就像调用者和方法都有自己的共享邮箱的个人密钥一样。任何一方都可能丢失其密钥,或者用其他邮箱的密钥替换该密钥,而不会影响另一方访问该邮箱的能力

      因此,该方法可以在不更改调用的情况下更改引用的值(
      marked=new boolean[]