Peter is a DZone MVB and is not an employee of DZone and has posted 161 posts at DZone. You can read more from them at their website. View Full User Profile

Thursday Code Puzzler: String Intern Puzzle in Java 7 and 6

10.18.2012
| 9245 views |
  • submit to reddit
A String based puzzle for you.

The following program

String te = "te", st = "st";
// "test".length();
String username = te + st;
username.intern();
System.out.println("String object the same is: " 
    + (username == "test"));

prints under Java 7 update 7.

String object the same is: true

but uncomment the "test".length(); line, or run with Java 6 and it prints

String object the same is: false

Can you work out why?
Published at DZone with permission of Peter Lawrey, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Vijay Nathani replied on Thu, 2012/10/18 - 8:47am

I am using Java 1.7.0.

I am getting true in both cases i.e. wheter the line ""test".length();" is commented or uncommented.

$ java -version
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)

$ nl test1.groovy
     1  #!/usr/bin/env groovy
     2  String te = "te", st = "st";
     3  "test".length();
     4  String username = te + st;
     5  username.intern();
     6  println("String object the same is: " + (username == "test"));

$ ./test1.groovy
String object the same is: true

$ nl test1.groovy
     1  #!/usr/bin/env groovy
     2  String te = "te", st = "st";
     3  //"test".length();
     4  String username = te + st;
     5  username.intern();
     6  println("String object the same is: " + (username == "test"));

$ ./test1.groovy
String object the same is: true

Balázs Bessenyei replied on Thu, 2012/10/18 - 11:28am

The original code ignores the return value of intern(); Because of this the value in username is interned, but username still references to the non interned instance.

Since the above is a typical error, my guess would be that the newer JDK tries to prevent this common error. However if the same String literal precedes the faulty intern() call. Then the compiler might mistakenly thinks that the silent user error correction is not needed. Since String literal "test" already interned.

Compiling the same class with JDK 1.6 and JDK 1.7 reveals no differences, beside the class version number. which implies that the above optimization is actually done by the JVM itself.

Fixing the code as follows, would make it work correctly in all situations:
username = username.intern();

 

@Vijay Nathani: Because you are trying to compile and execute it as groovy code. The groovy compiler apparently makes some changes to the code, probably fixing user error in line 5. It works as described from Eclipse and command line Java.

Kode Ninja replied on Thu, 2012/10/18 - 4:18pm

You're getting true in either case because you're running this in Groovy, which internally translates == to .equals().

To get the same behaviour in Groovy (reference comparison), you would have to use the below code:

username.is('test')

 

Keerthi Ramachandran replied on Wed, 2012/10/24 - 8:16am

just look into the javadoc for the intern method.... which explains why we're getting true in first case 

I've formatted the same for redability,

Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a
string equal to this String object as determined by the {#equals(Object)} method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
 
It follows that for any two strings s and t, s.intern()==t.intern() is true if and only if s.equals(t) is true.
 
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification.
 
@return  a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings. 

Sandor Pinter replied on Sat, 2012/11/03 - 5:50pm

EDITED: I removed my previous comment since it was wrong.

Meantime found that someone already had a similar question, but unfortunately the answers (multiple exist) are not clear, some of them are wrong, etc.
You can find that post here: http://stackoverflow.com/questions/7065337/intern-behaving-differently-in-java-6-and-java-7

So after I investigated further, I hereby summarize/explain it for the given example:)
In both cases:
Compiler (doesn't matter 6 or 7) detects that we defined "test" iteral in line 6 where we compare it with the username variable in the sysout call, so puts this info into the bytecode of the class.This will be important later.

On line 3, the -> String username = te + st; -> is compiled as String username = new StringBuilder().append(te).append(st).toString(); , so creates a new String.

In JVM 6, the constant pool is populated when the class is loaded, so in JVM 6, before starting to execute the first line, the classloader founds that a "test" literal is already defined in the class, so creates a String with text "test" and interns it (places on the constant pool).
When line 4 -> username.intern(); executes, our "test" string will not be interned because it had interned already (by class load time), so username.intern() returns a reference to the already interned String, however the return value is not used, username still reference to our created "test" String.
Taataa: the comparison of the two references results: false

In JVM 7, the constant pool population is changed (string pool is not located on the permgen space anymore).
That means, no string pool is populated during the class load process, but populated when the execution runs.
In detail, when line 4 -> username.intern(); executes, our "test" string will be interned because no String with text "test" was interened before (no population at class load time), so username.intern() first interns our String and returns a reference to itself.
Taataa: the comparison of the two references results: true

In JVM 7 when uncommenting line 2:
As explained before, during the execution, the line 2 -> "test".length(); interns the "test" String, after on line 3 a new String created, on line 4 the username.intern() returns not itself but the reference to the interned String (on line 2).
Just a note if it's not 100% clear: the "test".length(); is compiled as new String("test").intern().lenght();

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.