What is the toString Method in Java

Overview

In this article, I'll explain the toString method and how to override it from the Object class in our Java classes.

Any class in Java extends the Object class; thus, any class can override its methods. The toString method is one of these methods.

In the Object class, the default behavior of toString prints the object's memory address. That's not very practical since most applications need to serialize or print to output the object's value, not its address.

Whenever you print an instance of Object's sub-class, the JVM calls the default implementation that prints the object's memory address. If we want a different behavior, we need to override the toString method with the implementation we want.

Throughout this article, we'll use the Person class to exemplify the usage of toString:

1  class Person {
2    public String name;
3    public String lastName;
4
5    public Person(String name, String lastName) {
6      this.name = name;
7      this.lastName = lastName;
8    }
9  }
java

Let's also define a class with a main method to run our tests:

1public class ToString {
2
3    public static void main(String[] args) {
4        Person person = new Person("John", "Wick");
5
6        System.out.println(person);
7    }
8}
java

In the previous code, we define a Person object with name = John, and lastName = Wick. Let's check what the println method outputs with the person object as input.

1com.blog.codesnippets.strings.ToString$Person@77459877
text

The default toString implementation outputs the fully qualified name of the object followed by its memory address (represented as @77459877).

Commonly, we are interested in serializing the object's values instead of its memory address. Suppose the application requires serializing a Person field in a key-value style. To achieve that, we can create a custom implementation of toString. The implementation varies for classes with only primitive fields and classes with nested classes.

That scenario is straightforward. We need only to override toString in the target class using our custom implementation. Let's see a modification of the Person class with toString:

 1public class Person {
 2
 3  public String name;
 4  public String lastName;
 5
 6  public Person(String name, String lastName) {
 7    this.name = name;
 8    this.lastName = lastName;
 9  }
10
11  @Override
12  public String toString() {
13    return "Name: " + name + "\nLast name: " + lastName;
14  }
15}
...
java

The above code overrides the default behavior of toString and returns a formatted template of the name and lastName fields. If we call the same main method again, we should get the following output:

1Name: John
2Last name: Wick
text

Important note: The custom version of toString should follow the method overriding rules: the sub-class toString method should have the same signature as the super-class method. The @Override annotation helps notice an override, although it's optional.

Working with nested classes is a bit different than primitive types. To correctly serialize the base class, we must define a toString method in the nested class. Let's add a nested class to Person that represents its address:

 1public class Address {
 2
 3  public String street;
 4  public String city;
 5
 6  public Address(String street, String city) {
 7    this.street = street;
 8      his.city = city;
 9  }
10}
java

The new Person class is as follows:

 1public class Person {
 2
 3  public String name;
 4  public String lastName;
 5
 6  public Address address;
 7
 8  public Person(String name, String lastName, Address address) {
 9    this.name = name;
10    this.lastName = lastName;
11    this.address = address;
12  }
13
14  @Override
15  public String toString() {
16    return "Name: " + name + "\nLast name: " + lastName + "\nAddress:" + address;
17  }
18}
...
java

Let's test our implementation of toString, creating a Person with an Address nested object with the main method below:

1public class ToString {
2
3  public static void main(String[] args) {
4    Person person = new Person("John", "Wick", new Address("Fifth Avenue", "New York"));
5
6    System.out.println(person);
7  }
8}
java

Let's check the output for the main method above:

1Name: John
2Last name: Wick
3Address: com.blog.codesnippets.strings.Address@548c4f57
text

Since there's no custom implementation of toString in the Address class, the code outputs its memory address, as we see previously with Person. To avoid that, we also need to provide another implementation for Address's toString method. Let's change the content in the Address class to the following:

 1public class Address {
 2
 3  public String street;
 4  public String city;
 5
 6  public Address(String street, String city) {
 7    this.street = street;
 8    this.city = city;
 9  }
10
11  @Override
12  public String toString() {
13    return "\n\tStreet: " + street + "\n\tCity: " + city;
14  }
15}
...
java

The serialization of Address to string should return the street and city in a key-value format. After running the same main method, we should get the following results:

1Name: John
2Last name: Wick
3Address:
4  Street: Fifth Avenue
5  City: New York
text

Now, the Person object and its nested objects are correctly serialized to string.

Overriding the toString method for all classes in your application is tedious. Let's see ways to improve that process instead of manually doing it.

One option is to use the @ToString annotation of Lombok. If you use a build system like Maven or Gradle, you can add the Lombok dependency to your project and put the @ToString above the class name. In our example, that annotation should be at both Person and Address classes.

Please look at their documentation on configuring the most recent version of it on your project.

Using IntelliJ, we can generate the toString with the following steps:

  1. Press Alt + Insert in your class. Or, right-click on its name and choose Generate.
  2. Select the toString option.
  3. Choose the fields you want to serialize in the custom toString method and press OK.

This option can be helpful if your application doesn't use external tools like Lombok.

In this article, we've seen the purpose of the method toString, how to override it, and how to generate it using tools.