Java toString() Method
Table of Contents + β
In the last lesson you learned about the Java Object class. One free method it hands you is toString(). The trouble: print your object and you get junk like Student@1b6d3586 instead of the name and grade.
The fix is to override toString() so your object prints a clear, human description of itself. Letβs learn how.
π€ What the default toString() gives you
- Every object already has a
toString(), inherited fromObject. - When you print an object, Java calls it for you.
- The trouble is the default version is almost useless.
class Student { String name; int grade;
Student(String name, int grade) { this.name = name; this.grade = grade; }}
public class Main { public static void main(String[] args) { Student s = new Student("Alex", 90); System.out.println(s); // prints the default toString() }}Output
Student@1b6d3586That is the default toString() in action.
- It returns the class name, an at sign, then a hex code.
- The code is the objectβs hash code in hex. It is not the memory address.
- It shows no name and no grade. It tells you nothing about the object.
So we need to replace it.
π οΈ Overriding toString()
To fix this you write your own toString() that returns a String describing the object. Put @Override on top so the compiler checks the signature:
class Student { String name; int grade;
Student(String name, int grade) { this.name = name; this.grade = grade; }
@Override public String toString() { return "Student{name=" + name + ", grade=" + grade + "}"; }}Now the same System.out.println(s) prints the real data.
public class Main { public static void main(String[] args) { Student s = new Student("Alex", 90); System.out.println(s); // now calls YOUR toString() }}Output
Student{name=Alex, grade=90}- The method must be
public String toString()with no parameters, matchingObject. @Overrideforces the compiler to confirm this.- Typo the name or add a parameter, and the build fails instead of silently doing nothing.
πͺ Java calls it automatically
You never typed s.toString() above, yet your version ran. Java calls it for you in two common spots:
- When you print an object.
System.out.println(s)callss.toString()first. - When you join an object to a
Stringwith+.
Watch both at once.
public class Main { public static void main(String[] args) { Student s = new Student("Alex", 90);
System.out.println(s); // printing calls toString() String line = "Top: " + s; // joining with + calls toString() System.out.println(line); }}Output
Student{name=Alex, grade=90}Top: Student{name=Alex, grade=90}Java needed a String, so it called toString() on the student and pasted the result in.
- Write the method once, and the object reads cleanly everywhere it turns into text.
- You get it in prints, joins, and logs.
- Experienced developers add it on almost every new class.
π§± Building a clear toString()
A good toString() includes the key fields with labels, in the style ClassName{field1=value1, field2=value2}:
class Account { String owner; String id; double balance;
Account(String owner, String id, double balance) { this.owner = owner; this.id = id; this.balance = balance; }
@Override public String toString() { return "Account{owner=" + owner + ", id=" + id + ", balance=" + balance + "}"; }}
public class Main { public static void main(String[] args) { Account a = new Account("Maria", "AC-100", 250.0); System.out.println(a); }}Output
Account{owner=Maria, id=AC-100, balance=250.0}- The labels
owner=andbalance=tell a reader which value is which. - Without them you just see two numbers and a name. You have to guess.
- Include fields that tell objects apart, like a name or an id.
- Skip huge or noisy fields, or replace them with a short summary like a listβs size.
βοΈ IDEs and records can write it for you
Typing toString() by hand gets boring, and you rarely have to.
- Every major editor generates it. Pick βtoString()β from a menu, then choose the fields.
- The editor writes a correct method. You read it, tweak it, move on.
- A record builds
toString()automatically, no code needed.
record Point(int x, int y) { }
public class Main { public static void main(String[] args) { Point p = new Point(3, 5); System.out.println(p); // record made toString() for us }}Output
Point[x=3, y=5]We never wrote a toString(), yet the record printed a clean line.
- For plain data holders, a record gives you a readable
toString()for free. - For regular classes, let the editor generate it.
π toString() for debugging and logging
A readable toString() lets you see object state the instant something goes wrong. Printing a whole list is the classic case, because a list calls toString() on each item.
import java.util.List;
public class Main { public static void main(String[] args) { List<Student> roster = List.of( new Student("Alex", 90), new Student("Maria", 85) ); System.out.println(roster); // each Student uses its toString() }}Output
[Student{name=Alex, grade=90}, Student{name=Maria, grade=85}]- The list printed every student cleanly because each had a good
toString(). - Without the override you would get a line full of
Student@...codes. - Logging works the same way.
logger.info("Saving " + student)callstoString()through the+. - That turns a mystery bug into a quick read.
β οΈ Common Mistakes
A few toString() slip-ups come up again and again.
- Not overriding it at all. You print the object and get a useless
ClassName@hash. Always add atoString()to classes you plan to print or log.
class Student { // β no toString(), prints Student@1b6d3586 String name; int grade;}class Student { String name; int grade;
@Override public String toString() { // β
readable output return "Student{name=" + name + ", grade=" + grade + "}"; }}- Forgetting @Override. Without it, a typo in the name or a stray parameter creates a brand new method, and the useless default still runs with no warning.
public String tostring() { // β wrong name, silently NOT an override return "Student{name=" + name + "}";}@Overridepublic String toString() { // β
compiler verifies the signature return "Student{name=" + name + "}";}- Exposing sensitive fields. Never dump passwords, tokens, or card numbers into
toString(). They end up in logs and on screens where they do not belong.
@Overridepublic String toString() { return "User{name=" + name + ", password=" + password + "}"; // β leaks the secret}@Overridepublic String toString() { return "User{name=" + name + "}"; // β
leave secrets out}β Best Practices
A few habits keep your toString() useful and safe.
- Always write
@Override. It catches a wrong name or signature at compile time, the moment you build. - Include the key fields with labels. Use a
field=valuestyle so a reader knows what each value means without guessing. - Leave out secrets and noise. Skip passwords and tokens. Summarize big fields, like printing a listβs size instead of its full contents.
- Let the editor or a record generate it. Hand-typing is error-prone for many fields. Generated code is correct and fast.
- Keep it cheap and side-effect free.
toString()runs a lot during debugging and logging, so it should not do heavy work or change anything. - Make it readable, not parseable.
toString()is for humans reading logs. If you need machine-readable data, use a proper format instead.
π§© What Youβve Learned
Nicely done. Letβs recap the toString() method.
- β
The default
toString()fromObjectreturnsClassName@hexhashcode, which tells you nothing useful. - β
You override
toString()aspublic String toString()with@Overrideto return a readable description. - β
Java calls it automatically when you print an object or join it to a
Stringwith+. - β
A good
toString()includes the key fields with labels, in a style likeClassName{field=value}. - β IDEs can generate it, and records create it for you automatically.
- β
A clear
toString()makes debugging and logging fast, including printing whole lists of objects. - β
Never put sensitive fields like passwords or tokens into
toString().
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does the default toString() inherited from Object return?
Why: By default toString() returns the class name, an at sign, and the object's hash code in hex, which is not useful.
- 2
When does Java call toString() automatically?
Why: Printing an object and concatenating it with a String both call toString() for you.
- 3
Why should you put @Override on your toString()?
Why: @Override makes the compiler confirm you correctly match Object's toString(), turning typos into compile errors.
- 4
Which field should you NOT include in toString()?
Why: Secrets like passwords and tokens must stay out of toString() so they do not leak into logs or screens.
π Whatβs Next?
You can now make any object print a clear description of itself. Next you will learn the other famous Object method, the one that decides when two objects count as the same. It is just as common to override, and it has a few rules you need to know.