Noob question - StringBuffer

Discussion in 'Java' started by fxtrad@gmail.com, Jan 8, 2008.

  1. Guest

    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.
     
    , Jan 8, 2008
    #1
    1. Advertising

  2. Guest

    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
     
    , Jan 8, 2008
    #2
    1. Advertising

  3. On Tue, 8 Jan 2008 00:49:26 -0800 (PST), wrote:
    > 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 Beaton, Jan 8, 2008
    #3
  4. Guest

    On Jan 8, 4:53 am, Gordon Beaton <> wrote:
    > On Tue, 8 Jan 2008 00:49:26 -0800 (PST), wrote:
    > > 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);
    }
    }
     
    , Jan 8, 2008
    #4
  5. On Tue, 8 Jan 2008 03:08:35 -0800 (PST), wrote:
    > 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

    --
     
    Gordon Beaton, Jan 8, 2008
    #5
  6. Lew Guest

    Gordon Beaton wrote:
    > On Tue, 8 Jan 2008 03:08:35 -0800 (PST), wrote:
    >> 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.


    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.

    --
    Lew
     
    Lew, Jan 8, 2008
    #6
  7. wrote:
    > 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
     
    Patricia Shanahan, Jan 8, 2008
    #7
  8. Roedy Green Guest

    Roedy Green, Jan 8, 2008
    #8
  9. Mark Space Guest

    wrote:

    > 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.
     
    Mark Space, Jan 8, 2008
    #9
  10. Guest

    On Jan 8, 9:08 am, Lew <> wrote:
    > Gordon Beaton wrote:
    > > On Tue, 8 Jan 2008 03:08:35 -0800 (PST), wrote:
    > >> 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.

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



    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);
    }
    }
     
    , Jan 9, 2008
    #10
  11. Lew Guest

    wrote:
    > 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:
    > 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().

    --
    Lew
     
    Lew, Jan 9, 2008
    #11
  12. Lew wrote:
    > wrote:
    >> 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.


    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
     
    Patricia Shanahan, Jan 9, 2008
    #12
  13. Stefan Ram Guest

    Patricia Shanahan <> writes:
    >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.
     
    Stefan Ram, Jan 9, 2008
    #13
  14. Stefan Ram wrote:
    > Patricia Shanahan <> writes:
    >> 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.


    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
     
    Patricia Shanahan, Jan 9, 2008
    #14
  15. Lew Guest

    Stefan Ram wrote:
    > 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.

    --
    Lew
     
    Lew, Jan 9, 2008
    #15
  16. Guest

    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!
     
    , Jan 9, 2008
    #16
  17. Lew Guest

    wrote:
    > 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.

    --
    Lew
     
    Lew, Jan 9, 2008
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mike
    Replies:
    3
    Views:
    1,854
  2. qazmlp
    Replies:
    5
    Views:
    720
    Chris Smith
    Feb 19, 2004
  3. John Galt

    hash for String and StringBuffer

    John Galt, Feb 24, 2004, in forum: Java
    Replies:
    4
    Views:
    552
    Doug Pardee
    Feb 25, 2004
  4. Darren
    Replies:
    5
    Views:
    4,474
    Darren
    Jul 28, 2004
  5. Shawn
    Replies:
    15
    Views:
    1,011
    Shawn
    Dec 14, 2006
Loading...

Share This Page