7 min read
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
Classes & Objects
1. What Is It?
A is a blueprint describing what data a thing has (fields) and what it can do (methods). An is one concrete instance produced from that blueprint. If Car is a , your specific car toyota_at_VIN_123 is an .
Classes and objects solve a problem plain functions and structs don't handle well: bundling data with the behavior that operates on it, and doing so in a way that hides internal details from the rest of the program. Without them, related data and functions drift apart and grow tangled; with them, you can change a class's internals without touching every caller.
A team is building a payment processing service. They store transaction data in a plain struct and handle all validation, formatting, and fee calculations in scattered utility functions throughout the codebase. When the fee calculation logic changes, they must hunt down and update every function that touches transaction data. Which design change best addresses this problem?
2. How It Works
When you create an , memory is allocated for its fields, and the 's methods become callable through that . Each method implicitly knows which object it's operating on — that's what self (Python) or this (Java/C++) refers to.
# Python class Circle: def __init__(self, radius): self.radius = radius # instance field def area(self): # method; 'self' is the object return 3.14159 * self.radius ** 2 c = Circle(5) # construct print(c.area()) # 78.53975
// Java public class Circle { private double radius; // field public Circle(double radius) { // constructor this.radius = radius; } public double area() { // method return Math.PI * radius * radius; } } Circle c = new Circle(5); System.out.println(c.area());
// C++ class Circle { public: Circle(double radius) : radius_(radius) {} double area() const { return 3.14159 * radius_ * radius_; } private: double radius_; }; Circle c(5); // stack object Circle* p = new Circle(5); // heap object; must 'delete p' later
Construction and destruction
- Constructor — runs when an object is created. Initializes fields, validates inputs.
- Destructor — runs when the object is destroyed. Releases resources.
Python has __init__ (runs on creation) and __del__ (runs when garbage collected — unreliable timing). Java has constructors and finalize() (deprecated; don't use). C++ has constructors and destructors that run deterministically as ends — which is what enables RAII (Resource Acquisition Is Initialization: bind a resource's lifetime to an object, so the destructor releases it automatically when the object goes out of ).
Instance fields vs. class (static) fields
- Instance field — one copy per object.
self.radiusin Python, field declarations in Java/C++. - field — one copy shared across all instances. Used for constants, counters, shared configuration.
class Counter: total = 0 # class-level, shared def __init__(self): self.id = Counter.total Counter.total += 1 a = Counter(); b = Counter() print(a.id, b.id, Counter.total) # 0 1 2
public class Counter { static int total = 0; // class-level int id; public Counter() { this.id = total++; } }
Encapsulation: hiding internals
Objects expose a public API (methods) and hide their data. Callers should go through methods, not reach into fields. This lets you change the internal representation without breaking callers.
// Java — explicit access modifiers public class Account { private double balance; public double getBalance() { return balance; } public void deposit(double amount) { if (amount <= 0) throw new IllegalArgumentException(); balance += amount; } }
# Python — convention, not enforcement class Account: def __init__(self): self._balance = 0 # leading underscore = "internal" def deposit(self, amount): if amount <= 0: raise ValueError() self._balance += amount @property def balance(self): return self._balance # read-only public view
A backend service uses a DatabaseConnection class that opens a connection in the constructor. In C++, when a DatabaseConnection object goes out of scope, the destructor automatically closes the connection and releases resources — without the caller needing to do anything. What design principle does this demonstrate?
3. What You Actually Need to Know
- Methods are just functions that take the as a hidden first argument. Python makes it explicit (
def area(self):); Java and C++ make it implicit (thisis magic). self/thisresolves method calls to the right .c1.area()andc2.area()run the same code but on different data.- Don't expose internal state. Returning the internal list lets callers mutate it behind your back. Return a copy or an view.
- Constructors should leave the object in a valid state — no "half-constructed" objects. If arguments are invalid, throw; don't leave fields at defaults.
- Avoid "god classes". A with 30 methods and 15 fields usually wants to be two or three classes. One responsibility per is the rule of thumb — each class should have a single, clear reason to exist.
- Python's access control is advisory.
_namemeans "treat as private";__nametriggers name mangling but can still be reached. Java'sprivateis compile-time enforced; C++'sprivateis also enforced. - Debugging clues:
AttributeError: ... has no attribute 'X'→ you accessed a field that wasn't set (often forgotten in__init__).NullPointerExceptionon a field access → the field is a and was never initialized.- Uninitialized fields with garbage values (C++) → you declared but didn't initialize; use a constructor with an initializer list.
- Shared state surprise across instances → you wrote to what looked like an instance field but was actually class-level.
A ShoppingCart class stores items in an internal list and has a method get_items() that returns self._items directly. A developer calls cart.get_items().append('free_item') without going through any cart method. What problem does this cause, and what is the correct fix?
4. Language Differences
| Aspect | Python | Java | C++ |
|---|---|---|---|
| Access control | Convention (_, __) | Enforced (public/protected/private) | Enforced (same keywords) |
| Everything is an object? | Yes | No — primitives aren't | No — fundamental types aren't |
| Multiple inheritance | Yes (MRO) | No (only interfaces) | Yes |
| Constructor syntax | __init__ | ClassName(...) | ClassName(...) with optional initializer list |
| Destructor | __del__ (non-deterministic) | finalize (deprecated) | ~ClassName() (deterministic, load-bearing for RAII) |
| Object memory | Always on heap | Always on heap | Stack or heap — your choice |
this reference | self, explicit first parameter | this, implicit | this (pointer), implicit |
| Data classes / POJOs | @dataclass decorator | record (Java 14+) | struct (same as class, default-public) |
Python's "everything is an " is literal: even integers have methods ((5).bit_length()). Java separates primitives from objects. C++ gives you value-type objects (on the , fast) and /pointer-type objects (on the ).
A performance-critical backend service written in C++ needs to allocate thousands of small configuration objects per second. A developer argues these objects should be created on the stack rather than the heap. Which language feature makes this possible in C++ but not in Python or Java?
5. Tradeoffs & Decisions
- vs. just a function + dict. If your "" has no methods and no invariants, a dictionary or tuple is fine. Classes earn their keep when you have invariants to enforce (balance ≥ 0) or a nontrivial API.
- Public fields vs. getters/setters. Python culture prefers public fields with
@propertyif you later need validation. Java culture historically uses getters/setters everywhere (IDEs generate them). Both work; the key is that callers go through a stable API. - vs. . creates tight coupling between parent and child. (holding another as a field) is looser. Reach for composition first; inheritance when you genuinely need "is-a" semantics and .
- vs. objects. objects are safer across threads and easier to cache. objects are necessary when state genuinely changes.
Stringis immutable in Python and Java;StringBuilder/listis mutable. - Value semantics vs. semantics. C++
Circle c(5);is a value — copy makes a new circle. Java/PythonCircle c = new Circle(5);is always a — copying the shares the .
If you see a class with public fields, no methods, and no validation, it might as well be a dict / record / struct. You'd choose a full class when there are invariants to protect or meaningful behavior to attach.
A junior developer on your team creates a UserProfile class with three public fields (name, email, age), no methods, and no validation logic. A code reviewer suggests replacing it with a plain dictionary. Which situation would best justify keeping it as a full class instead?
6. Interview Cheat Sheet
- A is a blueprint (fields + methods); an is a concrete instance produced from the blueprint.
- Methods are functions with an implicit to the (
selfin Python,thisin Java/C++). - Instance fields are per-object; (static) fields are shared by all instances.
- Constructors run on creation and must leave the object in a valid state. Destructors (C++) run deterministically on exit; Python/Java rely on GC.
- hides implementation behind a stable public API so you can change internals without breaking callers.
- Python enforces access control by convention; Java and C++ enforce it at compile time.
Follow-ups:
- "What's the difference between a class and an object?" — A class is the template; an object is one instantiation of it.
Circlevs.c = Circle(5). - "What's
selfin Python?" — The object the method is being called on. Explicit first parameter by convention. - "When should you use a class instead of a dict?" — When you need to enforce invariants, attach behavior, or offer a stable API that can evolve without breaking callers.
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.