Skip to the content.

Object Creation and Storage (Instantiation)

Memory Allocation: Stack and Heap

In Java, memory allocation for variables occurs in two main areas: the stack and the heap.

Stack Memory

Stack memory is a region of memory that stores temporary variables created by each function (including the main function). It is managed by the execution stack and follows a last-in-first-out (LIFO) order.

  • Stores primitive data types and references to objects.
  • Memory is allocated in a last-in-first-out (LIFO) manner.
  • Each thread has its own stack, ensuring thread safety.

Example:

int number = 100; // Stored in the stack

Stack Variables Tips

College Board often asks questions about stack usage.

  • Since primitives are always on the stack, they point directly to the content. This is best observed in a debugger.
  • A reference type contains an address to the content on the stack.
  • Passing a stack variable to a method creates a copy of the content of that variable.
  • Changes to the content of a primitive type will not return back to the method caller; this is called pass-by-value.
  • Since a reference type contains an address to the heap, the reference is copied when calling a method. This is called pass-by-reference, as data type changes are then performed according to the reference.

Heap Memory

Heap memory is a region of memory used for dynamic memory allocation. It is managed by Java’s memory management system.

  • Used for storing objects and arrays.
  • Shared among all threads, requiring synchronization for thread safety.
  • Managed by thea garbage collector, which reclaims memory when objects are no longer in use.

Example:

// Long form showing new
String message = new String("Hello");

// Short form Java performs new find the scenes
String message = "Hello";

Heap Variables Tips

  • Heap variables stay alive as long as a stack variable points to them.
  • By nature, all reference data types refer to an address on the stack but change content on the heap.
  • Objects created in the heap are globally accessible and can be shared among multiple methods, this creates concurrency issues when programming.
  • The garbage collector automatically reclaims memory from objects that are no longer referenced, helping to prevent memory leaks.

Popcorn Hack: literal vs input

A value that is directly in code is called a literal. Often developers will say this value is hard coded value.

  • Literal: In source code representation of a fixed value, e.g. 17. A hard coded number.
  • String Literal: In sourced code set of letters within quotes, e.g. “blue”, A hard coded string.

Q1: Define some literal data. Q2: Obtain that data from input versus hard coded.

// Hard code literal values

int literalAge = 17;
String literalFavoriteColor = "blue";
// Input your age
Scanner scanObj = new Scanner(System.in);  // Create a Scanner object
System.out.println("Enter age");
int inputAge = scanObj.nextInt();  // Read user input
System.out.println("My Age is: " + inputAge);  // Output user input
Enter age
My Age is: 16

Popcorn Hack: pass-by-value, pass-by-reference

For College Articulation in Data Structures and College Board AP Exam you will need to understand pass-by-value and pass-br-reference.

  • If you pass primitives to a method they WILL NOT change the callers value.
  • If you wrap the primitive in a refrence type, in the example below using a class, then you can change the original.

Q1: Describe approache difference between IntByValue and IntByReference. Q2: Try to make a changeInt method that change would persist after it is called. Be careful, this will require a change in approach.

public class IntByValue {
    
    public static void changeInt(int n) {
        System.out.println("In changeInt method");
        System.out.println("\tBefore n += 10: n = " + n); // prints 5
        n = n += 10;
        System.out.println("\tAfter n += 10: n = " + n); // prints 10
    }

    public static void main(String[] args) {
        int n = 5;
        System.out.println("Main method before changeInt(n): n = " + n); // prints 5
        changeInt(n);
        System.out.println("Main method after changeInt(n): n = " + n); // still prints 5
    }
}
IntByValue.main(null);
Main method before changeInt(n): n = 5
In changeInt method
	Before n += 10: n = 5
	After n += 10: n = 15
Main method after changeInt(n): n = 5
public class IntByReference {
    private int value;

    public IntByReference(Integer value) {
        this.value = value;
    }

    public String toString() {
        return (String.format("%d", this.value));
    }

    public void swapToLowHighOrder(IntByReference i) {
        if (this.value > i.value) {
            int tmp = this.value;
            this.value = i.value;
            i.value = tmp;
        }
    }

    public static void swapper(int n0, int n1) {
        IntByReference a = new IntByReference(n0);
        IntByReference b = new IntByReference(n1);
        System.out.println("Before: " + a + " " + b);
        a.swapToLowHighOrder(b);  // conditionally build swap method to change values of a, b
        System.out.println("After: " + a + " " + b);
        System.out.println();
    }

    public static void main(String[] ags) {
        IntByReference.swapper(21, 16);
        IntByReference.swapper(16, 21);
        IntByReference.swapper(16, -1);
    }

}
IntByReference.main(null);
Before: 21 16
After: 16 21

Before: 16 21
After: 16 21

Before: 16 -1
After: -1 16
public class MemoryDemo {
    public static void main(String[] args) {
        // Stack variables
        int a = 10;
        int b = a;  // Copy of value
        b = 20;     // Changing b doesn't affect a
        
        System.out.println("Primitives (Stack):");
        System.out.println("a = " + a);  // Still 10
        System.out.println("b = " + b);  // Now it's 20
        
        // Heap variables
        int[] array1 = {1, 2, 3};
        int[] array2 = array1;  // Copy of reference (address)
        array2[0] = 99;         // Changing array2 DOES affect array1
        
        System.out.println("\nArrays (Heap):");
        System.out.println("array1[0] = " + array1[0]);  // Now it's 99!
        System.out.println("array2[0] = " + array2[0]);  // Also 99
    }
}
// Because they are both primitive values. When you set one equal to the other, now they are independent copies of the same value. Changing one does not affect the other.
// But the arrays are refrence types. When you set one equal to the other, now they both point to the same array in memory. Changing one affects the other because they are both references to the same underlying data.

// The values are in the stack, while the arrays are in stack + heap
public class PersonDemo {
    static class Person {
        String name;
        int age;
        
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    
    public static void haveBirthday(Person p) {
        p.age = p.age + 1;  // Modifying object content
        System.out.println("Inside method: " + p.name + " is now " + p.age);
    }
    
    public static void reassignPerson(Person p) {
        p = new Person("New Person", 99);  // Reassigning reference
        System.out.println("Inside reassign: " + p.name + " is " + p.age);
    }
    
    public static void main(String[] args) {
        Person john = new Person("John", 20);
        
        System.out.println("Before birthday: " + john.name + " is " + john.age);
        haveBirthday(john);
        System.out.println("After birthday: " + john.name + " is " + john.age);
        
        System.out.println("\nBefore reassign: " + john.name + " is " + john.age);
        reassignPerson(john);
        System.out.println("After reassign: " + john.name + " is " + john.age);
    }
}
//21
//John
//Modifying means changing the parameters of that obbject, while reassigning means relocalizing the parameter to a new object.
// Homework Hack #1: Object Creation Practice

public class ObjectCreation {
    public static void main(String[] args) {
        // 1. Create two Car objects using 'new'
        Car car1 = new Car("Tesla", 2024);
        Car car2 = new Car("Ford", 2020);

        // 2. Print each car's info
        System.out.println(car1);
        System.out.println(car2);
    }
}

class Car {
    // 1. Declare variables: brand, year
    private String brand;
    private int year;


    // 2. Create a constructor to set those variables
    public Car(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }

    // 3. Add a method or toString() to display car info
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", year=" + year +
                '}';
    }
}
// Homework Hack #2: Heap vs Stack Storage Demo

public class HeapVsStack {
    public static void main(String[] args) {
        // 1. Create a primitive variable (int pages)
        int pages = 100;
        // 2. Create another primitive variable that copies it
        int copiedPages = pages;
        
        // 3. Create a Book object (Book b1 = new Book("Java Basics");)
        Book b1 = new Book("Java Basics");

        // 4. Create another Book reference (Book b2 = b1;)
        Book b2 = b1;

        // 5. Change the original primitive and the Book title
        pages = 200;
        b2.setTitle("Advanced Java");

        // 6. Print both sets of values to compare behavior
        System.out.println("Original Pages: " + pages);
        System.out.println("Copied Pages: " + copiedPages);
        System.out.println("Book 1 Title: " + b1);
        System.out.println("Book 2 Title: " + b2);
    }
}

class Book {
    // 1. Declare variable: String title
    private String title;
    public Book(String title) {
        this.title = title;
    }
    
    // 2. Create a constructor to set the title
    
    // 3. Create a toString() to show the title
    public String toString() {
        return title;
    }
}