Skip to content
Theory · Classes and objects · lesson 9 of 10

toString and null

Let's print a whole point: System.out.println(p);. You would like to see the coordinates, but the program prints something like this:

TEXT
Point@1dbd16a6

This is the class name, the @ sign and a hexadecimal number the JVM gave the object. Yours may be different: the number depends on the JVM and on how the program is started. It is as if you asked someone to describe a house and got the name of the blueprint and the number on its plate.

When println gets an object, it asks the object to describe itself as a string: it calls the object's method toString. Every object has such a method, but the standard one returns exactly the string Point@…. Your own class can describe itself.

Your own toString method

Let's add a method to the class Point that returns the coordinates:

JAVA
public class Main {
    public static void main(String[] args) {
        Point p = new Point();
        System.out.println(p); // (0, 0)
    }
}

class Point {
    int x;
    int y;
    public String toString() {
        return "(" + x + ", " + y + ")";
    }
}

Let's go through the program step by step:

  1. The class Point now has a method toString with the header public String toString().
  2. The method builds a string from the coordinates: "(" + x + ", " + y + ")".
  3. System.out.println(p) calls p.toString() by itself and prints the result: (0, 0).

What matters about toString:

  • the header is written exactly so: the name toString, no parameters, the result String;
  • public is required: without it the program does not compile;
  • such a method replaces the standard toString, and how this replacement works and why @Override is written above it are covered in the theme on inheritance.

Adding an object to a string with + calls its toString too:

JAVA
p.x = 3;
System.out.println("p = " + p); // p = (3, 0)

The null reference

A variable of type Point can hold a special reference, null: a slip with no address on it. It leads to no object. A field of type String or Point that was given nothing holds null, and you can put it into a variable yourself:

JAVA
Point r = p;
p = null;
System.out.println(r);         // (3, 0)
System.out.println(p);         // null
System.out.println(p == null); // true

Line by line:

  1. Point r = p; is a second slip with the address of the same point.
  2. p = null; erases the address only on the slip p. The point itself stays where it is.
  3. r still leads to it, and println prints (3, 0).
  4. println(p) prints the word null: there is no object, nothing to describe.
  5. p == null gives true. This is how you check that there is an object before working with it.

println and + handle null themselves and write the word null. But you cannot take a field or a method through such a variable: p.toString() with p equal to null stops the program.

Here the slip picture is not exact. With a blank slip, a person goes nowhere and gets back to their business, while Java stops the program at an attempt to go to a blank address.

Common mistake

A field is read through a variable that holds null:

JAVA
Point p = null;
System.out.println(p.x); // ✗
TEXT
Exception in thread "main" java.lang.NullPointerException: Cannot read field "x" because "<local1>" is null

The program compiles: the compiler does not track whether a variable holds null. The JVM finds the error when it reaches p.x: there is no object, so there is nowhere to take the field x from. The JVM throws a NullPointerException and stops the program. Interviewers ask about it all the time: it is one of the most common exceptions in Java.

The message tells you what happened:

  • Cannot read field "x": the field x could not be read;
  • because "<local1>" is null: because the variable holds null;
  • the next line of output, at Main.main(…), names the method and the line number.

Why <local1> and not p? java Main.java keeps no variable names, so the JVM refers to p by number: it is the first local variable after args. If you compile the program with javac -g Main.java and run java Main, the message gives the name: because "p" is null.

To avoid the error, create the object before reading it, Point p = new Point();, or check the reference: if (p != null).

Key points

  • println(p) and "p = " + p call p.toString(); your own public String toString() describes the object as a string, while the standard one prints Point@….
  • null is a reference that leads to no object; p = null erases the reference only in p.
  • A field or a method through null is a NullPointerException at run time, not a compile error.
  • The exception's message says what failed and which variable was null; without javac -g a number stands in place of the name, such as <local1>.