ToString Method

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()?

  1. Better debugging
  2. Readable output
  3. Logging
  4. 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:

  1. toString() returns string representation
  2. Default format: ClassName@hashCode
  3. Called automatically when printing
  4. Override for readable output
  5. Used in debugging and logging
  6. Returns String
  7. No parameters
  8. Use @Override annotation
  9. StringBuilder for complex strings
  10. 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?