Java Reference Types

Reference types in Java store addresses that point to objects in memory. Unlike primitive types, they don't hold the actual data but references to where the data is stored.

Types of Reference Variables

Objects

Instances of classes that contain both data (fields) and behavior (methods).

1// Creating objects 2String name = new String("Java Developer"); 3Date today = new Date(); 4ArrayList<String> list = new ArrayList<>(); 5 6// Object references point to memory locations 7String str1 = "Hello"; 8String str2 = str1; // Both reference the same object

Arrays

Collections of elements of the same type stored in contiguous memory.

1// Array declarations 2int[] numbers = new int[5]; 3String[] names = new String[3]; 4int[][] matrix = new int[3][3]; // 2D array 5 6// Array initialization 7int[] primes = {2, 3, 5, 7, 11}; 8String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri"};

Strings

Special objects for text manipulation with unique memory handling.

1// String literals (stored in string pool) 2String literal1 = "Hello"; 3String literal2 = "Hello"; // Same object as literal1 4 5// String objects (stored in heap) 6String object1 = new String("Hello"); 7String object2 = new String("Hello"); // Different object

Memory Management

Stack vs Heap

Stack Memory

  • Stores primitive variables and reference variables
  • Fast access
  • Limited size
  • Automatically managed (LIFO order)
1public void method() { 2 int x = 10; // Stack 3 String name = "Java"; // Reference on stack, object on heap 4 int[] numbers = new int[5]; // Reference on stack, array on heap 5}

Heap Memory

  • Stores objects and arrays
  • Larger size
  • Slower access than stack
  • Managed by garbage collector
1// These objects are created in heap memory 2Person person = new Person("Alice", 25); 3List<Integer> list = new ArrayList<>(); 4int[] largeArray = new int[1000000];

Object Lifecycle

Creation

1// 1. Declaration (reference on stack) 2Person person; 3 4// 2. Instantiation (object on heap) 5person = new Person("Alice", 25); 6 7// 3. Initialization 8person.setName("Alice Smith");

Usage

1// Accessing object members 2String name = person.getName(); 3person.setAge(26); 4 5// Method calls 6person.birthday(); // Increments age

Garbage Collection

1public void createObjects() { 2 Person person1 = new Person("Alice"); 3 Person person2 = new Person("Bob"); 4 5 person1 = null; // Eligible for garbage collection 6 // person2 still referenced 7} // person2 becomes eligible at method end

Reference Types in Detail

Class Objects

1public class Car { 2 private String model; 3 private int year; 4 5 public Car(String model, int year) { 6 this.model = model; 7 this.year = year; 8 } 9 10 // Methods... 11} 12 13// Usage 14Car myCar = new Car("Toyota", 2023); 15Car yourCar = myCar; // Both reference same object

Interface References

1// Interface reference pointing to implementation 2List<String> list = new ArrayList<>(); 3Map<String, Integer> map = new HashMap<>(); 4Runnable task = new MyTask();

Abstract Class References

1// Abstract class reference pointing to concrete implementation 2Animal dog = new Dog(); 3Animal cat = new Cat();

Special Reference Types

Null Reference

1String text = null; // Reference to nothing 2if (text != null) { 3 System.out.println(text.length()); 4}

This Reference

1public class Person { 2 private String name; 3 4 public void setName(String name) { 5 this.name = name; // 'this' refers to current object 6 } 7}

Super Reference

1public class Student extends Person { 2 public void displayInfo() { 3 super.displayInfo(); // Calls parent method 4 System.out.println("Student info"); 5 } 6}

Pass by Value vs Pass by Reference

Java is always pass-by-value, but with reference types, the value passed is the reference.

1public void modifyString(String str) { 2 str = str + " Modified"; // Creates new string, doesn't change original 3} 4 5public void modifyList(List<String> list) { 6 list.add("New Item"); // Modifies the object referenced 7} 8 9String original = "Hello"; 10modifyString(original); 11System.out.println(original); // Still "Hello" 12 13List<String> items = new ArrayList<>(); 14items.add("Original"); 15modifyList(items); 16System.out.println(items); // Contains "Original" and "New Item"

Comparison of Reference Types

Equality (== vs equals)

1String str1 = new String("Hello"); 2String str2 = new String("Hello"); 3String str3 = str1; 4 5System.out.println(str1 == str2); // false (different objects) 6System.out.println(str1.equals(str2)); // true (same content) 7System.out.println(str1 == str3); // true (same object)

Arrays Comparison

1int[] arr1 = {1, 2, 3}; 2int[] arr2 = {1, 2, 3}; 3int[] arr3 = arr1; 4 5System.out.println(arr1 == arr2); // false (different arrays) 6System.out.println(Arrays.equals(arr1, arr2)); // true (same contents) 7System.out.println(arr1 == arr3); // true (same array)

Memory Optimization

Object Reuse

1// Good: Reuse objects 2StringBuilder builder = new StringBuilder(); 3for (int i = 0; i < 1000; i++) { 4 builder.setLength(0); // Clear and reuse 5 builder.append("Item ").append(i); 6 process(builder.toString()); 7} 8 9// Bad: Creating many objects 10for (int i = 0; i < 1000; i++) { 11 String str = "Item " + i; // Creates new string each iteration 12 process(str); 13}

Weak References

1import java.lang.ref.WeakReference; 2 3WeakReference<Person> weakRef = new WeakReference<>(new Person("Temp")); 4Person person = weakRef.get(); // May return null if garbage collected

Best Practices

  1. Initialize references to avoid NullPointerException
  2. Use null checks before accessing object members
  3. Prefer interfaces for reference types when possible
  4. Be aware of object creation in loops
  5. Use appropriate collection types based on usage patterns
  6. Consider memory usage when creating large objects

Common Pitfalls

  • NullPointerException: Accessing null reference
  • Memory leaks: Holding references to unused objects
  • Unnecessary object creation: In loops or frequent methods
  • String concatenation: Using + in loops creates many objects
  • Array vs ArrayList: Fixed vs dynamic size considerations

Understanding reference types and memory management is crucial for writing efficient and robust Java applications.