String References and Immutability

     

Something that is immutable means that it cannot be changed. One immutable thing in this universe is a Java String, which means, after it is assigned a value, that value never changes ” ever . To which you might reply, "Oh yeah? Well what about this?" and then whip out some ninja code like this:

 

 String s = "kitty"; s = s.concat(" cat"); System.out.println(s); //prints kitty cat 

The concat method of the String class takes the string value of the object on which you call the method and concatenates (adds to the end) the literal parameter you pass the method. So that looks like all kinds of mutability, right?

Here's what really happens:

When we call concat , the virtual machine takes the value of the String s, and hangs onto it. Because it can't make any other character string except for "kitty" be the value of s, it creates a second, new string object, assigns it the value of " cat", and then creates a third String object with the value of "kitty cat" (both literals mushed together), and then reassigns the object reference of s to that new object! Pretty shifty. In other words, there are now three String objects, and s refers to the third one. Technically (this is at least trying to be a technical book), s didn't change its string value; it only changed its object reference, which gives the appearance of changing that String object's value.

So how many references are there?

Only one. s .

But ”hey! What happened to the "cat" object? Isn't it on the heap? Yes. But nothing refers to it, and so it is lost forever, with no way for anyone to access it.

In the preceding example, we took a String and changed its characters by calling the concat method on it. That code looks like this:

 

 s = s.concat(" cat"); 

Notice that we assign s again here. That is, we have a String s, and then assign it to the String object created when the concat() method call returns, which is how we get the new value "kitty cat". Can you tell what this code will output?

 

 String s = "kitty"; s.concat(" cat"); System.out.println(s); 

If you haven't fallen asleep yet, good show. If you were paying careful attention, you might have guessed that this code prints "kitty" because we never reassign the object reference of s to point to the result of the method call. So it points to the same value it did when it was initialized . This code makes a String object for "kitty", and the concat() method makes two more (" cat" and "kitty cat"). No one ever asks the JVM about it, and now no one ever can.

Let's look at another example that is a little more complicated.

 

 1. String s1 = "first"; 2. String s2 = "second"; 3. s1 = s2; 4. s2 = "new value for s2"; 5. System.out.println("s1: " + s1); 6. System.out.println("s2: " + s2); 

What output will this code produce?

The first two lines are very straightforward: they create two separate String objects with different values. At line 3, the reference for s1 is reassigned to point to s2 , and there's the trick. Will line 5 print "second" or "new value for s2"?

If Strings are immutable, and s2 is given a new value on line 4, what happens? A new object is created and s2 is reassigned to that new object's address. s1 still points to the previous address for s1 , and "second" is printed. Line 6 prints "new value for s2" as you would expect.

Why go to all of the trouble to make Strings immutable? One benefit is speed. The runtime can reuse a literal from the pool of Strings without having to create a new one, and because object creation is one of the more expensive endeavors the runtime will undertake, it's good to avoid it if possible. There is another consideration. Security.

Strings and Security

A Java String inherits from Object, but no class inherits from String, or ever will. That's because String is marked final , which means that it is the last of its kind. No class can ever inherit from a class marked with the final keyword in its declaration. I'll prove it. Try typing this in Eclipse (or whatever IDE or vi or what have you):

 

 public class StringSubclass extends String { } 

Here's what the compiler tells you:

 

 The type StringSubclass cannot subclass the final      class String StringSubclass. 

So what does that have to do with security? Well, our inability to subclass String means that we cannot get a value from a host and rewrite that reference using our custom (and malicious) subclass, substituting the runtime type for ours (which otherwise would be legal).

Luckily, in day-to-day business programming, I have yet to find myself at my keyboard thinking, "Damn! It will never work! If only I could subclass String " Of course, I'm not in the business of writing viruses

There is the related issue of converting other objects to Strings. Some things that you think might directly convert to Strings (such as an array of characters) do not. You must use the Java keyword new , and pass it the char array. Now why on earth would that be necessary? All of these hoops they make us jump through! Let's take a step back.

What happens when you do this:

 

 String x = "This guy"; String y = "Some other guy"; x = y; 

Two String objects are created: x and y. Then, the value of the y String is assigned to x (meaning, now x has the value of y). Big deal. Well, in order to see the big deal, let's recall that the value of an object is its reference! Sure, it might look to us like the value of s String is the characters we write, but that is a human-centric view (which I learned in graduate school is indelicate). Think about it from the virtual machine's perspective. We just replaced the object reference for x to point to y's address in memory. If we could assign arbitrary references (or at least practically arbitrary types, such as byte arrays) to Strings, we could easily create a buffer overflow, ruin the integrity of the application, and gain access to local resources that we were not supposed to have access to. This small march of progress is the origin of many viruses that exploit less strongly typed languages. I won't mention any names .

String Assignments

The StringBuffer will change the object's value without using the assignment operator. This is slightly different than you might expect given how String works. Here's an example:

 

 String s = "Elvis"; s + " Presley"; //s = Elvis StringBuffer sb = "Elvis"; sb.append(" Presley"); //sb = Elvis Presley 

To make the String in the preceding code operate like the StringBuffer, you need to assign s to the result of the operation, like this:

 

 String s = "Elvis"; s = s + " Presley"; 

Recall that the shortcut for primitives works for Strings here as well:

 

 String s = "Elvis"; s += " Presley"; //same difference 

You can read about StringBuffer s a little later in this topic.



Java Garage
Java Garage
ISBN: 0321246233
EAN: 2147483647
Year: 2006
Pages: 228
Authors: Eben Hewitt

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net