1. String Interning
As an result of Strings immutability the JVM can make optimization to store only one copy of each literal String in the Java String Pool (the special memory region where Strings are stored by the JVM). This process is called interning
If found, the Java compiler will simply return a reference to its memory address, without allocating additional memory. If not found, it'll be added to the pool and its reference will be returned.
String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2); //true
2. Strings Allocated Using the Constructor
Creating a string using the new operator will create a new object and store it in the heap space
Every String created like this will point to a different memory region with its own address
String s1 = "Java";
String s2 = new String("Java"); //or
StringBuilder sb1 = new StringBuilder();
sb1.append("Java");
System.out.println(sb1.toString() == s1);; //false
System.out.println(s2 == s1);; //false
3. Conclusion
When we create a String object using the new() operator, it always
creates a new object in heap memory. On the other hand, if we create
an object using String literal, it may return an existing object from
the String pool
4. Manual Interning
Manually interning the String will store its reference in the pool, and the JVM will return this reference when needed
String s1 = "Java";
String s2 = new String("Java").intern(); //or
StringBuilder sb1 = new StringBuilder();
sb1.append("Java");
System.out.println(sb1.toString().intern() == s1);; //true
System.out.println(s2 == s1);; //true
System.out.println(sb1.toString().intern() == s1);