Definition
Object class facts:
- Root of all classes. Every class extends Object, even if not declared explicitly.
- 11 methods inherited by every object: toString, equals, hashCode, getClass, clone, finalize (deprecated), wait (3 overloads), notify, notifyAll.
- Default implementations: toString returns className@hexHashCode, equals uses ==, hashCode returns a memory-based value.
Object is the standard bank form template. Every specific form (Account, Transaction) is based on this template, even if nobody explicitly says so. The template provides generic features like a form number (hashCode), a description field (toString), and an identity check (equals).
Understanding the Concepts
Object methods overview
public class Account {
// Implicitly: extends Object
private String accountId;
private BigDecimal balance;
}
Account a = new Account();
// toString: default is className@hex
System.out.println(a.toString()); // com.bank.Account@1a2b3c4d
// equals: default is == (reference comparison)
Account b = new Account();
System.out.println(a.equals(b)); // false (different objects)
// hashCode: default is memory-based
System.out.println(a.hashCode()); // some integer
// getClass: runtime class
System.out.println(a.getClass().getName()); // com.bank.Account
Overriding toString
@Override
public String toString() {
return "Account{id=" + accountId + ", balance=" + balance + "}";
}
// Now: System.out.println(a) → Account{id=SAV2001, balance=500000}
Which methods should you override?
| Method | Override? | Why |
|---|---|---|
| toString() | Yes | Readable logging and debugging |
| equals() | Yes, for value objects | Correct equality comparison |
| hashCode() | Yes, if equals() is overridden | HashMap/HashSet correctness |
| clone() | Rarely | Prefer copy constructor |
| finalize() | Never | Deprecated since Java 9 |
What should a senior engineer know?
- Records auto-generate toString, equals, hashCode from all components.
- Lombok @Data generates toString, equals, hashCode, getters, setters.
- getClass() vs instanceof in equals: getClass() is strict (exact type match), instanceof allows subclass equality. Choose based on your hierarchy design.
What are common mistakes?
A common mistake is overriding equals() without overriding hashCode(), which breaks HashMap and HashSet behavior.
Wrong approach:
package com.bank.model;
public class Account {
private String accountId;
// WRONG: overrides equals but not hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Account)) return false;
return accountId.equals(((Account) o).accountId);
}
// Missing hashCode override!
}
// This breaks HashMap lookups
Map<Account, String> map = new HashMap<>();
Account a1 = new Account("SAV2001");
map.put(a1, "Rajesh");
Account a2 = new Account("SAV2001");
System.out.println(map.get(a2)); // null! Different hashCode
Correct approach:
package com.bank.model;
import java.util.Objects;
public class Account {
private String accountId;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Account)) return false;
return accountId.equals(((Account) o).accountId);
}
@Override
public int hashCode() {
return Objects.hash(accountId);
}
}
Now both a1 and a2 produce the same hash code, so HashMap lookups work correctly.
What are the edge cases?
Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.
package com.bank.edge;
public class EdgeCaseDemo {
public static void main(String[] args) {
// Edge case 1: null handling
String accountId = null;
try {
System.out.println(accountId.length());
} catch (NullPointerException e) {
System.out.println("Null account ID");
}
// Edge case 2: empty vs null
String emptyId = "";
System.out.println(emptyId.isEmpty()); // true
System.out.println(emptyId == null); // false
// Edge case 3: boundary values
java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
System.out.println(zero.compareTo(negativeBalance) > 0); // true
}
}
Null account ID
true
false
true
Always test with null values, empty strings, boundary values, and negative amounts in banking applications.
What are common interview follow-up questions?
Interviewers often follow up with these related questions:
- How does this concept apply to a high-throughput banking transaction system?
- What are the performance implications in a production environment?
- How would you debug issues related to this concept?
- What tools would you use to monitor this in production?
- How does this behave differently in Java 8 vs Java 17+?
When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.
Visual Overview
java.lang.Object
│
├── toString() → "Account@1a2b3c4d" (override for readability)
├── equals() → == by default (override for value comparison)
├── hashCode() → memory-based (override with equals)
├── getClass() → runtime Class object
├── clone() → shallow copy (prefer copy constructor)
├── finalize() → deprecated, never use
├── wait() → thread synchronization
├── notify() → thread synchronization
└── notifyAll() → thread synchronization
Every class extends Object implicitly.
Key Takeaway
java.lang.Object is the root of every Java class. It provides default implementations of toString (class@hash), equals (reference comparison), hashCode (memory-based), and other methods. Override toString for readable output, equals for value comparison, and hashCode to match equals for HashMap correctness.
One sentence to remember: every class extends Object, which provides toString, equals, hashCode, and getClass – override the first three for meaningful behavior.
---