5 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
Primitives vs References
1. What Is It?
A is a value stored directly in the slot that holds it — an int, a double, a char. The slot is the value. A (or pointer) is a slot that doesn't hold the value itself but an address pointing to where the value lives — usually on the . The name "" fits literally: the refers to the data.
Without this distinction you get stuck on bugs like "I passed a list into a function and the function changed it — why did my caller's list change too?" or "I compared two strings with == and got false even though they look identical." Both are primitives-vs-references surprises.
A backend developer passes a list of user IDs into a helper function to filter out duplicates. After the function returns, the original list in the caller has also been modified. What is the most likely explanation for this behavior?
2. How It Works
A holds its value inline. Copying the copies the value — the two are now independent. A variable holds an address. Copying the variable copies the address — both names now refer to the same . Mutating the through one name is visible through the other.
// Java — primitives vs references int a = 5; int b = a; // b is a fresh slot with the value 5; copies VALUE b = 99; // a is still 5 int[] xs = {1, 2, 3}; int[] ys = xs; // ys holds the SAME reference as xs; copies REFERENCE ys[0] = 99; System.out.println(xs[0]); // 99 — same underlying array
// C++ — value vs pointer vs reference int a = 5; int b = a; // value copy int* p = &a; // p holds the address of a *p = 99; // a is now 99 int& r = a; // r is another name for a (no address-taking syntax at use site) r = 7; // a is now 7
# Python — everything is a reference a = [1, 2, 3] b = a # b refers to the SAME list b.append(4) print(a) # [1, 2, 3, 4] x = 5 y = x # names look rebound, but ints are immutable — you can't mutate them y = 99 # y now refers to a different int object; x unchanged
Equality is the other classic trap
With primitives, == compares values. With references, == typically compares addresses (are they the same object?), not contents. This is why Java has equals(), Python has == (content) vs. is (identity), and C++ overloads == per type.
String s1 = new String("hi"); String s2 = new String("hi"); System.out.println(s1 == s2); // false — different objects System.out.println(s1.equals(s2)); // true — same content
a = [1, 2] b = [1, 2] print(a == b) # True — same contents print(a is b) # False — different objects
A backend developer writes the following Java code to update a user's profile before sending it to two different downstream services:
What email address willUserProfile original = getUserProfile(userId); UserProfile copy = original; copy.setEmail("new@example.com"); sendToServiceA(original); sendToServiceB(copy);
serviceA receive?3. What You Actually Need to Know
- Assignment of a does not copy the . It copies the address. To actually duplicate data, you need an explicit copy (
list(x),copy.deepcopy(x),Arrays.copyOf(arr, ...),std::vector<T> copy = original;). - Passing to a function follows the same rule. A is copied; a is aliased. If the callee mutates the , the caller sees it.
- Null/None exists only for references. A
intcan't be null in Java. A reference (Integer,String, any object) can.NullPointerExceptionis always a reference bug. - Python has no primitives — but small values (small ints, interned strings) behave as if they were primitives because you can't mutate them. The distinction collapses for ints and strings but reappears for lists/dicts/custom objects.
- Debugging clues:
- "My caller's data changed unexpectedly" → you passed a reference and the callee mutated it.
NullPointerException/AttributeError: 'NoneType' object has no attribute 'X'→ reference was null/None.==returning false for identical-looking objects → you're comparing identities, not contents.
A backend developer writes a function process_order(order) that modifies the order's status field in place. After calling process_order(my_order), the developer notices that my_order.status has changed in the calling code — even though they didn't intend for the original object to be modified. What is the most likely cause?
4. Language Differences
| Aspect | Python | Java | C++ |
|---|---|---|---|
| Has primitives? | No (everything is an object reference) | Yes — int, long, double, char, boolean, etc. | Yes — all fundamental types, plus any struct/class used by value |
| Default pass to function | Reference copy | Primitives copy; objects copy reference | Value copy (reference/pointer requires explicit & or *) |
== on objects | Content (via __eq__); is for identity | Identity for objects (unless overridden); use .equals() for content | Identity for pointers (p1 == p2); content for values (if == is defined) |
| Can be null/None? | Reference types yes (None) | Reference types yes (null); primitives no | Pointers yes (nullptr); values no; references must bind to something |
| Explicit deep copy | copy.deepcopy(x) | Object.clone() (shallow) or custom | Copy constructors / std::copy |
Java's split between int and boxed Integer is a frequent source of bugs: Integer a = 128; Integer b = 128; a == b; can be false because the boxed objects aren't the same instance (outside the small-int cache).
A Java backend service caches frequently used user IDs as Integer objects and compares them using == to check equality. During testing with IDs under 128 the comparisons work correctly, but IDs above 127 intermittently return false even when the values are identical. What is the most likely cause?
5. Tradeoffs & Decisions
- Efficiency. Primitives live inline — no allocation, no pointer dereference, cache-friendly. References add a level of indirection. For a million-element numeric array,
int[]in Java destroysInteger[]on both memory and speed. - Sharing vs. isolation. References let multiple owners see the same state — great for a single source of truth, dangerous when one owner mutates behind another's back. Primitives (or explicit copies) give isolation at the cost of memory.
- Nullability. types can be null, so every -dereference is a potential crash. Static analyzers,
Optional<T>,std::optional, and Python's type hints (Optional[X]) all exist to force you to think about this.
If you see two variables move in lockstep unexpectedly, it usually means they reference the same . You'd choose a copy (or an type like tuple, String, std::string) when the caller and callee must not see each other's changes.
A backend service builds a shopping cart object and passes it to both a pricing module and a discount module. After the call, the service notices the cart's item list has been unexpectedly modified. Which design change most directly prevents this unintended mutation?
6. Interview Cheat Sheet
- A holds its value directly in its memory slot. A holds an address pointing to the actual , usually on the .
- Copying or passing a duplicates the value; the two slots are independent. Copying or passing a duplicates the address; both names see and can mutate the same .
==semantics differ: in Java it's identity for objects (use.equals()for content); in Python==is content andisis identity; in C++ pointer==is identity, value==is content (if defined).- Java has both: primitives (-efficient, not null-able) and reference types (-allocated, null-able). Python has only references. C++ lets you choose per variable.
Follow-ups:
- "Why did my function mutate the caller's list?" — Python passed a reference; the callee and caller share the same list object.
- "What's the difference between
==and.equals()in Java?" —==compares addresses (identity)..equals()compares content (if overridden, as inString). - "Why is
Integerslower thanintin Java?" —Integeris a heap object requiring boxing/unboxing and an extra pointer dereference.intlives directly on the or inline in the enclosing object.
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.