创建方法并将对象作为参数传递时,它们是复制还是引用?

时间:2011-03-13 11:51:05

标签: java

  

可能重复:
  Is Java pass by reference?

参见下面的示例...需要运行java.io库...

public class BlankClass extends ConsoleProgram {

public void run() {
    while(true) {
         setFont("London-24");
         String name = readLine("Type a name: ");
         fixName(name);
         /* I know that the way this is written doesn't make sense and that println(fixName(name))
          * is the right way.  However, I thought that when objects then the method is using the object
          * (in this case a string) and not a copy of it. In other words, it is referenced.
          * So if it is referenced why isn't it printing out Steven when I give it STEVEN.
          */
         //println(fixName(name); this is removed to show the question.
           println(name);
    }
}

private String fixName(String name) {
    char first = name.charAt(0);
    first = Character.toUpperCase(first);
    name = name.substring(1);
    name = first + name.toLowerCase();
    return name;
}

}

3 个答案:

答案 0 :(得分:4)

Java 总是按值传递参数 - 但是对于类/对象,传递的值是引用,而不是对象本身。

涉及的类型是什么,参数表达式的值被复制为参数的初始值。参数变量本身的更改是调用者看不到的 ,而对参考引用的对象的更改将会被看到。

例如,使用StringBuilder(这是一种可变类型):

public void foo(StringBuilder builder)
{
    builder = new StringBuilder("Change to builder");
}

public void bar(StringBuilder builder)
{
    builder.append(" - appended");
}

现在:

StringBuilder x = new StringBuilder("Original value");
foo(x);
System.out.println(x); // Still prints "Original value"

StringBuilder y = new StringBuilder("Original value 2");
bar(y);
System.out.println(y); // Prints "Original value 2 - appended"

请注意,当我说“参数表达式的值”时,永远不会一个对象 - 它可以是原始值,也可以是引用。

我喜欢想到与房子类比。假设你有一张纸(一个变量),上面写着房子的方向。您调用一个方法并使用该变量作为参数 - 创建一个具有相同方向的新纸(参数)。如果该方法越过原始方向并将其替换为其他方向,则不会更改第一张纸张。另一方面,如果方法遵循指示,然后将房屋描绘成红色,那么如果您按照第一张纸上的指示, 会看到该更改。

编辑:要解释原始代码...没有复制对象,但name中的run正在复制到fixName。然后,当您编写此代码时,您正在更改fixName参数的值:

name = name.substring(1);

当你写下来时,你会再次改变它:

name = first + name.toLowerCase();

这些都没有改变调用代码中name的值,这仍然是指原始字符串。

然后你在这里返回新的字符串引用:

return name;

但是你的调用代码完全忽略了它,因为你刚刚写了:

fixName(name);

证明发生了什么的一种方法是在新变量中使用返回值:

String fixedName = fixName(name);

然后你可以打印name(显示原始字符串)和fixedName(显示新字符串)。

答案 1 :(得分:2)

你传递一个引用,所以你使用相同的字符串,但是你返回另一个字符串,因为java中的String是不可变的 - 每个操作(如subString)都会产生新的字符串,如果你想对字符串执行很多操作(如substring,replace等)使用StringBuffer或StringBuilder

答案 2 :(得分:0)

这并没有真正回答你的问题,但你应该避免分配参数(在这种情况下就像'名字'),它有时会很方便,但它通常被认为是一种不好的做法,因为它往往导致难以理解和难以维护代码。 在您的情况下,变量既是参数又是局部变量。

在Eclipse中,您可以在

中激活此警告
  

偏好设置 - > Java->编译器 - >错误/警告 - >代码样式 - >参数分配

我建议将参数'name'设置为final以强制执行此操作。 返回另一个基于您的'name'字符串的String并正确命名。 目标是阅读代码的任何人都应该能够通过消除快速了解正在发生的事情(函数是私有的,它是静态的,参数是最终的......)。这排除了很多副作用。 在网络上搜索“纯函数”的概念。使方法保持静态,以便读取代码的人知道实例没有副作用。 这是新版本:

private static String fixName(final String name) {
    final char firstCharOfName = Character.toUpperCase(name.charAt(0));
    final String fixedName = firstCharOfName + name.substring(1).toLowerCase();
    return fixedName;
}