Talk:Java Programming/API/java.lang.String
Add topicCompare Strings
[edit source]You can use == to compare equality of string references. It might be a good idea to explain the String Pool and limitation of using only ==, and then what the differences are between equals and compareTo. The main one being that equals will first do == to see if the string references are equivalent as this is extremely fast (compareTo probably does this to?), and if this fails, will then go on to do a lexical comparison, which is slow. Matt Oates
- Yes, someone please write an explanation, why I shouldn't just use == whenever I feel like it. CannibalSmith 12:04, 11 June 2007 (UTC)
- Well, "It is not really neccessary for a Java programmer to know byte code." - Strings are an exception. Strings are "==" if they are the same Reference. The Java Compiler will merge all optimize the constant pool and as a result all String literals with the same content will be merged into one constant pool entry. Newer versions of the JVM merge all constant String instances, so "Hello World" from class A is the same reference as "Hello World" from class B. BUT you can't use this magic if your Strings are created on the fly - e.g. by io/deserialisation or any kind of string manipulation. Let's see the Code
public class StringTest { public static String myString = "Hello World"; public static class A { public final static String myString = "Hello World"; } public static class B { public final static String myString = "Hello World"; } public static void main(String[] args) { // Same class, same constant pool if (myString == "Hello World") { System.out.println("References in the same class match"); } // Different classes, runtime string pool if (A.myString == B.myString) { System.out.println("References across classes match"); } // Manipulated Strings <-> No string pool String v = "Hello ".concat("World"); if (v != myString) { System.out.println(v); System.out.println(myString); System.out.println("--> See the difference - equals=" + v.equals(myString)); } } }
- Output on sun java 1.6.0_06-b02 for linux (other implementations can differ)
References in the same class match References across classes match Hello World Hello World --> See the difference - equals=true
- You can use javap to see what's going on
$ javap -c StringTest Compiled from "StringTest.java" public class StringTest extends java.lang.Object{ public static java.lang.String myString; static {}; Code: 0: ldc #10; //String Hello World 2: putstatic #12; //Field myString:Ljava/lang/String; 5: return public de.rtjava.test.StringTest(); Code: 0: aload_0 1: invokespecial #17; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #12; //Field myString:Ljava/lang/String; 3: ldc #10; //String Hello World 5: if_acmpne 16 8: getstatic #23; //Field java/lang/System.out:Ljava/io/PrintStream; 11: ldc #29; //String References in the same class match 13: invokevirtual #31; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: getstatic #23; //Field java/lang/System.out:Ljava/io/PrintStream; 19: ldc #37; //String References across classes match 21: invokevirtual #31; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 24: ldc #39; //String Hello 26: ldc #41; //String World 28: invokevirtual #43; //Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String; 31: astore_1 32: aload_1 33: getstatic #12; //Field myString:Ljava/lang/String; 36: if_acmpeq 63 39: getstatic #23; //Field java/lang/System.out:Ljava/io/PrintStream; 42: aload_1 43: invokevirtual #31; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 46: getstatic #23; //Field java/lang/System.out:Ljava/io/PrintStream; 49: getstatic #12; //Field myString:Ljava/lang/String; 52: invokevirtual #31; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 55: getstatic #23; //Field java/lang/System.out:Ljava/io/PrintStream; 58: ldc #49; //String --> See the difference 60: invokevirtual #31; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 63: return }
- As you can see the first comparison used the same constant pool entry. The strings in A and B are merged during class loading/initialisation. But the created String can't match as it is a different string instance with the same content. Not sure how this can be presented without a long paragraph about the java bytecode and the jvm.... Also note that merging across classes and merging the constant pool are optional features, so none of the == compares is guaranteed to work. I'm damn sure that merging between classes wasn't a feature from the beginning. Hope this clarifies the confusion. Understanding the byte format, the compiler and the jvm helps a lot. Especially when it comes to Strings (jvm+bytecode), Generics (compiler, bytecode), Autoboxing (compiler) etc. The rule for == is simple: If you don't intend to check for the same reference - don't use it. Every "equals" implementation should start with "if (this == o) {return true}". This is a requirement for java.util. Collections may break if a.equals(b) == false while a == b.
Strings Cannot Be Garbage Collected?
[edit source]I believe this statement on the page is incorrect:
"Remember that no String objects are ever eligible for garbage collection. Once a String is created, it takes up memory until the JVM terminates."
Yes, Strings are immutable objects.
Yes, literal strings, and strings created at compile time via constant expressions, are interned, and interned Strings won't be garbage collected.
But... not all Strings are interned!
And why shouldn't a non-interned String be eligible for garbage collection? If there are no references to it, it should be able to be garbage collected... and if it's not interned, then it certainly can be the case that nothing else points to it anymore.
In short: I'm pretty sure the statement is incorrect.
I'd really like to see some sort of reference, from Sun's website, or the JLS, indicating why Strings are *never* garbage collected. Otherwise, the statement should be replaced with something more accurate.
The code example is still valid; it does create a large number of temporary String objects that get thrown away quickly, and that does create a performance issue, mitigated a bit by generational garbage collection (since the temporary Strings don't live very long). But I don't believe it counts as a memory leak.
For more information, see:
http://java.sun.com/developer/TechTips/1999/tt0114.html
http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html
- You are right about that the String objects are garbage collected. I made the change, see the page. Thanks.