Lets learn more about Strings in Java today. Java Strings are one of the most commonly used and hence we will explore about it in detail.
- String is an object in Java and is part of the java.lang package.
- To invoke objects of other classes in Java, we need to create a new object() in Java and then access the methods inside it. But in Java, strings can also be created even without explicitly creating a new String() object for it.
- Strings are mutable. I will make this clear with some examples.
- String concatenation using “+” operator.
Java Strings declaration:
String java_str = "JavaStrings"; String java_str1 = new String("JavaStrings1");
In the first case, Java internally creates a new object instance of Strings and then stores its contents “JavaStrings”, whereas in the second case, we explicitly create Strings using Java constructor.
Remember method overloading, Java string class has more than 10 constructors, which can be used based on needs. Lets look at the following program to make things little interesting. I have split the example program into several pieces to emphasize key concepts. Lets look at each scenario in detail.
public class JavaString { /** * @param args */ public static void main(String[] args) { String str1 = "JavaString1"; String str2 = "JavaString1"; if(str1==str2) { System.out.println("They are equal!"); } else { System.out.println("No, they are not equal!"); } } }
Output:
They are equal!
In the first case, two different strings str1 and str2 was created using string literals (a commonly used name for such declarations). Remember that “==” method will compare two object references and not its contents. So both str1 and str2 have the same contents, Java internally optimizes and stores these different strings at the same memory location. Java sets aside some memory for this purpose. From now on, lets call it “String memory” for our reference. That is why they satisfy the equality condition.
Note: Always use equals() method for comparing two strings. I have used “==” just for illustration purposes.
String str3 = new String("JavaString1"); String str4 = new String("JavaString2"); <span style="font-size: 12px; line-height: 18px;"> if(str3==str4) {</span> System.out.println("They are equal!"); } else { System.out.println("No, they are not equal!"); }
Output:
No, they are not equal!
In the second case, I have created two Strings using new String() and as expected, they will be stored in two different places in memory even though their contents are the same. This is stored in Heap memory compared to the first case where Java optimizes it and stores it in a String memory. This is a major difference. And since we compare two different object references, output will be “They are not equal!”.
str3 = str4; if (str3==str4) { System.out.println("They are equal!"); } else { System.out.println("No, they are not equal!"); }
Output:
They are equal!
The third case combines the concepts of first and second scenarios. We are using the same strings created using new String() but assigning one string reference to another i.e., both will now point to the same memory location. This is to illustrate that same content strings created using new String() will always return false unless you explicitly assign one string reference to another as we do in this case. That’s the reason; we see the output “They are equal!” in this case.
String str5 = "JavaString5"; String str6 = "JavaString6"; if(str5==str6) { System.out.println("They are equal!"); } else { System.out.println("No, they are not equal!"); }
Now, predict the output of the above program before checking the output below:
Hurray! you are right. The output would be “No, they are not equal!”. This is because two strings are stored in different memory locations (not in Heap) as they have different contents.
Output:
No, they are not equal!
Immutable Strings
You must have heard this term in several places, Java Strings are immutable. So what might be the next question? Lets look at it in detail. Some of the primary reasons why String are immutable in Java are for Security, hashcode() lazy initialization and thread safety. Immutable means that an object’s state can’t be modified once it is created. So Java strings once created and assigned can’t be modified later. This might be a little confusing but stay with me and I will provide examples to illustrate this.
String myStr = new String("StringOld"); System.out.println( myStr + ":" + myStr.hashCode()); myStr.replace("Old", "New"); System.out.println( myStr + ":" + myStr.hashCode());
Oh wait… but didn’t we modify myStr to a new value !!!!. Why doesn’t it print the new value? That’s because strings are immutable. So once we create a String reference, its contents can’t be changed. So, what really happens is that when we modify its value, Java internally creates a new String instance and stores that new modified value with the old string reference untouched. That is why, the output prints the same output though we have modified its value.
String newStr = new String("Stringold"); System.out.println( newStr + ":" + newStr.hashCode()); newStr = newStr + "new"; System.out.println( newStr + ":" + newStr.hashCode());
Output:
Stringold: 1814702166 Stringoldnew: 1038981514
Then,
myStr = myStr.replace("Old", "New"); System.out.println( myStr + ":" + myStr.hashCode());
If we had done that, then the output would have been different hashcodes. Though we think that we are modifying the same Strings, Java internally uses up memory for creating a new String reference and has to do garbage collection for two string references instead of one. So keeping this in mind, as Java developers, we must always use StringBuilder/StringBuffer when we know that we are going to modify the string contents.
Output:
StringNew: 1814670255
This example will help you in understanding immutable a lot better. Now, though we are modifying the same string, another String reference is created internally by Java and that is why we see two different hashcodes printed out. But then the question might be why same hashcodes() in previous example? That is because we didn’t assign it to myStr.
Advantages of immutable
- As can be seen, for security purposes, if we need to make sure that nobody else modified the original value of Strings that its supposed to have, we can take advantage of this immutable property.
- We end up using lot of String hashcode() method in Java Collections framework such as HashMap. They use this immutable property to cache its hashcode() values rather than retrieving it every time. This is because we know that String values once created can’t be changed anytime after that.
- Thread Safety: Using this immutable property, we can be sure that a particular String value couldn’t have been changed any threads running in parallel. As if it had been changed, then a new String instance is created internally by Java and so this immutable helps us in thread safety.
StringBuilder and StringBuffer
Aren’t there some other forms of mutable String objects? Yes, there are. StringBuilder and StringBuffer are mutable String objects. We will look at them briefly here, as they are very easy to understand. They are almost the same except that StringBuilder is unsynchronized and StringBuffer is synchronized and so it depends on usage. Lets look at an example to see the differences in when to use StringBuffer, StringBuilder classes.
String str = ""; int size = 5000; // Using StringBuffer class - unsynchronized and so can save overhead // while being used in Single Threaded programs startTime = System.nanoTime(); size = 5000; StringBuilder sBuildbuf = new StringBuilder(size); for (int i = 0; i < size; ++i) { sBuildbuf = sBuildbuf.append("ABC"); } elapsedTime = System.nanoTime() - startTime; System.out.println("Building Stringbuilder " + "Time diff is " + elapsedTime/100000);
And
// Using StringBuilder class – synchronized and can be effectively // used in multi threaded programs startTime = System.nanoTime(); size = 5000; StringBuffer sBuffbuf = new StringBuffer(str); for (int i = 0; i < size; ++i) { sBuffbuf = sBuffbuf.append("ABC"); } elapsedTime = System.nanoTime() - startTime; System.out.println("Building Stringbuffer " + "Time diff is " + elapsedTime/100000); }
Output:
Building Stringbuilder Time diff is 18 Building Stringbuffer Time diff is 23
The time difference is not that much because StringBuilder and StringBuffer have been implemented in a similar manner except that Java saves overhead when implementing StringBuilder as it’s optimized for single threaded programs.
String msg = "one" + "two" + "three",
Java internally compiles these lines to
String msg = new StringBuffer().append("one").append("two").append("three") .toString();
As can be seen, it is an overhead when we modify Strings. Hence we should always avoid Strings when we know that we will be modifying them frequently. Rather we should be using StringBuffer and StringBuilder classes.
There are lot of methods in Strings, StringBuilder and StringBuffer classes. Explore them using Javadocs. Post them in comments if you find some more interesting stuff about Strings that might be useful for others too. Thanks for reading.
This article is originally published at Java tutorials – Lets jump into the ocean, re-posted here with authors permission and as part of the JBC program.