Static Keyword
What is Static?
Section titled “What is Static?”- Allocated once during class loading, not per object
- Shared across all instances of the class
- Accessible without creating an object
What Can Be Static
Section titled “What Can Be Static”| Type | Purpose | Example |
|---|---|---|
| Static Variables | Shared state across instances | public static int counter = 0; |
| Static Methods | Utility behavior | public static int add(int a, int b) |
| Static Blocks | Configuration at class load time | static { /* initialization */ } |
| Static Nested Classes | Logically grouped helpers | public static class Builder |
| Static Imports | Import static members | import static java.lang.Math.PI; |
Static vs Instance — Quick Reference
Section titled “Static vs Instance — Quick Reference”| Aspect | static | Instance |
|---|---|---|
| Belongs to | Class | Object |
| Memory | Method Area (Metaspace) | Heap (per object) |
| Access | ClassName.member | object.member |
| Lifetime | Class load → ClassLoader GC | Object creation → Object GC |
this reference | Not available | Available |
| Overriding | No (method hiding only) | Yes (polymorphism) |
| Thread safety | Shared — must synchronize | Per-object — safer by default |
Static Memory Management
Section titled “Static Memory Management”| Concept | Behavior |
|---|---|
| Memory Location | Stored in Method Area (part of Metaspace in modern JVMs) |
| GC Eligibility | Lives as long as the class remains loaded in JVM |
| Risk | Holding large static references may lead to memory leaks |
| Initialization | When class is first loaded by ClassLoader |
| Cleanup | When ClassLoader is garbage collected |
Static Variables
Section titled “Static Variables”Basic Static Variables
Section titled “Basic Static Variables”public class Counter { private static int count = 0; private int instanceId;
public Counter() { instanceId = ++count; }
public static int getTotalCount() { return count; }}
Counter c1 = new Counter(); // count = 1Counter c2 = new Counter(); // count = 2System.out.println(Counter.getTotalCount()); // 2Static Constants (static final)
Section titled “Static Constants (static final)”public class AppConstants { public static final double PI = 3.14159; public static final int MAX_RETRY = 3; public static final String DEFAULT_ENCODING = "UTF-8";
private AppConstants() {} // prevent instantiation}Complex Initialization via Static Block
Section titled “Complex Initialization via Static Block”public class Configuration { private static final Properties config = new Properties();
static { try (InputStream input = Configuration.class .getClassLoader() .getResourceAsStream("config.properties")) { if (input != null) { config.load(input); } } catch (IOException e) { throw new ExceptionInInitializerError(e); } }
public static String getProperty(String key) { return config.getProperty(key); }}Static Methods
Section titled “Static Methods”Utility Methods
Section titled “Utility Methods”public class StringUtils { private StringUtils() {}
public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); }
public static String reverse(String str) { if (str == null) return null; return new StringBuilder(str).reverse().toString(); }}Static Factory Methods
Section titled “Static Factory Methods”Static factory methods offer advantages over constructors: descriptive names, caching, and returning subtypes.
public class Connection { private final String url; private final boolean readOnly;
private Connection(String url, boolean readOnly) { this.url = url; this.readOnly = readOnly; }
public static Connection readWrite(String url) { return new Connection(url, false); }
public static Connection readOnly(String url) { return new Connection(url, true); }}
// Readable, self-documenting APIConnection rw = Connection.readWrite("jdbc:mysql://localhost/db");Connection ro = Connection.readOnly("jdbc:mysql://localhost/db");Static Blocks
Section titled “Static Blocks”Static blocks run once when the class is first loaded, before any constructor or static method call.
public class DatabaseConnection { private static Connection connection;
static { try { Class.forName("com.mysql.cj.jdbc.Driver"); connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/mydb", "user", "pass" ); } catch (Exception e) { throw new ExceptionInInitializerError(e); } }
public static Connection getConnection() { return connection; }}Multiple Static Blocks & Initialization Order
Section titled “Multiple Static Blocks & Initialization Order”Static blocks and static field initializers execute top-to-bottom in source order:
public class InitOrder { static { System.out.println("1. Static block A"); }
private static String field = initField();
static { System.out.println("3. Static block B"); }
private static String initField() { System.out.println("2. Field initializer"); return "value"; }}// Output: 1 → 2 → 3Static Nested Classes
Section titled “Static Nested Classes”| Static Nested Class | Inner Class (non-static) | |
|---|---|---|
| Outer instance needed? | No | Yes (implicit reference) |
| Can access outer instance members? | No — static only | Yes — all members |
| Memory | No hidden reference | Holds reference → can cause leaks |
| Typical use | Builder, helper, Entry | Iterator, event handler |
public class Outer { private static String staticField = "static"; private String instanceField = "instance";
public static class Nested { public void display() { System.out.println(staticField); // OK // System.out.println(instanceField); // compile error } }}
Outer.Nested nested = new Outer.Nested(); // no Outer instance neededBuilder Pattern (Classic Use Case)
Section titled “Builder Pattern (Classic Use Case)”public class Person { private final String name; private final int age; private final String email;
private Person(Builder b) { this.name = b.name; this.age = b.age; this.email = b.email; }
public static class Builder { private String name; private int age; private String email;
public Builder name(String name) { this.name = name; return this; } public Builder age(int age) { this.age = age; return this; } public Builder email(String email) { this.email = email; return this; }
public Person build() { Objects.requireNonNull(name, "name is required"); return new Person(this); } }}
Person p = new Person.Builder().name("John").age(30).email("j@x.com").build();Static and Inheritance
Section titled “Static and Inheritance”class Parent { static void greet() { System.out.println("Parent"); } void instanceGreet() { System.out.println("Parent"); }}
class Child extends Parent { static void greet() { System.out.println("Child"); } // hides @Override void instanceGreet() { System.out.println("Child"); } // overrides}
Parent ref = new Child();ref.greet(); // "Parent" — static, resolved by reference typeref.instanceGreet(); // "Child" — instance, resolved by object type| Static (hiding) | Instance (overriding) | |
|---|---|---|
| Binding | Compile-time (early) | Runtime (late) |
| Resolved by | Reference type | Actual object type |
@Override | Not applicable | Required |
| Polymorphism | No | Yes |
Static Imports
Section titled “Static Imports”Static imports let you use static members without class-qualifying them.
import static java.lang.Math.PI;import static java.lang.Math.sqrt;import static java.util.Collections.unmodifiableList;
double circumference = 2 * PI * radius; // instead of Math.PIdouble hypotenuse = sqrt(a * a + b * b); // instead of Math.sqrt(...)Thread Safety with Static
Section titled “Thread Safety with Static”Static fields are shared across all threads. Without synchronization, concurrent access causes race conditions.
Problem: Unsynchronized Counter
Section titled “Problem: Unsynchronized Counter”public class UnsafeCounter { private static int count = 0;
public static void increment() { count++; // NOT atomic: read-modify-write }}Solutions
Section titled “Solutions”// Option 1: AtomicInteger (preferred for simple counters)private static final AtomicInteger count = new AtomicInteger(0);public static void increment() { count.incrementAndGet(); }
// Option 2: synchronized methodprivate static int count = 0;public static synchronized void increment() { count++; }
// Option 3: volatile (only for flags, not compound actions)private static volatile boolean running = true;| Technique | Use Case | Overhead |
|---|---|---|
AtomicInteger / AtomicReference | Single-variable CAS operations | Low |
synchronized | Multi-step critical sections | Medium |
volatile | Visibility-only (flags, published refs) | Low |
ThreadLocal | Per-thread isolated state | Low |
Class Loading & Initialization Order
Section titled “Class Loading & Initialization Order”When a class hierarchy is loaded, the JVM follows a strict order:
1. Parent static fields & static blocks (top-to-bottom)2. Child static fields & static blocks (top-to-bottom)3. Parent instance fields & instance blocks4. Parent constructor5. Child instance fields & instance blocks6. Child constructorclass Parent { static { System.out.println("1. Parent static block"); } { System.out.println("3. Parent instance block"); } Parent() { System.out.println("4. Parent constructor"); }}
class Child extends Parent { static { System.out.println("2. Child static block"); } { System.out.println("5. Child instance block"); } Child() { System.out.println("6. Child constructor"); }}
new Child();// Output: 1 → 2 → 3 → 4 → 5 → 6