Noob question - StringBuffer

F

fxtrad

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.
 
D

deepak.vaswani

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
 
G

Gordon Beaton

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".

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

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 --> <-- y

Calling x.append() doesn't change x, it changes the object pointed to
by x (and a):

a --> [AB] <-- x
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 -->

Note that both x and y are lost after returning. This is what's left:

a --> [AB]
b -->

/gordon

--
 
F

fxtrad

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".

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

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 --> <-- y

Calling x.append() doesn't change x, it changes the object pointed to
by x (and a):

a --> [AB] <-- x
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 -->

Note that both x and y are lost after returning. This is what's left:

a --> [AB]
b -->

/gordon

--







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, ], 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);
}
}
 
G

Gordon Beaton

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

--
 
L

Lew

Gordon said:
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.
 
P

Patricia Shanahan

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".

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
 
M

Mark Space

I tried to re-create the behavior with the simple implementation, and
I got the same results: [A, ], 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.
 
F

fxtrad

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.


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],



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

Lew

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
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:
Simple simple = new Simple(inner.toString());
return simple;
}catch (Exception e){
e.printStackTrace();
return this;
}
}
}

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().
 
P

Patricia Shanahan

Lew said:
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.

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
 
S

Stefan Ram

Patricia Shanahan said:
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.
 
P

Patricia Shanahan

Stefan said:
References are needed anyways for self-references or circular
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
 
L

Lew

Stefan said:
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.
 
F

fxtrad

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!
 
L

Lew

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,585
Members
45,081
Latest member
AnyaMerry

Latest Threads

Top