All you need to know about Strings in Java

Photo by davide ragusa on Unsplash

A sequence of characters “abc” in Java is an object that implements an instance of String class, meaning this string object stores only the reference of this object in memory rather than the actual value.
Definition of String class:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

How to create a String object?

//Declaration using string literal
String str = "abc";
String anotherStr = "abc";
//This is equivalent to below declaration
//done using new Keyword.
char data[] = {'a', 'b', 'c'};
String strNew = new String(data);

What happens when a new string object is created?

When a new object is created like in above declaration, JVM (Java Virtual Machine) firstly looks in “String Constant pool” section of Java Heap to see if the same string value (“abc”) already exists. If value exists, it will not create another instance of the value but will only create a new object in the heap that will reference to the same value location in String Pool.

String Constant Pool is a special memory area in Heap where Java directly stores all the string values on direct allocations as constants. It’s like a cache.

For example, let’s create a string str1.

String str1 = "Strings in Java";

JVM will first check in String pool for presence of same string value. Since this is a first declaration of such a string, string pool will not have this value, then a new constant will be created in the pool and object str1 will be allocated stack that will reference the value in string pool like a pointer.

Now, let’s create new string str2 but with same value and str3 with different value.

String str1 = "Strings in Java";
String str2 = "Strings in Java";
String str3 = "I like Strings";

JVM will first check in String pool for presence of same string value. The string is present in pool, so a new variable str2 will only be created in stack that will point to the same location in heap, thus saving crucial memory resource.

Whereas for str3, the value doesn’t exist in pool, so a new instance of that value is created in heap and a new variable in stack that points to this instance.

This way String Constant Pool helps reduce memory and encourages the re-use of existing instances in memory.
String pool exists because Strings in Java are immutable, meaning the value that the object is referencing to can’t be changed. What happens if you change the value of a string? a new instance will be created in pool for the new value and string object will now reference to this new instance.
Caching the String literals and reusing them saves a lot of heap space because very often different String variables refer to the same object in the String pool.

The key benefits of keeping String class as immutable are caching, security, synchronization, and performance.

This article very interestingly explains why strings are immutable in depth!

Moving further, in order to break this caching and re-use, you can declare new String variable with ‘new’ Keyword that forces a new instance to always be created regardless of whether the same value was used previously or not.
For example:

String stringOne = new String("Hello");
String stringTwo = new String("Hello");

Although the value of both strings will be same, objects will be referencing to two separate instances created outside the String pool.

Methods of String Class

String class provides methods for:

  • Comparing two strings,
  • Examining individual characters of the sequence,
  • Searching strings,
  • Extracting substrings,
  • Creating copies of strings with options to convert to upper-case and lower-case.

The list of all methods can be found on the official documentation page.
Let’s try and cover the most commonly methods:

There are three methods to compare two strings:

  • equals() and equalsIgnoreCase() method-
    Both these methods compare values of string for equality.
String str1 = "hello";
String str2 = "HELLO";
System.out.println(str1.equals(str2));//false
System.out.println(str1.equalsIgnoreCase(str2));//true
  • ‘==’ operator
    This operator compares the string objects, if they reference the same instance in Heap, not values.
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = "world";
System.out.println(str1 == str2);//true
System.out.println(str1 == str3);//false -- although the values are //same, the 'new' keyword creates a new instance
System.out.println(str1 == str4);//false
  • compareTo() method
    This method compares string values lexicographically and returns an integer in 0,1 or -1.
String str1 = "abc";
String str2 = "def";
String str3 = "abc"
System.out.println(str1.compareTo(str3));//0 str1 == str3
System.out.println(str2.compareTo(str1));//1 str2 > str1
System.out.println(str1.compareTo(str2));//-1 str2 > str1

In java, you can concatenate two strings, value of which will be stored in a new instance all together. You can’t modify an already existing string instance (because strings are immutable in java).
Concatenation can be done using:

  • ‘+’ operator
String str1 = "Hello" + "World";
System.out.println(str1); //Hello
  • concat() method
String str1 = "Hello";
String str2 = "World";
String str3 = str1.concat(str2);
System.out.println(str3);//Hello World

In above two methods, a the newly formed string is stored in a new instance, in order to append into the same object, we can use StringBuilder or StringBuffer. The string created by these two methods are mutable.

StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.put.println(sb); // Hello World
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.put.println(sb); // Hello World

FYI:

String str1 = "Hello" + "World";

Java compiler transforms the above code to-

String str = (new StringBuilder()).append("Hello").append("World").toString();

Substring is a part of a string. You can get a substring by either of these two methods-

  • public String substring(int startIndex)-
    returns new String object containing substring of the given string from specified startIndex (inclusive).
String str1 = "HelloWorld";
System.out.println(str1.substring(5)); //World
  • public String substring(int startIndex, int endIndex)
    returns new String object containing substring of the given string from specified startIndex (inclusive) and endIndex (exclusive).
String str1 = "HelloWorld";
System.out.println(str1.substring(0,5)); //Hello

How to represent any object in Java as String?

The answer to this is, toString() method.
This method returns String representation of an object. Whenever you try to print some object, Object class toString() method is called internally. We can write our also override this method and write our own implementation.

Syntax of Object class toString() method:

public String toString()
{
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}

Example where internal toString() is called.

class Employee{
String name;
int age;
Employee(String name, int age){
this.name = name;
this.age = age;
}
public static void main(String[] args)
{
Employee emp= new Employee("Some name", 21);
System.out.println(emp);
}
}

Output:

Employee@2a139a55

Hashcode of the object is being printed.
Below is an example of how we can override the toString() method to print what we want.

class Employee{
String name;
int age;
Employee(String name, int age){
this.name = name;
this.age = age;
}
//Overriding toString() method
public String toString()
{
return "Hi, This is " + this.name +" and I am "+ this.age + " years old.";
}
public static void main(String[] args)
{
Employee emp= new Employee("Some name", 21);
System.out.println(emp);
}
}

Output:

Hi, This is Some name and I am 21 years old.

TL;DR

  • In Java, String object is a sequence of character, that only references the string value in memory.
  • Strings can be declared using literals or ‘new’ keyword and they are immutable.
  • The one declared by using literals are stored in “String constant pools”, a special memory in heap, in order to reduce memory and reuse the already present instances. Any new String object created with some value already present in pool will reference the existing instance.
  • Whereas declaration using ‘new’ keyword will not check for presence of string value in pool and will create a new instance outside the pool regardless.
  • String pool exists because Strings in Java are immutable, for the purpose of caching, security, synchronization, and performance
  • We can make string mutable be using StringBuilder or StringBuffer.
  • We can represent any object as string by using toString() method.

Techie on a mission to save the planet 🌏👩‍💻