Skip to content
beginnerPhase 9 · Java Foundations

Reference Types

Understand objects, arrays, and references vs primitives.

45m
2 problems
Topic Progress0%

Reference Types Overview

What are Reference Types?

Reference types are any type that is not a primitive. They store a reference (memory address) to an object, not the object itself.

Categories of Reference Types

// 1. Classes
public class Person {
    String name;
    int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 2. Interfaces
public interface Comparable {
    int compareTo(Object o);
}

// 3. Arrays
int[] numbers = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
Person[] people = new Person[10];

// 4. Enums
public enum Color {
    RED, GREEN, BLUE
}

// 5. Annotation types (special)
@Override
public String toString() { ... }

Common Reference Types

// String - immutable sequence of characters
String name = "Amazon";

// Collections
List<Integer> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
Set<String> set = new HashSet<>();

// Wrapper classes (object versions of primitives)
Integer intObj = 42;      // auto-boxed from int
Double doubleObj = 3.14;  // auto-boxed from double
Boolean boolObj = true;   // auto-boxed from boolean

// Date/Time
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.now();

// File I/O
File file = new File("data.txt");
Path path = Paths.get("data.txt");

// Exceptions
Exception e = new Exception("Error");
RuntimeException re = new RuntimeException("Runtime error");

Reference Type Sizes

// Reference sizes are platform-dependent:
// - 32-bit JVM: 4 bytes
// - 64-bit JVM: 8 bytes (or 4 bytes with compressed oops)

// But the OBJECT they reference can be much larger:
public class LargeObject {
    int[] data = new int[1000];  // 4000+ bytes
    String name = "Hello";       // references another object
}

// Reference: 4-8 bytes
// Object in heap: potentially megabytes

Reference vs Primitive

How Primitives and References Differ

// Primitive: stores the VALUE directly
int primitiveVar = 42;
// Memory: [42] (stack)

// Reference: stores an ADDRESS to the object
String refVar = "Hello";
// Memory: [address] → "Hello" (heap)

Assignment Behavior

// Primitive: copies the value
int a = 10;
int b = a;  // b gets a copy of value 10
b = 20;     // a is still 10
System.out.println(a);  // 10
System.out.println(b);  // 20

// Reference: copies the reference (both point to same object)
String s1 = new String("Hello");
String s2 = s1;  // s2 points to same String object
s2 = "World";    // s1 still points to "Hello" (String is immutable)
System.out.println(s1);  // Hello
System.out.println(s2);  // World

// But with mutable objects:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
List<Integer> list2 = list1;  // same list!
list2.add(2);  // affects list1 too
System.out.println(list1);  // [1, 2]
System.out.println(list2);  // [1, 2]

Comparison Behavior

// Primitive: compares values
int a = 42;
int b = 42;
System.out.println(a == b);  // true (same value)

// Reference: compares addresses (unless overridden)
String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2);      // false (different objects)
System.out.println(s1.equals(s2)); // true (same content)

// String pool optimization
String s3 = "Hello";
String s4 = "Hello";
System.out.println(s3 == s4);  // true (same pooled object)

Null References

// Null means no object
String nothing = null;
// nothing points to no object

// NullPointerException
// System.out.println(nothing.length());  // NPE!

// Safe null checking
if (nothing != null) {
    System.out.println(nothing.length());
}

// Null-safe operations
String name = null;
int length = (name != null) ? name.length() : 0;

// Optional (Java 8+)
Optional<String> opt = Optional.ofNullable(name);
int safeLength = opt.map(String::length).orElse(0);

Memory Comparison

PRIMITIVE (int x = 42):
┌─────────────┐
│   Stack     │
│  x: [42]   │
└─────────────┘

REFERENCE (String s = "Hello"):
┌─────────────┐     ┌─────────────┐
│   Stack     │     │    Heap     │
│  s: [addr]──┼────→│ "Hello"     │
└─────────────┘     └─────────────┘

Working with Null

Null in Java

Null represents the absence of an object reference. It's a valid value for any reference type.

Common Null Scenarios

// 1. Uninitialized reference
String name;
// System.out.println(name);  // COMPILE ERROR: might not be initialized

// 2. Explicitly null
String empty = null;

// 3. Method returning null
public String findUser(int id) {
    if (id < 0) return null;  // not found
    return "User" + id;
}

// 4. Collection element
List<String> list = new ArrayList<>();
list.add(null);  // valid!
list.add("Hello");
System.out.println(list.get(0));  // null

NullPointerException Prevention

// BAD: risky null access
public int getLength(String s) {
    return s.length();  // NPE if s is null!
}

// GOOD: null check
public int getLengthSafe(String s) {
    if (s == null) return 0;
    return s.length();
}

// GOOD: Objects utility class
public int getLengthObjects(String s) {
    return Objects.toString(s, "").length();
}

// GOOD: Optional (Java 8+)
public int getLengthOptional(String s) {
    return Optional.ofNullable(s)
                   .map(String::length)
                   .orElse(0);
}

// GOOD: @Nullable annotation (clearer intent)
public int getLengthAnnotated(@Nullable String s) {
    if (s == null) return 0;
    return s.length();
}

Null in Collections

// HashMap allows null keys
Map<String, Integer> map = new HashMap<>();
map.put(null, 0);  // valid!
map.put("key", null);  // valid!

// HashSet allows null elements
Set<String> set = new HashSet<>();
set.add(null);  // valid!

// TreeMap does NOT allow null keys
TreeMap<String, Integer> treeMap = new TreeMap<>();
// treeMap.put(null, 0);  // NullPointerException!

// ArrayList allows null elements
List<String> arrayList = new ArrayList<>();
arrayList.add(null);  // valid!

// Arrays.toString for printing
System.out.println(Arrays.toString(arrayList.toArray()));  // [null]

Null Safety Patterns

// 1. Builder pattern with null checks
public class User {
    private final String name;
    private final int age;
    
    private User(String name, int age) {
        this.name = Objects.requireNonNull(name, "name cannot be null");
        this.age = age;
    }
    
    public static class Builder {
        private String name;
        private int age;
        
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        
        public Builder age(int age) {
            this.age = age;
            return this;
        }
        
        public User build() {
            return new User(name, age);
        }
    }
}

// 2. Null object pattern
public interface Animal {
    void speak();
}

public class Dog implements Animal {
    public void speak() { System.out.println("Woof!"); }
}

public class NullAnimal implements Animal {
    public void speak() { /* do nothing */ }
}

// 3. Defensive copying
public void processList(List<String> input) {
    List<String> copy = new ArrayList<>(input);  // defensive copy
    // work with copy safely
}

Practice Problems

0/2solved
Predict Output: Reference Equality
Reference Equality

What is the output of this code?

Example:

Input: public class Test { public static void main(String[] args) { String s1 = new String("Hello"); String s2 = new String("Hello"); System.out.println(s1 == s2); System.out.println(s1.equals(s2)); } }

Output: false true

new String creates different objects (different references). equals() compares content.

Optimal Solution — O(n) time, O(1) space

Understand reference vs value equality

public class Test {
    public static void main(String[] args) {
        String s1 = new String("Hello");  // new object
        String s2 = new String("Hello");  // another new object
        System.out.println(s1 == s2);      // false (different references)
        System.out.println(s1.equals(s2)); // true (same content)
    }
}

Edge Cases:

  • String pool optimization
  • Integer cache
Predict Output: Reference Sharing
Mutable Objects

What is the output of this code?

Example:

Input: import java.util.*; public class Test { public static void main(String[] args) { List<Integer> a = new ArrayList<>(); a.add(1); List<Integer> b = a; b.add(2); System.out.println(a); System.out.println(b); System.out.println(a == b); } }

Output: [1, 2] [1, 2] true

Both variables point to the same ArrayList object. Changes through one are visible through the other.

Optimal Solution — O(1) time, O(1) space

Understand reference sharing for mutable objects

import java.util.*;
public class Test {
    public static void main(String[] args) {
        List<Integer> a = new ArrayList<>();
        a.add(1);
        List<Integer> b = a;  // b points to same list
        b.add(2);             // affects both a and b
        System.out.println(a);  // [1, 2]
        System.out.println(b);  // [1, 2]
        System.out.println(a == b);  // true (same reference)
    }
}

Edge Cases:

  • Defensive copying
  • Immutable vs mutable

Quiz

1. What does a reference variable store?

Question 1 options

2. What is the default value of a reference variable?

Question 2 options

3. What causes a NullPointerException?

Question 3 options

4. What is the primary purpose of Reference Types?

Question 4 options

Flashcards

Question

What is the difference between == and .equals() for objects?

Answer

== compares memory addresses (references), .equals() compares object content. Always use .equals() for content comparison.

Question

What happens when you assign one reference to another?

Answer

Both references point to the same object. Changes through one are visible through the other. No copy is made.

Question

How do you prevent NullPointerException?

Answer

Use null checks, Optional class, Objects utility methods, or @Nullable annotations. Never call methods on potentially null references.

Question

What is Reference Types?

Answer

Reference Types is a key concept in Java programming.

Question

When to use Reference Types?

Answer

Use Reference Types when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.Reference variables store memory addresses, not objects
  • 2.== compares references, .equals() compares content
  • 3.Assignment of references creates aliases, not copies
  • 4.Null represents absence of an object - handle carefully
  • 5.Use Optional for null-safe code (Java 8+)

Interview Tips

  • Know the difference between == and .equals()
  • Explain pass-by-value for references
  • Discuss null safety strategies
  • Understand String immutability and pooling

Cheat Sheet

Reference Types Cheat Sheet

Reference vs Primitive:

  • Primitive: stores value directly (stack)
  • Reference: stores address to object (heap)

Key Behaviors:

  • Assignment copies reference (not object)
  • == compares addresses, .equals() compares content
  • Null means no object

Null Safety:

  • Check before use: if (ref != null)
  • Use Optional for safe null handling
  • Use Objects.requireNonNull() for validation

Common Reference Types:

  • String (immutable)
  • Collections (ArrayList, HashMap)
  • Wrapper classes (Integer, Double)
  • Custom classes