Back to Keywords: static
Q2Question 2 of 4

What is the difference between static and instance variables?

Key Concepts
  • Static variable: one copy per class, shared by all instances, stored in Metaspace
  • Instance variable: one copy per object, stored on the heap inside each object
  • Static is created when the class loads and destroyed when the class is unloaded
  • Instance is created with new and destroyed when the object is garbage collected
  • Changing a static variable affects all instances – changing an instance variable affects only that object

Definition

A static variable has one copy for the entire class. An instance variable has a separate copy for each object.

The key differences:

  • Static variable: One shared copy stored with the class (Metaspace). Accessed via ClassName.field. Created when the class loads, lives until the class is unloaded.
  • Instance variable: Separate copy for each object, stored on the heap inside the object. Accessed via objectRef.field. Created with new, lives until the object is garbage collected.

The bank's base interest rate (static) is one number on the head-office notice board. Every branch reads the same number. A customer's balance (instance) is printed in each customer's own passbook. Changing the interest rate affects every customer. Changing one customer's balance affects only that customer.

Understanding the Concepts

Side-by-side comparison

The most effective way to understand the difference is to see both types in the same class. A static variable has one copy shared across all instances. An instance variable has a separate copy inside each object.

JAVA
package com.bank.core;

import java.math.BigDecimal;

public class Account {
    private static double baseRate = 4.0;   // one copy for the class
    private BigDecimal balance;              // one copy per object

    public Account(BigDecimal balance) {
        this.balance = balance;
    }

    public static void setBaseRate(double rate) {
        baseRate = rate;  // changes for ALL accounts
    }

    public BigDecimal getInterest() {
        return balance.multiply(BigDecimal.valueOf(baseRate / 100));
    }
}
JAVA
Account a = new Account(new BigDecimal("500000"));
Account b = new Account(new BigDecimal("200000"));

System.out.println(a.getInterest());  // 20000.0 (500000 * 4%)
System.out.println(b.getInterest());  // 8000.0 (200000 * 4%)

Account.setBaseRate(5.0);  // changes for ALL accounts

System.out.println(a.getInterest());  // 25000.0 (500000 * 5%)
System.out.println(b.getInterest());  // 10000.0 (200000 * 5%)

Changing baseRate affects every Account object. Changing balance on one object affects only that object.

Full comparison table

| Property | Static variable | Instance variable |
|---|---|---|
| Copies | One per class | One per object |
| Stored in | Metaspace (class data) | Heap (inside the object) |
| Accessed via | ClassName.field | objectRef.field or this.field |
| Created when | Class is loaded | Object is created (new) |
| Destroyed when | Class is unloaded | Object is garbage collected |
| Default value | Same as instance (0, null, false) | 0, null, false |
| Thread safety | Shared: needs synchronization | Per-object: shared only if the object is shared |
| Use case | Constants, counters, config | Object state (balance, name, ID) |

What about memory layout?

JAVA
Account a = new Account(new BigDecimal("500000"));
Account b = new Account(new BigDecimal("200000"));
DIAGRAM
  Metaspace (class data):
    Account.baseRate = 4.0    ← one shared copy

  Heap:
    Account a:                 Account b:
    ┌────────────────┐         ┌────────────────┐
    │ balance: 500000│         │ balance: 200000│  ← separate copies
    └────────────────┘         └────────────────┘

How is this relevant to banking?

In a banking application, the distinction maps naturally to shared configuration versus per-customer data.

JAVA
package com.bank.core;

import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicInteger;

public class SavingsAccount {
    // Static: shared across all savings accounts
    private static BigDecimal minimumBalance = new BigDecimal("10000.00");
    private static final AtomicInteger accountCount = new AtomicInteger(0);

    // Instance: unique to each account
    private final String accountId;
    private BigDecimal balance;

    public SavingsAccount(String accountId, BigDecimal initialDeposit) {
        this.accountId = accountId;
        this.balance = initialDeposit;
        accountCount.incrementAndGet();  // shared counter, needs thread safety
    }

    public static int getTotalAccounts() {
        return accountCount.get();
    }

    public boolean meetsMinimum() {
        return balance.compareTo(minimumBalance) >= 0;
    }
}

minimumBalance is static because it is the same policy for every savings account. balance is instance because each customer has their own. accountCount is static because it tracks the total across all accounts.

What are common mistakes?

  • Using a static field for per-object state. If you make balance static, all accounts share one balance – deposits by one customer change everyone's balance.
  • Accessing static fields via instance references. account.baseRate compiles but is misleading. Use Account.baseRate so the reader knows it is a class-level field.
  • Not synchronizing mutable statics. Multiple threads incrementing a static int counter cause lost updates. Use AtomicInteger or synchronized.

What should a senior engineer know?

  • Constants should be static final. private static final BigDecimal MINIMUM_BALANCE = new BigDecimal("10000.00"); is the standard pattern.
  • Static fields survive object GC. Even if all Account objects are collected, Account.baseRate persists until the classloader is collected.
  • AtomicInteger for thread-safe static counters. private static final AtomicInteger count = new AtomicInteger(0); is safe without synchronized.
  • Static fields and testing. Global mutable state makes unit tests harder because tests can interfere with each other. Prefer dependency injection over static state for testability.

Visual Overview

DIAGRAM
  static variable (baseRate = 4.0):     instance variable (balance):
    one copy in Metaspace                 one copy per object on heap
    shared by ALL instances               private to each object
    changed via ClassName.field           changed via this.field
    lives until class unloaded            lives until object GC'd

  Account.setBaseRate(5.0) → ALL accounts see 5.0
  a.balance = 500000       → only 'a' has 500000

Key Takeaway

A static variable has one copy shared by all instances, stored with the class in Metaspace. An instance variable has a separate copy per object, stored on the heap inside each object. Changing a static variable affects every instance. Changing an instance variable affects only that object.

In banking, the base interest rate is static because it is one value for the whole bank. A customer's balance is an instance variable because each customer has their own. Constants are static final. Mutable static counters need thread synchronization.

One sentence to remember: static is one copy for the class, instance is one copy per object.

---

Share
Use ← → arrow keys to navigate