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.

# 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.

a = [1, 2]
b = [1, 2]
print(a == b)    # True — same contents
print(a is b)    # False — different objects

Value copy versus reference copy

Copying a Java int copies the value, so the two ints stay independent. Copying an array variable copies the reference, so both names see a change.

The int a is a primitive, so its slot holds five.

Text version
  1. The int a is a primitive, so its slot holds five.
  2. Assigning a to b copies the value five into a new slot.
  3. Setting b to ninety-nine changes only b, so a stays five.
  4. The array variable xs holds a reference to the array.
  5. Assigning xs to ys copies the reference, so both names point to one array.
  6. Writing ninety-nine through ys changes that one array, so xs sees it too.
  7. Printing xs at index zero shows ninety-nine.
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 (cannot be 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).

Copying and equality depend on the language

Only Java and C++ have primitives. The three languages also differ in argument passing and in the tests for identity and content.

Python has no primitives, while Java and C plus plus both have them.

Text version
  1. Python has no primitives, while Java and C plus plus both have them.
  2. By default, passing an argument copies a reference in Python, a primitive value or object reference in Java, and a value in C plus plus.
  3. For identity, Python uses the is operator, while Java objects and C plus plus pointers use double equals.
  4. For content, Python lists use double equals, Java strings use equals, and C plus plus uses double equals if the type defines it.
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, const 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.

Reading

On this page

My notes

Sign in to keep highlights and notes.

Glossary history

Click dotted jargon to save explanations here.