<code>toString()</code> in Java
toString() returns a human-readable representation of an object. Every class inherits one from Object β the default prints ClassName@1a2b3c, useless for debugging. Override it on anything you'll see in logs, exception messages or debugger output.
Default vs overridden
class UserNoToString {
String name; int age;
}
new UserNoToString().toString(); // "UserNoToString@5e8c92f4" β the hashCode in hex
class User {
String name; int age;
@Override
public String toString() {
return "User{name=" + name + ", age=" + age + "}";
}
}
new User().toString(); // "User{name=null, age=0}"
Records: automatic
public record User(String name, int age) {}
new User("Alice", 30).toString(); // "User[name=Alice, age=30]"
Conventions
- Include the class name β so you can tell types apart in logs.
- Include every field that matters for identity or debugging.
- Skip large byte arrays, streams, and secrets (passwords, tokens).
- Keep it short enough to fit on one log line.
Where it matters most
log.error("Payment failed for " + order); // calls order.toString()
throw new IllegalStateException("invalid " + config); // message shows config
System.out.println(Map.of("a", user)); // map.toString recursively calls values
Formatting tools
// Commons Lang
ToStringBuilder.reflectionToString(this);
// Manual with StringBuilder
return new StringBuilder("User{")
.append("name=").append(name)
.append(", age=").append(age)
.append('}')
.toString();
// Java 15+ text blocks for multi-line
return """
User {
name: %s
age: %d
}
""".formatted(name, age);
Common mistakes
- Leaking secrets β
Credentials.toString()must not show the password. Replace with***. - Recursive
toStringβ parent β child references blow the stack. Print IDs only for references to other managed objects. - Expensive
toStringβ remember it's called inside log statements that might be disabled. Use a lazySupplieror SLF4J's{}placeholder.
Related
Pillar: Java methods. See also records, equals & hashCode.