Primitives vs References

5 min read

Reading Progress0%
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
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.

QUICK CHECK

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?

Choose one answer

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
QUICK CHECK

A backend developer writes the following Java code to update a user's profile before sending it to two different downstream services:

UserProfile original = getUserProfile(userId);
UserProfile copy = original;
copy.setEmail("new@example.com");
sendToServiceA(original);
sendToServiceB(copy);
What email address will serviceA receive?

Choose one answer

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 int can't be null in Java. A reference (Integer, String, any object) can. NullPointerException is 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.
QUICK CHECK

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?

Choose one answer

4. Language Differences

AspectPythonJavaC++
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 functionReference copyPrimitives copy; objects copy referenceValue copy (reference/pointer requires explicit & or *)
== on objectsContent (via __eq__); is for identityIdentity for objects (unless overridden); use .equals() for contentIdentity for pointers (p1 == p2); content for values (if == is defined)
Can be null/None?Reference types yes (None)Reference types yes (null); primitives noPointers yes (nullptr); values no; references must bind to something
Explicit deep copycopy.deepcopy(x)Object.clone() (shallow) or customCopy 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).

QUICK CHECK

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?

Choose one answer

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 destroys Integer[] 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.

QUICK CHECK

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?

Choose one answer

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 and is is 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 in String).
  • "Why is Integer slower than int in Java?"Integer is a heap object requiring boxing/unboxing and an extra pointer dereference. int lives 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.