The basic answer

String

Java’s attitude to strings, I think, is almost like treating String as a basic data type. The main differences between String and primitive data types are:

  • Save data to constant pool;
  • Strings have a lot of behavior; In Java, shallow copy means that only the primitive data type attributes of an object, String attributes, and other references to data types can be copied.

The way Java treats Strings is that both properties and classes are final, indicating that they are not extensible and cannot be changed. What String is actually doing internally is maintaining a char array, which is a final property, indicating that the property is immutable, so any extension to String is creating a new String.

The JVM handles strings by storing them in a constant pool, which is the same way it treats basic types. Strings are also declared much like primitive data types, and can be created without using the new keyword. This is because it gets similar processing (constant pooling) at the JVM level to basic data types. Wrapper classes, on the other hand, have only the cache provided at the code level and are declared as syntactic sugar, essentially creating new objects that are just placed at the syntactic level to handle the work.

For example, when connecting to a database in JDBC, String is used as the URL to create a connection, which also ensures that the URL cannot be tampered with

StringBuffer

StringBuffer and String are both created at the same time in order to actually have an extensible String and also to take the pressure off adding objects to the JVM heap, which can be extended using the Append method. But StringBuffer is thread-safe, and its thread-safe approach is heavy-duty synchronized.

StringBuilder

This came after JDK1.5 and has the same functionality as StringBuffer, except that it is not thread-safe. This class can be used when the need for strings is not thread-safe.