Monday, July 19, 2021

Java Tutorial: Exploring The String Class

Chapters

Java String

String in java represents a set of characters that acts as a form of text. All string literals that are defined in a program are instances of the String class. So, new String("abc") is a string object and "abc" is also a string object.

String Immutability

Strings are immutable. Immutability means fixed or unmodifiable. Everytime we create a string object, its value can't be modified. So, s1 = "abc" and s1 = "bcd" are different strings. Once we assign a different string literal to s1, the current string literal("abc") that is assigned to s1 will be unreferenced and the new string literal("bcd") will be referenced to s1. In other words, we didn't modify "abc", we just replaced it with "bcd".

There are classes that support mutable string. StringBuffer and StringBuilder support mutable string.

String Constant Pool

String Constant Pool(SCP) is a container in java heap memory where some string objects are placed in order to optimize string usage in a program. Take a look at this example.
References:
15.28. Constant Expressions
When will a string be garbage collected in java

public class SampleClass{

  public static void main(String[]args){
    String s1 = new String("Hello");
    String s2 = "Hello";
    String s3 = "Hello";
    
    //Remember, == is different from equals()
    //
    //==, when is used in string objects,
    //compares strings' memory 
    //addresses
    //
    //whereas equals() compares
    //objects' string representation
    System.out.println(s1 == s2);
    System.out.println(s2 == s3);
    
    //concatenation that is evaluated
    //as compile time constant can be
    //automatically interned
    //
    //"Hello" and 123 are both literals
    //and they're going to be assigned
    //in a string variable. So,
    //"Hello"+123 is evaluated as
    //compile time constant
    String s4 = "Hello"+123;
    System.out.println(s4 == "Hello123");
    
    int i = 35;
    //In this code, we include a non-final
    //variable which is not a constant
    //variable.
    //Thus, the expression below is not
    //evaluated as compile time constant
    //
    //When we include variables in an
    //expression and we want that 
    //expression to be evaluated as
    //a string contant then, the
    //variables must be constant also.
    //
    //make "i" final and the println()
    //below is going to display true
    //on the console
    String s5 = "Hello" + i + "Hi";
    System.out.println(s5 == "Hello35Hi");
  }
}

Result
false
true
true
false
The first comparison is false because "Hello" in s1 is the string reference in the heap whereas "Hello" in s2 is the string reference in the String Constant Pool. The second comparison is true because the string reference in s3 is copied from SCP. The thrid comparison is true 'cause the expression "Hello"+123 is a compile time constant. The fourth comparison is false 'cause the expression "Hello" + i + "Hi" is not a compile time constant.

String literals, as long as they're not yet registered in the SCP and they're considered as compile time constants, are placed in the SCP. Also, when we use this overloaded constructor of String: public String(String original)
It creates a string in the SCP and then creates an explicit copy into the heap, as long as the "original" is not yet registered in the SCP and considered as compile time constant. Otherwise, the constructor will only create the string object in the heap. The constructor returns the reference of the string object in the heap.

Note: Garbage Collector may collect unreferenced string instance in SCP.
Note: Prior to Java 6, String constant pool was located in Permgen Area(renamed to metaspace in Java 8) and Since Java 7, String constant pool is relocated to heap.

String Interning

There are times that we may want to get a string reference in the SCP or put a string instance in the SCP that is not yet there. Well, we can do that by using the intern() method.
public class SampleClass{

  public static void main(String[]args){
    String s1 = new String("Hello");
    String s2 = "Hello";
    String s3 = s1;
    String s3a = s1.intern();
    
    System.out.println(s1 == s2);
    System.out.println(s2 == s3);
    System.out.println(s2 == s3a);
    
    char[] c = new char[]{'H','i'};
    String s4 = new String(c);
    String s5 = s4.intern();
    
    System.out.println(s4 == s5);
    String s6 = "Hi";
    System.out.println(s5 == s6);
  }
}

Result
false
false
true
false
true
The first comparison is false 'cause "Hello" in s1 is in the heap whereas "Hello" in s2 is in the SCP. The second comparison is still false 'cause "Hello" in s3 is the reference of "Hello" in s1. The third comparison is true 'cause "Hello" in s3a is the reference of "Hello" in SCP which is also the reference of "Hello" in s2.

As we can see in the example above, if we intern a string and that string is equal to one of the strings in the SCP, intern() returns the reference of that one string instead of adding a copy of the interned string in the SCP. That's why the thrid comparison is true.

Now, let's evaluate the 4th and 5th comparison. The fourth comparison is false 'cause "Hi" in s4 is in the heap whereas "Hi" in s5 is in the SCP. Fifth comparison is true 'cause "Hi" in s6 and s5 are in the SCP.

As we can see in the example above, if the interned string doesn't have a copy in the SCP, intern() creates a copy of the interned string to SCP and returns the reference of that copy. That's why the fourth comparison is false.

Demonstrating Some String Constructors

String class has many overloaded constructors that we can use. I will demonstrate some of them here. To check out all overloaded constructors of String class, go visit the String Class Documentation.
public class SampleClass{

  public static void main(String[]args){
    
    //String(String original)
    String s1 = new String("String1");
    
    char c[] = new char[]{'H','i'};
    //String(char[] value)
    String s2 = new String(c);
    
    //String(byte[] bytes)
    //constructs a new String by decoding the
    //specified array of bytes using the platform's
    //default charset.
    String s3 = new String(new byte[]{50,80,90,75});
                           
    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);
  }
}

Result
String1
Hi
2PZK
Demonstrating Some String Methods

String class has many methods that we can use. I will demonstrate some of them here. To check out all methods of String class, go visit the String Class Documentation.
public class SampleClass{

  public static void main(String[]args){
    String s1 = "Hello";
    
    //charAt(int index)
    //Returns the char value at the specified index.
    System.out.println(s1.charAt(1));
    
    //contains(CharSequence s)
    //Returns true if and only if this string
    //contains the specified sequence of char
    //values.
    System.out.println(s1.contains("Hi"));
    
    //equals(Object anObject)
    //compares this string to the specified
    //object.
    System.out.println(s1.equals("Hello"));
    
    //equalsIgnoreCase(String anotherString)
    //compares this String to another String,
    //ignoring case considerations.
    System.out.println(s1.equalsIgnoreCase("hello"));
    
    //lastIndexOf(int ch)
    //returns the index within this string of the
    //last occurrence of the specified character.
    //The result is 3 'cause the last occurence
    //of 'l' is in index 3
    System.out.println(s1.lastIndexOf('l'));
    
    //indexOf(int ch)
    //returns the index within this string
    //of the first occurrence of the specified
    //character.
    //The result is 2 'cause the first 
    //occurence of 'l' is in index 2
    System.out.println(s1.indexOf('l'));
    
    String s2 = "Hello, Hello";
    
    //lastIndexOf(String str)
    //returns the index within this string of
    //the last occurrence of the specified
    //substring.
    //
    //The result is 7 'cause the last occurence
    //of "Hell" starts at index 7
    System.out.println(s2.lastIndexOf("Hell"));
    
    //length()
    //returns string length
    System.out.println(s2.length());
    
    //toLowerCase()
    //Converts all of the characters in this
    //String to lower case using the rules of
    //the default locale.
    System.out.println(s2.toLowerCase());
    
    //toUpperCase()
    //Converts all of the characters in this
    //String to upper case using the rules of
    //the default locale.
    System.out.println(s2.toUpperCase());
    
    //startsWith(String prefix)
    //Tests if this string starts with the
    //specified prefix.
    System.out.println("startsWith: " + s2.startsWith("Hell"));
    
    //endsWith(String suffix)
    //Tests if this string ends with the
    //specified suffix.
    System.out.println("endsWith: " + s2.endsWith("Hello"));
    
    String s3 = "Hello Everyone!";
    
    //substring(int beginIndex)
    //Returns a string that is a substring
    //of this string.
    //
    //Parameters:
    //beginIndex - the beginning index, inclusive.
    System.out.println(s3.substring(6));
    
    //substring(int beginIndex, int endIndex)
    //Returns a string that is a substring
    //of this string.
    //
    //Parameters:
    //beginIndex - the beginning index, inclusive.
    //endIndex - the ending index, exclusive.
    //
    //inclusive means included in the result.
    //Therefore, the character in the beginIndex
    //is included in the result
    //
    //exclusive means excluded in the result
    //Therefore, the character in the endIndex
    //is excluded in the result
    System.out.println(s3.substring(6,11));
    
    //isEmpty()
    //Returns true if, and only if, length() is 0.
    String s5 = "\n";
    System.out.println("isEmpty? " + s5.isEmpty());
    
    //static String valueOf(int i)
    //Returns the string representation
    //of the int argument.
    //Check the documentation for more
    //valueOf() forms
    String s6 = String.valueOf(100);
    System.out.println(s6);
  }
}

Result
e
false
true
true
3
2
7
12
hello, hello
HELLO, HELLO
startsWith: true
endsWith: true
Everyone!
Every
isEmpty? false
100

No comments:

Post a Comment