-1

Possible Duplicate:
Is Java pass by reference?

see example below... need java.io library to run...

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;
}

}
Community
  • 1
  • 1
Steven
  • 29
  • 1
  • 1
  • 6

3 Answers3

4

Java always passes parameters by value - but in the case of classes/objects, the value that's passed is a reference, not an object itself.

What the type involved, the value of the argument expression is copied as the initial value of the parameter. Changes to the parameter variable itself are not seen by the caller, whereas changes to the object that the reference refers to will be seen.

For example, using StringBuilder (which is a mutable type):

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

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

Now:

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"

Note that when I say "the value of the argument expression", that is never an object - it's either a primitive value, or a reference.

I like to think of an analogy with houses. Suppose you have a piece of paper (a variable) with directions to a house written on it. You call a method and use that variable as the argument - that creates a new piece of paper (the parameter) with the same directions on. If the method crosses out the original directions and replaces them with some other ones, that doesn't change the first piece of paper. On the other hand, if the method follows the directions and then paints the house red, then you would see that change if you followed the directions on the first piece of paper.

EDIT: To explain your original code... no objects are being copied, but the value of name in run is being copied into fixName. You're then changing the value of the parameter in fixName when you write this:

name = name.substring(1);

You're changing it again when you write:

name = first + name.toLowerCase();

Neither of these have changed the value of name in the calling code, which is still referring to the original string.

You're then returning the new string reference here:

return name;

but your calling code is completely ignoring it, because you've just written:

fixName(name);

One way to demonstrate what's happened is to use the return value in a new variable:

String fixedName = fixName(name);

Then you could print out name (which would show the original string) and fixedName (which would show the new one).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I am still confused. Are somehow the changes that I've made to name undone when I return the changed name at the end of fixName? – Steven Mar 13 '11 at 12:01
  • @Steven: It's confusing because you've got two variables called `name` - the one in `run` and the one in `fixName`. The value of the variable in `run` *cannot be changed* within `fixName`. That's true even if you were using a mutable type, where you could change the contents of the *object*, but you still wouldn't be changing *which* object the original variable referred to. Now the next thing to note is that although you're *returning* a value from `fixName`, you're not actually *using* that return value in the calling code. – Jon Skeet Mar 13 '11 at 12:04
  • Okay this makes sense, thanks. Because I'm within a method, I will never be able to change primitive types or objects that were declared outside of that method. That said, it kind of makes it difficult to understand why it's important to understand that name was passed by reference. Seems that it's just as good as being copied within the method. The other weird thing is that even when I make it a private instance variable (declared outside run) the value still won't persist. To be clear, I know how to get the program to do what I want it to do, I just want to understand what java is doing – Steven Mar 13 '11 at 12:14
  • @Steven: You can make a change to the *object*, but not to the *variable*. It's incredibly important that you understand the difference... and that `name` *isn't* passed by reference. It's passed by value - but the value is a **reference**, not an **object**. The value of a variable is *never* an object. – Jon Skeet Mar 13 '11 at 12:17
  • @Jon You're blowing my mind. Let's say 'name' is assigned the value **"Steven"** and I write a method passing name. So "Steven" is not getting passed. Rather, the location where 'name' is stored is passed. And then the method creates a new object named 'name' and assigns the value given from the reference **"Steven"**.... I read your article on parameter passing but may need to give it another read in the morning as it's late in nz right now. Thanks for indulging my curiosity. – Steven Mar 13 '11 at 12:33
  • 1
    @Steven: Well, the value of `name` is passed, which is "a way of getting to the string object". It doesn't have to be the actual address, but that's a simple implementation. You're then talking about creating a new *object* named `name` - get rid of that association. Objects don't have names. Variables have names and values; the values can be references to objects. Two variables with different names can have the same value, i.e. refer to the same object. I'm being very pedantic, but it's really important to get the concepts properly separated in your head. – Jon Skeet Mar 13 '11 at 12:39
2

you pass a reference, so you work with the same string, BUT you return another string, because String in java is immutable - every operation (such as subString) produce new string and if you want to perform many operations on string (such as substring, replace etc.) use a StringBuffer or StringBuilder

Max Komarychev
  • 2,836
  • 1
  • 21
  • 32
  • True - I pass another string. But the string I return is the same [referenced] string I got in. But then when I print it, it seems like the original string is unaltered. – Steven Mar 13 '11 at 11:58
  • In you method there are 2 new String objects created first here `name = name.substring(1)` (substring) and second here `name = first + name.toLowerCase();` (concatenation) – Max Komarychev Mar 13 '11 at 12:01
  • when you write `name = name.subString(1)` you NOT change the outer String, you just make name referenced to another object (inside your method) – Max Komarychev Mar 13 '11 at 12:02
  • It seems when I make name a private instance variable then it's still the same issue. – Steven Mar 13 '11 at 12:05
  • you still pass `name` into methid like parameter ? – Max Komarychev Mar 13 '11 at 12:08
  • I say "outer" about string that created outside `fixName` method and then passed to it. I mean this `String name = readLine("Type a name: ");` is outer string – Max Komarychev Mar 13 '11 at 12:09
0

This does not really answer your question, but you should avoid assigning parameters (like 'name' in this case), it can be handy at times but it is generally considered a bad practice because it often leads to unreadable and hard to maintain code. In your case the variable is both a parameter and a local variable.

In Eclipse there is a warning you can activate for this in

Preferences->Java->Compiler->Errors/Warnings->Code style->Parameter assignment

I would recommend to set the parameter 'name' final in order to enforce this. Return another String that is based on your 'name' String and name it properly. The goal is that anyone reading your code should be able to quickly understand what is going on by elimination (the function is private, it is static, the parameter is final...). This excludes a lot of side effects. Search for the concept of 'pure functions' on the web. Make the method static so the person reading your code knows that there are no side effects on the instance. Here is the new version:

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;
}
Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85