Introduction
toString() returns string representation of an object. Called automatically when printing objects.
Default Implementation
class Student {
String name;
int rollNo;
Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("John", 101);
System.out.println(s); // Student@15db9742
System.out.println(s.toString()); // Student@15db9742
}
}
Default Format: ClassName@hashCode
Overriding toString()
class Student {
String name;
int rollNo;
Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
@Override
public String toString() {
return "Student[name=" + name + ", rollNo=" + rollNo + "]";
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("John", 101);
System.out.println(s); // Student[name=John, rollNo=101]
}
}
Why Override toString()?
- Better debugging
- Readable output
- Logging
- Display information
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
// Without toString()
// Output: Product@1a2b3c4d (not helpful)
@Override
public String toString() {
return name + ": $" + price;
}
// Output: Laptop: $50000 (helpful!)
}
Automatic Invocation
toString() is called automatically:
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("John", 25);
// All these call toString() automatically
System.out.println(p); // John (25)
System.out.println("Person: " + p); // Person: John (25)
String s = "" + p; // Calls toString()
}
}
Different Formats
Simple Format:
@Override
public String toString() {
return name + " - " + rollNo;
}
// Output: John - 101
Detailed Format:
@Override
public String toString() {
return "Student[name=" + name + ", rollNo=" + rollNo + "]";
}
// Output: Student[name=John, rollNo=101]
JSON-like Format:
@Override
public String toString() {
return "{name: " + name + ", rollNo: " + rollNo + "}";
}
// Output: {name: John, rollNo: 101}
Multiline Format:
@Override
public String toString() {
return "Student:\n" +
" Name: " + name + "\n" +
" Roll No: " + rollNo;
}
// Output:
// Student:
// Name: John
// Roll No: 101
Complete Example
class BankAccount {
private String accountNumber;
private String holderName;
private double balance;
BankAccount(String accountNumber, String holderName, double balance) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = balance;
}
@Override
public String toString() {
return String.format("Account[%s, %s, $%.2f]",
accountNumber, holderName, balance);
}
}
public class Main {
public static void main(String[] args) {
BankAccount acc1 = new BankAccount("A001", "John", 5000.50);
BankAccount acc2 = new BankAccount("A002", "Alice", 10000.75);
System.out.println(acc1); // Account[A001, John, $5000.50]
System.out.println(acc2); // Account[A002, Alice, $10000.75]
}
}
Using String.format()
class Employee {
String name;
int id;
double salary;
Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
@Override
public String toString() {
return String.format("Employee[ID=%d, Name=%s, Salary=$%.2f]",
id, name, salary);
}
}
public class Main {
public static void main(String[] args) {
Employee emp = new Employee("John", 101, 50000.50);
System.out.println(emp);
// Employee[ID=101, Name=John, Salary=$50000.50]
}
}
toString() in Arrays
class Student {
String name;
int rollNo;
Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
@Override
public String toString() {
return name + "(" + rollNo + ")";
}
}
public class Main {
public static void main(String[] args) {
Student[] students = {
new Student("John", 101),
new Student("Alice", 102),
new Student("Bob", 103)
};
// Print array
for (Student s : students) {
System.out.println(s); // Calls toString()
}
// Output:
// John(101)
// Alice(102)
// Bob(103)
}
}
toString() in Collections
import java.util.ArrayList;
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return name + ": $" + price;
}
}
public class Main {
public static void main(String[] args) {
ArrayList<Product> products = new ArrayList<>();
products.add(new Product("Laptop", 50000));
products.add(new Product("Mouse", 500));
products.add(new Product("Keyboard", 1500));
System.out.println(products);
// [Laptop: $50000.0, Mouse: $500.0, Keyboard: $1500.0]
}
}
Inheritance and toString()
class Person {
protected String name;
protected int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person[name=" + name + ", age=" + age + "]";
}
}
class Student extends Person {
private int rollNo;
private String course;
Student(String name, int age, int rollNo, String course) {
super(name, age);
this.rollNo = rollNo;
this.course = course;
}
@Override
public String toString() {
return "Student[name=" + name + ", age=" + age +
", rollNo=" + rollNo + ", course=" + course + "]";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("John", 25);
Student s = new Student("Alice", 20, 101, "BCA");
System.out.println(p); // Person[name=John, age=25]
System.out.println(s); // Student[name=Alice, age=20, rollNo=101, course=BCA]
}
}
Using super.toString()
class Person {
protected String name;
protected int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
class Employee extends Person {
private String empId;
private double salary;
Employee(String name, int age, String empId, double salary) {
super(name, age);
this.empId = empId;
this.salary = salary;
}
@Override
public String toString() {
return "Employee[" + super.toString() +
", empId=" + empId + ", salary=$" + salary + "]";
}
}
public class Main {
public static void main(String[] args) {
Employee emp = new Employee("John", 30, "E001", 50000);
System.out.println(emp);
// Employee[name=John, age=30, empId=E001, salary=$50000.0]
}
}
StringBuilder for Performance
For complex toString(), use StringBuilder:
class Student {
String name;
int rollNo;
String[] subjects;
Student(String name, int rollNo, String[] subjects) {
this.name = name;
this.rollNo = rollNo;
this.subjects = subjects;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Student[name=").append(name)
.append(", rollNo=").append(rollNo)
.append(", subjects=");
for (int i = 0; i < subjects.length; i++) {
sb.append(subjects[i]);
if (i < subjects.length - 1) sb.append(", ");
}
sb.append("]");
return sb.toString();
}
}
public class Main {
public static void main(String[] args) {
String[] subs = {"Math", "Physics", "Chemistry"};
Student s = new Student("John", 101, subs);
System.out.println(s);
// Student[name=John, rollNo=101, subjects=Math, Physics, Chemistry]
}
}
toString() for Debugging
class Circle {
private double radius;
private String color;
Circle(double radius, String color) {
this.radius = radius;
this.color = color;
}
double area() {
return Math.PI * radius * radius;
}
@Override
public String toString() {
return String.format("Circle[radius=%.2f, color=%s, area=%.2f]",
radius, color, area());
}
}
public class Main {
public static void main(String[] args) {
Circle c = new Circle(5.0, "Red");
// Easy debugging
System.out.println("Created: " + c);
// Created: Circle[radius=5.00, color=Red, area=78.54]
}
}
Real-World Example
class Order {
private String orderId;
private String customerName;
private double totalAmount;
private String status;
Order(String orderId, String customerName, double totalAmount, String status) {
this.orderId = orderId;
this.customerName = customerName;
this.totalAmount = totalAmount;
this.status = status;
}
@Override
public String toString() {
return String.format("Order #%s | Customer: %s | Amount: $%.2f | Status: %s",
orderId, customerName, totalAmount, status);
}
}
public class Main {
public static void main(String[] args) {
Order order1 = new Order("ORD001", "John Smith", 2500.50, "Pending");
Order order2 = new Order("ORD002", "Alice Brown", 1500.75, "Shipped");
Order order3 = new Order("ORD003", "Bob Wilson", 3000.00, "Delivered");
System.out.println("=== Orders ===");
System.out.println(order1);
System.out.println(order2);
System.out.println(order3);
// Output:
// === Orders ===
// Order #ORD001 | Customer: John Smith | Amount: $2500.50 | Status: Pending
// Order #ORD002 | Customer: Alice Brown | Amount: $1500.75 | Status: Shipped
// Order #ORD003 | Customer: Bob Wilson | Amount: $3000.00 | Status: Delivered
}
}
Common Patterns
Pattern 1: Simple
@Override
public String toString() {
return field1 + " " + field2;
}
Pattern 2: Formatted
@Override
public String toString() {
return ClassName + "[" + field1 + ", " + field2 + "]";
}
Pattern 3: With Labels
@Override
public String toString() {
return "ClassName{field1=" + field1 + ", field2=" + field2 + "}";
}
String Concatenation vs StringBuilder
String Concatenation (Simple):
@Override
public String toString() {
return "Student[" + name + ", " + rollNo + "]";
}
StringBuilder (Better Performance):
@Override
public String toString() {
return new StringBuilder()
.append("Student[")
.append(name)
.append(", ")
.append(rollNo)
.append("]")
.toString();
}
null Handling
class Person {
String name;
String city;
Person(String name, String city) {
this.name = name;
this.city = city;
}
@Override
public String toString() {
return "Person[name=" +
(name != null ? name : "Unknown") +
", city=" +
(city != null ? city : "Not specified") +
"]";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person(null, null);
System.out.println(p);
// Person[name=Unknown, city=Not specified]
}
}
Quick Reference
class Example {
String field1;
int field2;
// Simple toString()
@Override
public String toString() {
return "Example[field1=" + field1 + ", field2=" + field2 + "]";
}
// Using String.format()
@Override
public String toString() {
return String.format("Example[field1=%s, field2=%d]", field1, field2);
}
// Using StringBuilder
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Example[field1=").append(field1)
.append(", field2=").append(field2)
.append("]");
return sb.toString();
}
}
// Automatic calls
Example e = new Example();
System.out.println(e); // Calls toString()
String s = "Value: " + e; // Calls toString()
System.out.println(e.toString()); // Explicit call
Common Mistakes
Mistake 1: Infinite Recursion
class Student {
String name;
@Override
public String toString() {
return "Student: " + this; // ✗ Calls toString() again!
}
}
// ✓ Correct
@Override
public String toString() {
return "Student: " + name;
}
Mistake 2: Not Overriding
class Student {
String name;
// ✗ Wrong - doesn't override
public String ToString() { // Capital T
return name;
}
}
// ✓ Correct
@Override
public String toString() {
return name;
}
Exam Tips
Remember:
- toString() returns string representation
- Default format:
ClassName@hashCode - Called automatically when printing
- Override for readable output
- Used in debugging and logging
- Returns String
- No parameters
- Use @Override annotation
- StringBuilder for complex strings
- Handle null values
Common Questions:
- What is toString()?
- Default implementation?
- Why override toString()?
- When is it called automatically?
- How to format output?
- toString() vs equals()?
- How to handle null fields?
- String concatenation vs StringBuilder?