Java Strings: "String s = new String("silly");"
Asked Answered
H

23

88

I'm a C++ guy learning Java. I'm reading Effective Java and something confused me. It says never to write code like this:

String s = new String("silly");

Because it creates unnecessary String objects. But instead it should be written like this:

String s = "No longer silly";

Ok fine so far...However, given this class:

public final class CaseInsensitiveString {
    private String s;
    public CaseInsensitiveString(String s) {
        if (s == null) {
            throw new NullPointerException();
        }
        this.s = s;
    }
    :
    :
}

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
  1. Why is the first statement ok? Shouldn't it be

    CaseInsensitiveString cis = "Polish";

  2. How do I make CaseInsensitiveString behave like String so the above statement is OK (with and without extending String)? What is it about String that makes it OK to just be able to pass it a literal like that? From my understanding there is no "copy constructor" concept in Java?

Haughay answered 2/12, 2008 at 16:16 Comment(1)
String str1 = "foo"; String str2 = "foo"; Both str1 and str2 belongs to tha same String object, "foo", b'coz for Java manages Strings in StringPool, so is a new variable refers to the same String, it doesn't create another one rather assign the same alerady present in StringPool. But when we do this: String str1 = new String("foo"); String str2 = new String("foo"); Here both str1 and str2 belongs to different Objects, b'coz new String() forcefully create a new String Object.Florentinoflorenza
R
115

String is a special built-in class of the language. It is for the String class only in which you should avoid saying

String s = new String("Polish");

Because the literal "Polish" is already of type String, and you're creating an extra unnecessary object. For any other class, saying

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

is the correct (and only, in this case) thing to do.

Rolling answered 2/12, 2008 at 16:21 Comment(3)
a second point is that the compiler can to fancy internig/propagating-stuff with string literals which are not necessarily possible with such a weird functioncall like "String(literal)"Hospitality
Since you should never call new String("foo"), you may ask yourself why the constructor new String(String) exists. The answer is that there is sometimes a good use for it: https://mcmap.net/q/36962/-what-is-the-purpose-of-the-expression-quot-new-string-quot-in-javaDiaspore
FYI, Tetha’s comment above misspelled the word "interning", as in string interning.Extended
N
58

I believe the main benefit of using the literal form (ie, "foo" rather than new String("foo")) is that all String literals are 'interned' by the VM. In other words it is added to a pool such that any other code that creates the same string will use the pooled String rather than creating a new instance.

To illustrate, the following code will print true for the first line, but false for the second:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));
Nephew answered 2/12, 2008 at 16:26 Comment(7)
Similarly, that's why FindBugs tells you to replace "new Integer(N)" with "Integer.valueOf(N)" - because of that interning.Waltner
You should also add "foo" == new String("foo").intern()Vial
A correction: String literals are made to point to the same reference by the compiler, not the VM. The VM may intern String objects at runtime, so the second line may return true or false!Neufer
@Motlin: I'm not sure that's correct. The javadoc for the String class mandates that "All literal strings and string-valued constant expressions are interned". So we can rely on literals being interned, meaning that "foo" == "foo" should always return true.Nephew
@Nephew Yes, literals are interned, but by the compiler and not the VM. What Motlin is getting at is that the VM may additionally intern strings, thus, whether or not new String("bar") == new String("bar") --> false is implementation Dependant.Quaternary
@Aaron: java.sun.com/docs/books/jls/third_edition/html/… says: The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created. So it will always be false.Mondragon
Yes new String("") creates a new String object.Florentinoflorenza
L
30

Strings are treated a bit specially in java, they're immutable so it's safe for them to be handled by reference counting.

If you write

String s = "Polish";
String t = "Polish";

then s and t actually refer to the same object, and s==t will return true, for "==" for objects read "is the same object" (or can, anyway, I"m not sure if this is part of the actual language spec or simply a detail of the compiler implementation-so maybe it's not safe to rely on this) .

If you write

String s = new String("Polish");
String t = new String("Polish");

then s!=t (because you've explicitly created a new string) although s.equals(t) will return true (because string adds this behavior to equals).

The thing you want to write,

CaseInsensitiveString cis = "Polish";

can't work because you're thinking that the quotations are some sort of short-circuit constructor for your object, when in fact this only works for plain old java.lang.Strings.

Libratory answered 2/12, 2008 at 16:36 Comment(2)
+1 for mentioning immutability, which to me is the real reason in java one writes strA = strB, instead of strA = new String(strB). it really doesn't have to do much with string interning.Falter
They are not handled by reference counting. String pooling is required by the JLS.Stauder
P
20
String s1="foo";

literal will go in pool and s1 will refer.

String s2="foo";

this time it will check "foo" literal is already available in StringPool or not as now it exist so s2 will refer the same literal.

String s3=new String("foo");

"foo" literal will be created in StringPool first then through string arg constructor String Object will be created i.e "foo" in the heap due to object creation through new operator then s3 will refer it.

String s4=new String("foo");

same as s3

so System.out.println(s1==s2);// **true** due to literal comparison.

and System.out.println(s3==s4);// **false** due to object

comparison(s3 and s4 is created at different places in heap)

Plath answered 18/1, 2014 at 18:33 Comment(1)
Don't t use quote formatting for text that isn't quoted, and do use code formatting for code.Stauder
C
12

Strings are special in Java - they're immutable, and string constants are automatically turned into String objects.

There's no way for your SomeStringClass cis = "value" example to apply to any other class.

Nor can you extend String, because it's declared as final, meaning no sub-classing is allowed.

Comfy answered 2/12, 2008 at 16:19 Comment(0)
M
8

The best way to answer your question would be to make you familiar with the "String constant pool". In java string objects are immutable (i.e their values cannot be changed once they are initialized), so when editing a string object you end up creating a new edited string object wherease the old object just floats around in a special memory ares called the "string constant pool". creating a new string object by

String s = "Hello";

will only create a string object in the pool and the reference s will refer to it, but by using

String s = new String("Hello");

you create two string objects: one in the pool and the other in the heap. the reference will refer to the object in the heap.

Mandrill answered 10/5, 2012 at 5:33 Comment(0)
H
7

Java strings are interesting. It looks like the responses have covered some of the interesting points. Here are my two cents.

strings are immutable (you can never change them)

String x = "x";
x = "Y"; 
  • The first line will create a variable x which will contain the string value "x". The JVM will look in its pool of string values and see if "x" exists, if it does, it will point the variable x to it, if it does not exist, it will create it and then do the assignment
  • The second line will remove the reference to "x" and see if "Y" exists in the pool of string values. If it does exist, it will assign it, if it does not, it will create it first then assignment. As the string values are used or not, the memory space in the pool of string values will be reclaimed.

string comparisons are contingent on what you are comparing

String a1 = new String("A");

String a2 = new String("A");
  • a1 does not equal a2
  • a1 and a2 are object references
  • When string is explicitly declared, new instances are created and their references will not be the same.

I think you're on the wrong path with trying to use the caseinsensitive class. Leave the strings alone. What you really care about is how you display or compare the values. Use another class to format the string or to make comparisons.

i.e.

TextUtility.compare(string 1, string 2) 
TextUtility.compareIgnoreCase(string 1, string 2)
TextUtility.camelHump(string 1)

Since you are making up the class, you can make the compares do what you want - compare the text values.

Helicoid answered 2/12, 2008 at 17:15 Comment(1)
The compiler creates the string pool, not the JVM. Variables don't contain objects, they refer to them. String pool space for string literals is never reclaimed.Stauder
H
6

You can't. Things in double-quotes in Java are specially recognised by the compiler as Strings, and unfortunately you can't override this (or extend java.lang.String - it's declared final).

Hazardous answered 2/12, 2008 at 16:20 Comment(3)
final is a red herring here. Even if String wasn't final, extending String wouldn't help him in this case.Kristikristian
I think you mean fortunately you can't override this. :)Neufer
@Motlin: Ha! you may well be right. I think I read somewhere that Java was designed to stop anyone ever doing anything stupid by deliberately excluding anything interesting like this...Hazardous
A
4

- How do i make CaseInsensitiveString behave like String so the above statement is ok (with and w/out extending String)? What is it about String that makes it ok to just be able to pass it a literal like that? From my understanding there is no "copy constructor" concept in Java right?

Enough has been said from the first point. "Polish" is an string literal and cannot be assigned to the CaseInsentiviveString class.

Now about the second point

Although you can't create new literals, you can follow the first item of that book for a "similar" approach so the following statements are true:

    // Lets test the insensitiveness
    CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
    CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

    assert cis5 == cis6;
    assert cis5.equals(cis6);

Here's the code.

C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java
import java.util.Map;
import java.util.HashMap;

public final class CaseInsensitiveString  {


    private static final Map<String,CaseInsensitiveString> innerPool 
                                = new HashMap<String,CaseInsensitiveString>();

    private final String s;


    // Effective Java Item 1: Consider providing static factory methods instead of constructors
    public static CaseInsensitiveString valueOf( String s ) {

        if ( s == null ) {
            return null;
        }
        String value = s.toLowerCase();

        if ( !CaseInsensitiveString.innerPool.containsKey( value ) ) {
             CaseInsensitiveString.innerPool.put( value , new CaseInsensitiveString( value ) );
         }

         return CaseInsensitiveString.innerPool.get( value );   
    }

    // Class constructor: This creates a new instance each time it is invoked.
    public CaseInsensitiveString(String s){
        if (s == null) {
            throw new NullPointerException();
         }         
         this.s = s.toLowerCase();
    }

    public boolean equals( Object other ) {
         if ( other instanceof CaseInsensitiveString ) {
              CaseInsensitiveString otherInstance = ( CaseInsensitiveString ) other;
             return this.s.equals( otherInstance.s );
         }

         return false;
    }


    public int hashCode(){
         return this.s.hashCode();
    }

// Test the class using the "assert" keyword

    public static void main( String [] args ) {

        // Creating two different objects as in new String("Polish") == new String("Polish") is false
        CaseInsensitiveString cis1 = new CaseInsensitiveString("Polish");
        CaseInsensitiveString cis2 = new CaseInsensitiveString("Polish");

        // references cis1 and cis2 points to differents objects.
        // so the following is true
        assert cis1 !=  cis2;      // Yes they're different
        assert cis1.equals(cis2);  // Yes they're equals thanks to the equals method

        // Now let's try the valueOf idiom
        CaseInsensitiveString cis3 = CaseInsensitiveString.valueOf("Polish");
        CaseInsensitiveString cis4 = CaseInsensitiveString.valueOf("Polish");

        // References cis3 and cis4 points to same  object.
        // so the following is true
        assert cis3 == cis4;      // Yes they point to the same object
        assert cis3.equals(cis4); // and still equals.

        // Lets test the insensitiveness
        CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
        CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

        assert cis5 == cis6;
        assert cis5.equals(cis6);

        // Futhermore
        CaseInsensitiveString cis7 = CaseInsensitiveString.valueOf("SomethinG");
        CaseInsensitiveString cis8 = CaseInsensitiveString.valueOf("someThing");

        assert cis8 == cis5 && cis7 == cis6;
        assert cis7.equals(cis5) && cis6.equals(cis8);
    }

}

C:\oreyes\samples\java\insensitive>javac CaseInsensitiveString.java


C:\oreyes\samples\java\insensitive>java -ea CaseInsensitiveString

C:\oreyes\samples\java\insensitive>

That is, create an internal pool of CaseInsensitiveString objects, and return the corrensponding instance from there.

This way the "==" operator returns true for two objects references representing the same value.

This is useful when similar objects are used very frequently and creating cost is expensive.

The string class documentation states that the class uses an internal pool

The class is not complete, some interesting issues arises when we try to walk the contents of the object at implementing the CharSequence interface, but this code is good enough to show how that item in the Book could be applied.

It is important to notice that by using the internalPool object, the references are not released and thus not garbage collectible, and that may become an issue if a lot of objects are created.

It works for the String class because it is used intensively and the pool is constituted of "interned" object only.

It works well for the Boolean class too, because there are only two possible values.

And finally that's also the reason why valueOf(int) in class Integer is limited to -128 to 127 int values.

Achilles answered 3/12, 2008 at 5:24 Comment(0)
N
3

In your first example, you are creating a String, "silly" and then passing it as a parameter to another String's copy constructor, which makes a second String which is identical to the first. Since Java Strings are immutable (something that frequently stings people who are used to C strings), this is a needless waste of resources. You should instead use the second example because it skips several needless steps.

However, the String literal is not a CaseInsensitiveString so there you cannot do what you want in your last example. Furthermore, there is no way to overload a casting operator like you can in C++ so there is literally no way to do what you want. You must instead pass it in as a parameter to your class's constructor. Of course, I'd probably just use String.toLowerCase() and be done with it.

Also, your CaseInsensitiveString should implement the CharSequence interface as well as probably the Serializable and Comparable interfaces. Of course, if you implement Comparable, you should override equals() and hashCode() as well.

Navigation answered 2/12, 2008 at 16:29 Comment(0)
L
3

CaseInsensitiveString is not a String although it contains a String. A String literal e.g "example" can be only assigned to a String.

Losel answered 14/12, 2010 at 3:54 Comment(0)
D
2

CaseInsensitiveString and String are different objects. You can't do:

CaseInsensitiveString cis = "Polish";

because "Polish" is a String, not a CaseInsensitiveString. If String extended CaseInsensitiveString String then you'd be OK, but obviously it doesn't.

And don't worry about the construction here, you won't be making unecessary objects. If you look at the code of the constructor, all it's doing is storing a reference to the string you passed in. Nothing extra is being created.

In the String s = new String("foobar") case it's doing something different. You are first creating the literal string "foobar", then creating a copy of it by constructing a new string out of it. There's no need to create that copy.

Dakota answered 2/12, 2008 at 16:21 Comment(2)
Even if you extended String this wouldn't work. You'd need String to extend CaseInsensitiveString.Kristikristian
in either case its impossible, either because string is built in or because its declared finalThermodynamics
R
2

Just because you have the word String in your class, does not mean you get all the special features of the built-in String class.

Roi answered 4/5, 2010 at 21:8 Comment(0)
D
2

when they say to write

String s = "Silly";

instead of

String s = new String("Silly");

they mean it when creating a String object because both of the above statements create a String object but the new String() version creates two String objects: one in heap and the other in string constant pool. Hence using more memory.

But when you write

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

you are not creating a String instead you are creating an object of class CaseInsensitiveString. Hence you need to use the new operator.

Deucalion answered 19/5, 2012 at 4:27 Comment(0)
C
1

If I understood it correctly, your question means why we cannot create an object by directly assigning it a value, lets not restrict it to a Wrapper of String class in java.

To answer that I would just say, purely Object Oriented Programming languages have some constructs and it says, that all the literals when written alone can be directly transformed into an object of the given type.

That precisely means, if the interpreter sees 3 it will be converted into an Integer object because integer is the type defined for such literals.

If the interpreter sees any thing in single quotes like 'a' it will directly create an object of type character, you do not need to specify it as the language defines the default object of type character for it.

Similarly if the interpreter sees something in "" it will be considered as an object of its default type i.e. string. This is some native code working in the background.

Thanks to MIT video lecture course 6.00 where I got the hint for this answer.

Cottrill answered 24/5, 2012 at 18:12 Comment(0)
K
0

In Java the syntax "text" creates an instance of class java.lang.String. The assignment:

String foo = "text";

is a simple assignment, with no copy constructor necessary.

MyString bar = "text";

Is illegal whatever you do because the MyString class isn't either java.lang.String or a superclass of java.lang.String.

Kristikristian answered 2/12, 2008 at 16:22 Comment(0)
L
0

First, you can't make a class that extends from String, because String is a final class. And java manage Strings differently from other classes so only with String you can do

String s = "Polish";

But whit your class you have to invoke the constructor. So, that code is fine.

Laurinelaurita answered 2/12, 2008 at 16:23 Comment(0)
A
0

I would just add that Java has Copy constructors...

Well, that's an ordinary constructor with an object of same type as argument.

Ammonic answered 2/12, 2008 at 17:15 Comment(1)
It's a design pattern, not a language construct. With Java there are very few uses where a copy constructor would be interesting since everything is always "by reference", and each object just has one copy. In fact, making copies would really cause a LOT of problems.Neckband
N
0

String is one of the special classes in which you can create them without the new Sring part

it's the same as

int x = y;

or

char c;

Nameplate answered 14/7, 2009 at 21:0 Comment(0)
F
0
 String str1 = "foo"; 
 String str2 = "foo"; 

Both str1 and str2 belongs to tha same String object, "foo", b'coz Java manages Strings in StringPool, so if a new variable refers to the same String, it doesn't create another one rather assign the same alerady present in StringPool.

 String str1 = new String("foo"); 
 String str2 = new String("foo");

Here both str1 and str2 belongs to different Objects, b'coz new String() forcefully create a new String Object.

Florentinoflorenza answered 27/12, 2013 at 8:29 Comment(0)
H
-1

It is a basic law that Strings in java are immutable and case sensitive.

Hyperpituitarism answered 2/12, 2008 at 16:16 Comment(0)
R
-1

In most versions of the JDK the two versions will be the same:

String s = new String("silly");

String s = "No longer silly";

Because strings are immutable the compiler maintains a list of string constants and if you try to make a new one will first check to see if the string is already defined. If it is then a reference to the existing immutable string is returned.

To clarify - when you say "String s = " you are defining a new variable which takes up space on the stack - then whether you say "No longer silly" or new String("silly") exactly the same thing happens - a new constant string is compiled into your application and the reference points to that.

I dont see the distinction here. However for your own class, which is not immutable, this behaviour is irrelevant and you must call your constructor.

UPDATE: I was wrong! I tested this and realise that my understanding is wrong - new String("Silly") does indeed create a new string rather than reuse the existing one. I am unclear why this would be (what is the benefit?) but code speaks louder than words!

Royalroyalist answered 2/12, 2008 at 16:51 Comment(0)
F
-1

Java creates a String object for each string literal you use in your code. Any time "" is used, it is the same as calling new String().

Strings are complex data that just "act" like primitive data. String literals are actually objects even though we pretend they're primitive literals, like 6, 6.0, 'c', etc. So the String "literal" "text" returns a new String object with value char[] value = {'t','e','x','t}. Therefore, calling

new String("text"); 

is actually akin to calling

new String(new String(new char[]{'t','e','x','t'}));

Hopefully from here, you can see why your textbook considers this redundant.

For reference, here is the implementation of String: http://www.docjar.com/html/api/java/lang/String.java.html

It's a fun read and might inspire some insight. It's also great for beginners to read and try to understand, as the code demonstrates very professional and convention-compliant code.

Another good reference is the Java tutorial on Strings: http://docs.oracle.com/javase/tutorial/java/data/strings.html

Fission answered 15/6, 2015 at 22:29 Comment(1)
Any time "" is used it is a reference to the same string, in the constant pool.Stauder

© 2022 - 2024 — McMap. All rights reserved.