Thursday, 14 July 2016

String in Java

In Java String class represents character strings which means; Strings in Java are objects and all strings are instances of the String class. Internally in String class Strings are stored as character array.

String literals and string pool

Since strings are objects so strings can of course be created using new operator. String class has more than 10 constructors to create Strings which ranges from taking nothing as parameter to taking char array, StringBuffer, StringBuilder, another String as argument.

As Example - String str = new String(“abc”);

But the most preferred way to create Strings is to assign String literal directly to a String reference as you will do for any primitive type. For every string literal Java automatically constructs a String object.

As example - String str = “abc”;

But having String literals bring another dimension to store Strings. If it were only String objects those will go in the heap with their own space. But String literals are treated differently they are stored in a String pool and that is a common pool; which means if there are two strings literals having the same content then those string will share the space in the pool.

When String object is created by assigning a string literal, pool will be checked to verify if there is any existing object with the same content if there is then that existing reference is used, no new object is created in that case. If no object is found with the same content then this new literal will be added in the pool.

String pool in Java
String pool in Java

Let’s see it with an example

In this program two string literals will be created with the same content and then these two string objects are checked for equality. Since we are not comparing the content but the references of two objects so “==” operator will be used.

public class StringDemo {
 public static void main(String[] args) {
  String str1 = "abc";
  String str2 = "abc";
  if(str1 == str2){
   System.out.println("str1 and str2 are same");
  }else{
   System.out.println("str1 and str2 are not same");
  }
 }
}

Output

str1 and str2 are same

Now if we create two more strings using new operator and then compare reference they should be different.

public class StringDemo {
 public static void main(String[] args) {
  String str1 = "abc";
  String str2 = "abc";
  if(str1 == str2){
   System.out.println("str1 and str2 are same");
  }else{
   System.out.println("str1 and str2 are not same");
  }

  String str3 = new String("abc");
  String str4 = new String("abc");
  if(str3 == str4){
   System.out.println("str3 and str4 are same");
  }else{
   System.out.println("str3 and str4 are not same");
  }
  
  if(str1 == str4){
   System.out.println("str1 and str4 are same");
  }else{
   System.out.println("str1 and str4 are not same");
  }
 }
}

Output

str1 and str2 are same
str3 and str4 are not same
str1 and str4 are not same

Here it can be seen that str3 and str4 are having separate reference as those strings are created using new operator.

String intern() method

Using intern() method you can still get string object from the pool (if it exists) even if new operator is used to create a 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.

In the previous Java program if str4 is changed to have interned string then the code will look like –

public class StringDemo {

 public static void main(String[] args) {
  String str1 = "abc";
  String str2 = "abc";
  if(str1 == str2){
   System.out.println("str1 and str2 are same");
  }else{
   System.out.println("str1 and str2 are not same");
  }
  String str3 = new String("abc");
  String str4 = new String("abc").intern();
  if(str3 == str4){
   System.out.println("str3 and str4 are same");
  }else{
   System.out.println("str3 and str4 are not same");
  }
  
  if(str1 == str4){
   System.out.println("str1 and str4 are same");
  }else{
   System.out.println("str1 and str4 are not same");
  }
 }
}

Output

str1 and str2 are same
str3 and str4 are not same
str1 and str4 are same

It can be seen that str1 and str4 are having the same reference now.

String is immutable

Once you create a String object the content of that string cannot be modified. As we have already seen Java maintains a string pool where references are shared thus changing content of any of the String will also affect the other strings sharing the same references that’s one reason why string is immutable.

Here being immutable means whenever you perform any operation on string which alters its content a new string object is created which contains the modified string. Original string is left as it is. If there are no references to the original string it is garbage collected.

As example – Using any of the methods like toLowerCase, toUpperCase, concatenating using concatenate() method or ‘+’ operator will result in creating a new string object.

In the case string is modified frequently consider using StringBuffer or StringBuilder classes which are mutable.

String class is final

As already mentioned above whenever you perform any operation on string which alters its content a new string object is created containing the modified string. Which means all the methods of the String class that modify the content in any way return a new String object with the modified content.

Now, What if you can override the method of the String class so that it modifies and return the original string reference itself? In that case all the other strings having the same data in the string pool will also get affected as the reference is shared for the String literals having the same content.

Another scenario - You extend the String class and override hashCode() and equals() method in such a way that two dissimilar strings return the same hashCode and at the same time equals() return true. Then you can have different strings sharing the same reference in the String pool.

To avoid these kind of scenarios String class is declared as final and it can’t be overridden.

String and thread-safety

Since String objects are immutable thus thread-safe.

Overloaded operator in String

Apart from using concatenate method to concatenate two strings ‘+’ operator can be used to do the same. Actually + and += are two operators which are overloaded for String in Java.

So, if you have two strings
String str1 = "Hi";
String str2 = "Hello";

You can use ‘+’ operator to concatenate them

str1 = str1 + str2;
System.out.println("str1 " + str1);

Or, to make it more concise

str1 += str2;
System.out.println("str1 " + str1);

Comparing Strings using .equals method

In the section about string pool we used == to compare references but what if you want to compare content of two strings even if their references are different. You have to use .equals method in that case.

public class StringDemo {

 public static void main(String[] args) {
  String str1 = "abc";
  String str4 = new String("abc");
  // comparing content
  if(str1.equals(str4)){
   System.out.println("str1 and str4 are same");
  }else{
   System.out.println("str1 and str4 are not same");
  }
  // comparing references
  if(str1 == str4){
   System.out.println("str1 and str4 are same");
  }else{
   System.out.println("str1 and str4 are not same");
  }
 }
}

Output

str1 and str4 are same
str1 and str4 are not same

Though str1 and str4 have same content but they will have different references as str4 is created using new operator. That is why comparing using "==" prints "str1 and str4 are not same" as references are different but comparing using .equals prints "str1 and str4 are same", as content is compared in that case.

Points to note

  1. Internally in String class Strings are stored as character array.
  2. Strings in Java are objects and all strings are instances of the String class.
  3. Strings can be created by assigning String literal directly to a String reference like String str = “abc”; which may look like assigning to a primitive data type but don't forget Strings are objects.
  4. String literals are treated differently they are stored in a String pool and that is a common pool.
  5. If there are two strings literals having the same content then those string will share the space in the pool.
  6. String is immutable once you create a String object the content of that string cannot be modified.
  7. Since string is immutable whenever you perform any operation on string which alters its content a new string object is created which contains the modified string. Original string is left as it is.
  8. Since String is immutable it is also thread safe.
  9. String class is declared as final and it can’t be overridden.
  10. "+" operator is overloaded for String and it is used for concatenating strings.
  11. Using intern() method you can still get string object from the pool (if it exists) even if new operator is used to create a string.
  12. For comparing the content of two strings .equals() method is used. If you want to ignore case then use .equalsIgnoreCase().
  13. From Java 7 string can also be used in switch case statement.
  14. join() method is added in String class in Java 8 which makes it very easy to join multiple strings.

That's all for this topic String in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related topics

  1. String charAt() and subString() methods in Java
  2. String comparison in Java
  3. Searching within a String using indexOf(), lastIndexOf() and contains() methods
  4. String join() method in Java 8
  5. StringBuffer in Java
  6. Java String interview questions

You may also like -

>>>Go to Java Basics page

2 comments:

  1. Hi,

    Thank you for your great articles. Could you write some posts about Java stream API, like you did with lambda expressions?

    ReplyDelete
  2. This post covers String in Java exhaustively. A very nice post.

    ReplyDelete