Java Forum / General / January 2008
Noob question - StringBuffer
fxtrad@gmail.com - 08 Jan 2008 08:49 GMT Hi Folks,
in the following example:
public class Test { static void operate (StringBuffer x, StringBuffer y) { x.append(y); y = x; }
public static void main (String [] args) { StringBuffer a = new StringBuffer ("A"); StringBuffer b = new StringBuffer ("B"); operate (a,b); System.out.println(a + "," +b); } }
output is: AB,B
I thought arguments in java are always passed "by value". Meaning that copies of objects "a" and "b" would be passed to the method "operate".
But in the above example, while object "b" is behaving as expected (since it didn't change after being passed to "operate"), object "a" have changed - which is confusing me. it's as if "a" was passed by reference, while "b" was passed by value. Am I missing something here?
Thanks for your feedback.
deepak.vaswani@gmail.com - 08 Jan 2008 09:34 GMT public class StringBuff { static void operate(StringBuffer x,StringBuffer y) { x.append(y); y=x; }
public static void main(String[] args) { StringBuffer a = new StringBuffer("A"); StringBuffer b = new StringBuffer("B"); System.out.println(a + "," +b); } }
Yes . We will get the Output as AB,B Reason - This is StringBuffer and we are passing the object of it . So any changes in it will reflected in the next statement .
But if we are using String instead of StringBuffer . firstly - we dont have append method in the String Class So we have to use this
x+=y; // instead of append method y=x;
Then we will get the answer A,B
Gordon Beaton - 08 Jan 2008 09:53 GMT > Hi Folks, > [quoted text clipped - 21 lines] > I thought arguments in java are always passed "by value". Meaning that > copies of objects "a" and "b" would be passed to the method "operate". The arguments are indeed passed by value. But the value of an object reference is (something like) the address of the object referred to, not the value of the object itself.
Before the call, a and b (both references) "point to" their corresponding objects, something like this:
a --> [A] b --> [B]
Inside operate(), x and y (also references) get their values from a and b. As a result they point to the same objects as a and b, something like this:
a --> [A] <-- x b --> [B] <-- y
Calling x.append() doesn't change x, it changes the object pointed to by x (and a):
a --> [AB] <-- x b --> [B] <-- y
But assigning to y doesn't change the object pointed to by y (and b), it changes y itself, which now points to same object as x and a:
a --> [AB] <-- x ^-- y b --> [B]
Note that both x and y are lost after returning. This is what's left:
a --> [AB] b --> [B]
/gordon
--
fxtrad@gmail.com - 08 Jan 2008 11:08 GMT > > Hi Folks, > [quoted text clipped - 60 lines] > > -- Gordon,
Thank you. Nicely said and illustrated (gotta love those ascii illustrations :)
I suspected this was the case - like you said, and if I understood correctly, the arguments being passed were copies - but they were "shallow" copies and the embedded references weren't replicated, and hence the behavior that confused me. I had expected a major utility class like StringBuffer to override the default clone() with a meaningful "deep" implementation, but that's obviously not the case.
I tried to re-create the behavior with the simple implementation, and I got the same results: [A, [B]],[B] which is now (gladly) the expected behavior:
import java.util.*;
class Simple { private Vector<Object> inner; public Simple(String str) { inner = new Vector(); inner.add(str); } public void addInner(Simple s) {inner.add(s.toString());} public String toString() {return inner.toString();} }
public class HelloWorld { static void operate(Simple x, Simple y) { x.addInner(y); y = x; } public static void main(String[] args) { Simple a = new Simple("A"); Simple b = new Simple("B"); operate(a,b); System.out.println(a+","+b); } }
Gordon Beaton - 08 Jan 2008 12:06 GMT > I suspected this was the case - like you said, and if I understood > correctly, the arguments being passed were copies - but they were > "shallow" copies and the embedded references weren't replicated, and > hence the behavior that confused me. I had expected a major utility > class like StringBuffer to override the default clone() with a > meaningful "deep" implementation, but that's obviously not the case. The object itself is not involved when the reference is copied or assigned; in this respect references behave just like primitives.
/gordon
--
Lew - 08 Jan 2008 14:08 GMT >> I suspected this was the case - like you said, and if I understood >> correctly, the arguments being passed were copies - but they were [quoted text clipped - 5 lines] > The object itself is not involved when the reference is copied or > assigned; in this respect references behave just like primitives. In other words, there's no question of clone()ing or copying. The clone() method has absolutely nothing to do with parameter passing. Nothing.
Java is "pass by value", but it's the value of the *reference* that's passed, not the value of the object itself.
 Signature Lew
fxtrad@gmail.com - 09 Jan 2008 01:52 GMT > >> I suspected this was the case - like you said, and if I understood > >> correctly, the arguments being passed were copies - but they were [quoted text clipped - 14 lines] > -- > Lew You're right Lew. I realize now that clone() has nothing to do with the previous discussion.
For some reason I was under the impression that the JVM calls clone() automatically when an assignment (=) is made or an object is passed as argument. I tried overriding the default clone() and realized it wasn't automatically called in either case.
Another misconception corrected :)
So it seems to me that the only way we can pass a true deep-copy of an object as an argument, is to override the clone() method and EXPLICITLY invoke it like so: operate((Simple)a.clone(), (Simple)b.clone());
The following works fine as a deep-copy is used in argument, and the resulting:
[A],[B]
import java.util.*;
class Simple implements Cloneable { private Vector<Object> inner; public Simple(String str) { inner = new Vector(); inner.add(str); } public void addInner(Simple s) {inner.add(s.toString());} public String toString() {return inner.toString();} public Object clone() { try{ //performing the "deep" copying here: Simple simple = new Simple(inner.toString()); return simple; }catch (Exception e){ e.printStackTrace(); return this; } } }
public class Test { static void operate(Simple x, Simple y) { x.addInner(y); y = x; } public static void main(String[] args) { Simple a = new Simple("A"); Simple b = new Simple("B"); operate((Simple)a.clone(),(Simple)b.clone()); System.out.println(a+","+b); } }
Lew - 09 Jan 2008 02:43 GMT > So it seems to me that the only way we can pass a true deep-copy of an > object as an argument, is to override the clone() method and > EXPLICITLY invoke it like so: operate((Simple)a.clone(), > (Simple)b.clone()); I thought your question was about pass-by-value vs. pass-by-reference for method arguments. Now you're talking about deep cloning, which is an entirely separate topic and doesn't really cross over.
This is mildly confusing to me.
> class Simple implements Cloneable How come the class isn't public? It doesn't really matter, I'm just curious.
> { > private Vector<Object> inner; Are you sure you need Vector? Why not ArrayList?
You should reconsider your generic argument - you apparently want some sort of List <String>, so why did you declare it as <Object>?
> public Simple(String str) > { > inner = new Vector(); You shouldn't mix generics and raw types. Didn't the compiler issue you a warning?
> inner.add(str); > } > public void addInner(Simple s) {inner.add(s.toString());} You should indent more conventionally. This is an unusual idiom - having an instance that holds Strings hold its own String representation, which in turn
> public String toString() {return inner.toString();} comes from that very holder, using the Java default Object.toString(), which returns an obscure String containing hex digits and other stuff.
What is the ultimate point of all that circularity?
> public Object clone() > { try{ //performing the "deep" copying here: [quoted text clipped - 6 lines] > } > } This clone() method does not return anything like a copy of the original object, and therefore doesn't live up to the purpose of clone().
 Signature Lew
Patricia Shanahan - 09 Jan 2008 03:05 GMT >> So it seems to me that the only way we can pass a true deep-copy of an >> object as an argument, is to override the clone() method and [quoted text clipped - 6 lines] > > This is mildly confusing to me. Deep cloning would be a way of simulating pass-by-value for Java objects, taking into account the fact that a Java object never contains another object, only a reference to it.
My question is "Why?".
Patricia
Stefan Ram - 09 Jan 2008 03:19 GMT >a Java object never contains >another object, only a reference to it. >My question is "Why?". References are needed anyways for self-references or circular references. Therefore, aggregation could not replace references, but only be added to references.
If aggregation would be added, the programmer always would have to decide what to use. This would have made learning and using the language more difficult.
A kind of composition is inheritance. By
class A { int a; int b; } class B extends A { int c; }
an »object of class A« is part of every object of class B.
Patricia Shanahan - 09 Jan 2008 03:23 GMT >> a Java object never contains >> another object, only a reference to it. [quoted text clipped - 3 lines] > references. Therefore, aggregation could not replace > references, but only be added to references. Looking back, my comment was a bit ambiguous, and the deletion of the start of the sentence resolved the ambiguity the wrong way. I meant "Why try so hard to do pass-value of objects in a pointer-based language?" not "Why pointers rather than composition?".
Patricia
fxtrad@gmail.com - 09 Jan 2008 05:07 GMT I do not agree that copying (cloning) is unrelated to the original topic. When the subject of passing by value comes up, the question of shallow vs. deep copying follows.
In response to "why": I'm simply just trying to learn the language. While this discussion might or might not have practical applications, I needed to clear it in my head so I can move on.
Thanks for everybody's help!
Lew - 09 Jan 2008 06:01 GMT > I do not agree that copying (cloning) is unrelated to the original > topic. When the subject of passing by value comes up, the question of > shallow vs. deep copying follows. Got ya.
To copy an object for use as a method argument in Java is not inherent. The method can access the referenced object via the pointer argument without resort to any copying.
This is fine when you want that. For example, Strings and many other types have immutable objects. You can safely pass a String reference to a method without fear that the method will modify the String contents. Here the question of shallow vs. deep copy never emerges.
When the object is mutable and the caller intends for the method to change it, then shallow vs. deep copy is not a question. The method just works directly on the object through the argument.
When the object is mutable but the caller wishes for it not to change, then one of two scenarios must prevail. Either the method must promise not to change the object, which might require it to make a copy, or the caller must make a copy to pass to the method. Now the question of shallow vs. deep copy arises.
 Signature Lew
Lew - 09 Jan 2008 04:47 GMT > A kind of composition is inheritance. By > > class A { int a; int b; } > class B extends A { int c; } > > an »object of class A« is part of every object of class B. Sort of. There isn't actually a separate "object of class A" that is a part of class B, but there is a core part of B that expresses its "A-ness", so the analogy holds to a point.
However,
> A kind of composition is inheritance. I see your point, but the technical term "composition" is used in contradistinction to "inheritance", as in "Item 14: Favor composition over inheritance" in Joshua Bloch's seminal /Effective Java/.
"Composition" refers to the literal containment of an object reference as a member, which inheritance doesn't actually do. Inheritance models the 'is-a' relation, where the child-type object literally is an instance of the parent type. Composition models the 'has-a' relation, where the composing class instance actually has an attribute of the composed type.
 Signature Lew
Mark Space - 08 Jan 2008 18:08 GMT > I tried to re-create the behavior with the simple implementation, and > I got the same results: [A, [B]],[B] which is now (gladly) the > expected behavior: So here's an exercise for you, which I'm too lazy to write up. Can you get your previous (pass-by value) expected results with primitives? And can you then duplicate that with objects? You should be able too.
Patricia Shanahan - 08 Jan 2008 15:32 GMT > Hi Folks, > [quoted text clipped - 21 lines] > I thought arguments in java are always passed "by value". Meaning that > copies of objects "a" and "b" would be passed to the method "operate". Copies of the references "a" and "b" were passed by value to "operate". Any Java expression is one of:
A primitive, such as an int.
A null reference that does not point to anything.
A non-null reference that is a pointer to some object whose class is appropriate for the type of the reference.
Your actual parameters are non-null references that point to the two StringBuffer objects that main created.
> But in the above example, while object "b" is behaving as expected > (since it didn't change after being passed to "operate"), object "a" > have changed - which is confusing me. it's as if "a" was passed by > reference, while "b" was passed by value. Am I missing something here? y = x is an operation on the actual parameters, the references that were passed by value. x.append(y) is a call to a method in the object referenced by x. Because x has the same value as a, x points to the same StringBuffer as a.
Making a clear distinction between references and the objects they point to is the key to understanding Java parameter passing. With that understanding, it is simple pass by value.
Patricia
Roedy Green - 08 Jan 2008 16:32 GMT >I thought arguments in java are always passed "by value". Meaning that >copies of objects "a" and "b" would be passed to the method "operate". no. a copy of the reference is passed.
See http://mindprod.com/jgloss/callbyvalue.html http://mindprod.com/jgloss/callbyreference.html
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|