Java 'static' Keyword - Complete Guide

Overview
The static
keyword in Java belongs to the class rather than to any specific instance of the class. Static members (variables, methods, blocks, and nested classes) are shared among all instances of the class and can be accessed without creating an object of the class. The static
keyword is fundamental for creating class-level functionality, utility methods, constants, and memory-efficient programming.
Uses of 'static' Keyword
1. Static Variables (Class Variables)
Static variables are shared among all instances of a class. There's only one copy of a static variable, regardless of how many objects are created.
Example:
public class Student {
private String name;
private int rollNumber;
private static int totalStudents = 0; // Static variable
private static String schoolName = "ABC School"; // Static variable
public Student(String name, int rollNumber) {
this.name = name;
this.rollNumber = rollNumber;
totalStudents++; // Increment for each new student
}
public static int getTotalStudents() {
return totalStudents;
}
public static String getSchoolName() {
return schoolName;
}
public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Roll: " + rollNumber);
System.out.println("School: " + schoolName); // Accessing static variable
System.out.println("Total Students: " + totalStudents);
}
}
// Usage
Student s1 = new Student("John", 101);
Student s2 = new Student("Jane", 102);
Student s3 = new Student("Bob", 103);
System.out.println("Total Students: " + Student.getTotalStudents()); // 3
System.out.println("School: " + Student.getSchoolName()); // ABC School
// All students share the same static variables
s1.displayInfo(); // Shows total as 3
s2.displayInfo(); // Shows total as 3
s3.displayInfo(); // Shows total as 3
2. Static Methods
Static methods belong to the class and can be called without creating an instance. They can only directly access static variables and other static methods.
Example:
public class MathUtils {
// Static constants
public static final double PI = 3.14159;
public static final double E = 2.71828;
// Static methods
public static int add(int a, int b) {
return a + b;
}
public static double calculateCircleArea(double radius) {
return PI * radius * radius; // Accessing static variable
}
public static double power(double base, int exponent) {
double result = 1;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
public static boolean isPrime(int number) {
if (number <= 1) return false;
if (number <= 3) return true;
if (number % 2 == 0 || number % 3 == 0) return false;
for (int i = 5; i * i <= number; i += 6) {
if (number % i == 0 || number % (i + 2) == 0) {
return false;
}
}
return true;
}
// Static method calling another static method
public static void printMathInfo() {
System.out.println("PI value: " + PI);
System.out.println("E value: " + E);
System.out.println("5 + 3 = " + add(5, 3)); // Calling static method
}
}
// Usage - No object creation needed
int sum = MathUtils.add(10, 20);
double area = MathUtils.calculateCircleArea(5.0);
boolean prime = MathUtils.isPrime(17);
MathUtils.printMathInfo();
3. Static Blocks (Static Initializers)
Static blocks are executed when the class is first loaded, before any object is created or any static method is called. They're used for static variable initialization.
Example:
public class DatabaseConfig {
private static String databaseUrl;
private static String username;
private static String password;
private static boolean isConfigured = false;
// Static block - executes when class is loaded
static {
System.out.println("Loading database configuration...");
databaseUrl = "jdbc:mysql://localhost:3306/mydb";
username = "admin";
password = "secret123";
isConfigured = true;
System.out.println("Database configuration loaded successfully!");
}
// Multiple static blocks are allowed and execute in order
static {
System.out.println("Performing additional configuration...");
// Additional initialization logic
System.out.println("Additional configuration completed!");
}
public static void displayConfig() {
System.out.println("Database URL: " + databaseUrl);
System.out.println("Username: " + username);
System.out.println("Configured: " + isConfigured);
}
public static boolean isReady() {
return isConfigured;
}
}
// Usage
DatabaseConfig.displayConfig(); // Static blocks execute before this call
// Output:
// Loading database configuration...
// Database configuration loaded successfully!
// Performing additional configuration...
// Additional configuration completed!
// Database URL: jdbc:mysql://localhost:3306/mydb
// Username: admin
// Configured: true
4. Static Nested Classes
Static nested classes don't need a reference to the outer class instance and can be instantiated without creating an instance of the outer class.
Example:
public class OuterClass {
private static String outerStaticField = "Outer Static Field";
private String outerInstanceField = "Outer Instance Field";
// Static nested class
public static class StaticNestedClass {
private String nestedField = "Nested Field";
public void display() {
System.out.println("Nested field: " + nestedField);
System.out.println("Outer static field: " + outerStaticField); // Can access
// System.out.println(outerInstanceField); // Cannot access - compilation error
}
public static void staticMethod() {
System.out.println("Static method in nested class");
System.out.println("Outer static field: " + outerStaticField);
}
}
// Non-static nested class for comparison
public class InnerClass {
public void display() {
System.out.println("Can access: " + outerStaticField); // Can access
System.out.println("Can access: " + outerInstanceField); // Can access
}
}
public void createInstances() {
// Creating static nested class instance
StaticNestedClass staticNested = new StaticNestedClass();
staticNested.display();
// Creating inner class instance
InnerClass inner = new InnerClass();
inner.display();
}
}
// Usage
// Static nested class can be instantiated without outer class instance
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
nested.display();
// Static method in nested class
OuterClass.StaticNestedClass.staticMethod();
// Inner class requires outer class instance
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
5. Static Import
Static import allows you to access static members of a class without qualifying them with the class name.
Example:
// Without static import
import java.lang.Math;
public class WithoutStaticImport {
public void calculate() {
double result1 = Math.pow(2, 3);
double result2 = Math.sqrt(16);
double result3 = Math.PI * 5;
System.out.println("Results: " + result1 + ", " + result2 + ", " + result3);
}
}
// With static import
import static java.lang.Math.*;
import static java.lang.System.out;
public class WithStaticImport {
public void calculate() {
double result1 = pow(2, 3); // No need to write Math.pow
double result2 = sqrt(16); // No need to write Math.sqrt
double result3 = PI * 5; // No need to write Math.PI
out.println("Results: " + result1 + ", " + result2 + ", " + result3);
}
}
// Custom class for static import
public class Constants {
public static final String APP_NAME = "MyApplication";
public static final String VERSION = "1.0.0";
public static final int MAX_USERS = 1000;
public static void printInfo() {
System.out.println(APP_NAME + " version " + VERSION);
}
}
// Using static import with custom class
import static Constants.*;
public class Application {
public void start() {
System.out.println("Starting " + APP_NAME); // No need for Constants.APP_NAME
System.out.println("Version: " + VERSION);
System.out.println("Max users: " + MAX_USERS);
printInfo(); // No need for Constants.printInfo()
}
}
6. Singleton Pattern Implementation
Static keyword is crucial for implementing the Singleton design pattern.
Example:
public class DatabaseConnection {
// Static variable to hold the single instance
private static DatabaseConnection instance;
private static final Object lock = new Object();
// Private constructor prevents instantiation from outside
private DatabaseConnection() {
System.out.println("Database connection established");
// Initialize database connection
}
// Static method to get the singleton instance (Thread-safe)
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public void executeQuery(String query) {
System.out.println("Executing query: " + query);
}
// Static method for connection info
public static void printConnectionInfo() {
System.out.println("Database Connection Utility");
System.out.println("Version: 1.0");
}
}
// Alternative - Enum-based Singleton (Thread-safe by default)
public enum DatabaseConnectionEnum {
INSTANCE;
private DatabaseConnectionEnum() {
System.out.println("Enum-based database connection established");
}
public void executeQuery(String query) {
System.out.println("Executing query via enum: " + query);
}
}
// Usage
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true - same instance
DatabaseConnection.printConnectionInfo(); // Static method call
// Enum singleton usage
DatabaseConnectionEnum.INSTANCE.executeQuery("SELECT * FROM users");
7. Factory Pattern with Static Methods
Static methods are commonly used in factory patterns to create objects.
Example:
abstract class Animal {
protected String name;
protected String species;
public Animal(String name, String species) {
this.name = name;
this.species = species;
}
public abstract void makeSound();
public void displayInfo() {
System.out.println("Name: " + name + ", Species: " + species);
}
}
class Dog extends Animal {
public Dog(String name) {
super(name, "Canine");
}
@Override
public void makeSound() {
System.out.println(name + " barks: Woof!");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name, "Feline");
}
@Override
public void makeSound() {
System.out.println(name + " meows: Meow!");
}
}
class Bird extends Animal {
public Bird(String name) {
super(name, "Avian");
}
@Override
public void makeSound() {
System.out.println(name + " chirps: Tweet!");
}
}
// Factory class with static methods
public class AnimalFactory {
// Static method to create animals
public static Animal createAnimal(String type, String name) {
switch (type.toLowerCase()) {
case "dog":
return new Dog(name);
case "cat":
return new Cat(name);
case "bird":
return new Bird(name);
default:
throw new IllegalArgumentException("Unknown animal type: " + type);
}
}
// Static method with multiple animals
public static Animal[] createMultipleAnimals(String type, String... names) {
Animal[] animals = new Animal[names.length];
for (int i = 0; i < names.length; i++) {
animals[i] = createAnimal(type, names[i]);
}
return animals;
}
// Static utility method
public static void displayAllAnimals(Animal[] animals) {
System.out.println("=== Animal Information ===");
for (Animal animal : animals) {
animal.displayInfo();
animal.makeSound();
System.out.println();
}
}
}
// Usage
Animal dog = AnimalFactory.createAnimal("dog", "Buddy");
Animal cat = AnimalFactory.createAnimal("cat", "Whiskers");
Animal bird = AnimalFactory.createAnimal("bird", "Tweety");
Animal[] dogs = AnimalFactory.createMultipleAnimals("dog", "Max", "Rex", "Bella");
AnimalFactory.displayAllAnimals(dogs);
Important Rules and Restrictions
1. Static Context Restrictions
Static methods and blocks cannot access non-static (instance) variables and methods directly.
public class StaticRestrictions {
private int instanceVariable = 10;
private static int staticVariable = 20;
public void instanceMethod() {
System.out.println("Instance method");
}
public static void staticMethod() {
System.out.println("Static method");
}
public static void demonstrateRestrictions() {
// Valid - accessing static members
System.out.println(staticVariable);
staticMethod();
// Invalid - cannot access instance members directly
// System.out.println(instanceVariable); // Compilation error
// instanceMethod(); // Compilation error
// Valid - accessing instance members through object reference
StaticRestrictions obj = new StaticRestrictions();
System.out.println(obj.instanceVariable); // Valid
obj.instanceMethod(); // Valid
}
public void instanceMethodAccess() {
// Instance methods can access both static and instance members
System.out.println(instanceVariable); // Valid
System.out.println(staticVariable); // Valid
instanceMethod(); // Valid
staticMethod(); // Valid
}
}
2. Static Variable Initialization
Static variables are initialized when the class is first loaded, and they're initialized only once.
public class StaticInitialization {
private static int counter = 0;
private static String message;
// Static block for complex initialization
static {
System.out.println("Static block executing...");
message = "Initialized in static block";
counter = 100;
System.out.println("Static initialization completed");
}
public StaticInitialization() {
counter++;
System.out.println("Constructor called, counter: " + counter);
}
public static void displayCounter() {
System.out.println("Counter: " + counter);
System.out.println("Message: " + message);
}
}
// Usage demonstrates initialization order
StaticInitialization.displayCounter(); // Static block runs first
StaticInitialization obj1 = new StaticInitialization(); // Constructor increments counter
StaticInitialization obj2 = new StaticInitialization(); // Constructor increments counter again
StaticInitialization.displayCounter(); // Shows updated counter
3. Static Method Overriding
Static methods cannot be overridden, but they can be hidden (method hiding).
class Parent {
public static void staticMethod() {
System.out.println("Parent static method");
}
public void instanceMethod() {
System.out.println("Parent instance method");
}
}
class Child extends Parent {
// This is method hiding, not overriding
public static void staticMethod() {
System.out.println("Child static method");
}
// This is method overriding
@Override
public void instanceMethod() {
System.out.println("Child instance method");
}
}
// Demonstration
Parent parentRef = new Child();
parentRef.staticMethod(); // Calls Parent's static method (method hiding)
parentRef.instanceMethod(); // Calls Child's instance method (method overriding)
Child childRef = new Child();
childRef.staticMethod(); // Calls Child's static method
childRef.instanceMethod(); // Calls Child's instance method
4. Static and Inheritance
Static members are inherited by subclasses but belong to the class where they're defined.
class Vehicle {
protected static int totalVehicles = 0;
protected static String manufacturer = "Generic Motors";
public static void displayTotalVehicles() {
System.out.println("Total vehicles: " + totalVehicles);
}
public Vehicle() {
totalVehicles++;
}
}
class Car extends Vehicle {
private static int totalCars = 0;
public static void displayCarInfo() {
System.out.println("Total cars: " + totalCars);
System.out.println("Total vehicles: " + totalVehicles); // Inherited
System.out.println("Manufacturer: " + manufacturer); // Inherited
}
public Car() {
super(); // Increments totalVehicles
totalCars++;
}
}
class Truck extends Vehicle {
private static int totalTrucks = 0;
public Truck() {
super(); // Increments totalVehicles
totalTrucks++;
}
public static void displayTruckInfo() {
System.out.println("Total trucks: " + totalTrucks);
System.out.println("Total vehicles: " + totalVehicles); // Inherited
}
}
// Usage
Car car1 = new Car();
Car car2 = new Car();
Truck truck1 = new Truck();
Vehicle.displayTotalVehicles(); // 3 (2 cars + 1 truck)
Car.displayCarInfo(); // Cars: 2, Vehicles: 3
Truck.displayTruckInfo(); // Trucks: 1, Vehicles: 3
Memory Management and Performance
Static Memory Allocation
public class MemoryDemo {
private static String staticString = "I'm in Method Area";
private String instanceString = "I'm in Heap";
private static final int STATIC_CONSTANT = 100;
static {
System.out.println("Static block: Class loaded into Method Area");
}
public MemoryDemo() {
System.out.println("Constructor: Object created in Heap");
}
public static void explainMemory() {
System.out.println("Static members are stored in Method Area (Metaspace in Java 8+)");
System.out.println("They exist once per class, not per instance");
System.out.println("Accessible without object creation");
}
public void explainInstanceMemory() {
System.out.println("Instance members are stored in Heap memory");
System.out.println("Each object has its own copy");
System.out.println("Requires object creation to access");
}
}
Common Design Patterns Using Static
1. Utility Class Pattern
public final class StringUtils {
// Private constructor prevents instantiation
private StringUtils() {
throw new AssertionError("Utility class cannot be instantiated");
}
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
public static String capitalize(String str) {
if (isEmpty(str)) return str;
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
public static String reverse(String str) {
if (isEmpty(str)) return str;
return new StringBuilder(str).reverse().toString();
}
public static int countWords(String str) {
if (isEmpty(str)) return 0;
return str.trim().split("\\s+").length;
}
}
2. Constants Class Pattern
public final class AppConstants {
private AppConstants() {} // Prevent instantiation
// Database constants
public static final String DB_URL = "jdbc:mysql://localhost:3306/myapp";
public static final String DB_USERNAME = "admin";
public static final int DB_CONNECTION_TIMEOUT = 30;
// Application constants
public static final String APP_NAME = "MyApplication";
public static final String VERSION = "2.1.0";
public static final int MAX_RETRY_ATTEMPTS = 3;
// File paths
public static final String CONFIG_FILE_PATH = "/config/app.properties";
public static final String LOG_FILE_PATH = "/logs/application.log";
// Business logic constants
public static final double TAX_RATE = 0.18;
public static final int MAX_LOGIN_ATTEMPTS = 5;
public static final long SESSION_TIMEOUT_MINUTES = 30;
}
3. Logger Pattern
public class Logger {
private static Logger instance;
private static final Object lock = new Object();
private boolean debugMode = false;
private Logger() {}
public static Logger getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new Logger();
}
}
}
return instance;
}
public static void info(String message) {
getInstance().log("INFO", message);
}
public static void error(String message) {
getInstance().log("ERROR", message);
}
public static void debug(String message) {
if (getInstance().debugMode) {
getInstance().log("DEBUG", message);
}
}
public static void setDebugMode(boolean debug) {
getInstance().debugMode = debug;
}
private void log(String level, String message) {
System.out.println("[" + level + "] " + java.time.LocalDateTime.now() + " - " + message);
}
}
Interview Questions and Answers
Basic Level Questions
Q1: What is the 'static' keyword in Java?
Answer: The static
keyword in Java belongs to the class rather than to any specific instance of the class. Static members (variables, methods, blocks, and nested classes) are shared among all instances of the class and can be accessed without creating an object of the class. Static members are loaded into memory when the class is first loaded and exist throughout the program's lifetime.
Q2: What are the different uses of the 'static' keyword?
Answer: The static
keyword can be used with:
Variables - Class-level variables shared by all instances
Methods - Methods that belong to the class, not instances
Blocks - Code blocks that execute when the class is loaded
Nested Classes - Inner classes that don't need outer class instance
Imports - To import static members for direct access
Q3: Can you access non-static variables from a static method? Why or why not?
Answer: No, you cannot directly access non-static (instance) variables from a static method. This is because static methods belong to the class and can be called without creating any object, while non-static variables belong to specific instances. Since there might be no instance when a static method is called, accessing instance variables directly would be meaningless.
public class Example {
private int instanceVar = 10;
private static int staticVar = 20;
public static void staticMethod() {
System.out.println(staticVar); // Valid
// System.out.println(instanceVar); // Compilation error
// Valid way to access instance variable
Example obj = new Example();
System.out.println(obj.instanceVar); // Valid
}
}
Q4: What is the difference between static and non-static methods?
Answer:
Static Methods: Belong to the class, can be called without object creation, can only directly access static members, loaded when class is loaded
Non-static Methods: Belong to instances, require object creation to call, can access both static and non-static members, loaded when object is created
public class Comparison {
private int instanceVar = 10;
private static int staticVar = 20;
// Static method
public static void staticMethod() {
System.out.println("Static method");
System.out.println(staticVar); // Can access static variables
}
// Non-static method
public void instanceMethod() {
System.out.println("Instance method");
System.out.println(instanceVar); // Can access instance variables
System.out.println(staticVar); // Can also access static variables
}
}
Intermediate Level Questions
Q5: Explain static blocks and their execution order.
Answer: Static blocks are code blocks marked with the static
keyword that execute when the class is first loaded into memory. They execute before any object is created or any static method is called.
Execution Order:
Static variables are initialized in the order they appear
Static blocks execute in the order they appear
This happens only once when the class is first loaded
public class StaticBlockDemo {
private static int var1 = initVar1();
static {
System.out.println("First static block");
var2 = 20;
}
private static int var2;
static {
System.out.println("Second static block");
var3 = var1 + var2;
}
private static int var3;
private static int initVar1() {
System.out.println("Initializing var1");
return 10;
}
public static void display() {
System.out.println("var1: " + var1 + ", var2: " + var2 + ", var3: " + var3);
}
}
// Output when StaticBlockDemo.display() is called:
// Initializing var1
// First static block
// Second static block
// var1: 10, var2: 20, var3: 30
Q6: Can static methods be overridden? Explain the concept of method hiding.
Answer: Static methods cannot be overridden, but they can be hidden. This is called method hiding, not method overriding.
Key Differences:
Method Overriding: Runtime polymorphism, depends on object type
Method Hiding: Compile-time resolution, depends on reference type
class Parent {
public static void staticMethod() {
System.out.println("Parent static method");
}
public void instanceMethod() {
System.out.println("Parent instance method");
}
}
class Child extends Parent {
// Method hiding (not overriding)
public static void staticMethod() {
System.out.println("Child static method");
}
// Method overriding
@Override
public void instanceMethod() {
System.out.println("Child instance method");
}
}
// Demonstration
Parent ref = new Child();
ref.staticMethod(); // "Parent static method" - method hiding
ref.instanceMethod(); // "Child instance method" - method overriding
Child.staticMethod(); // "Child static method"
Parent.staticMethod(); // "Parent static method"
Q7: How does static variable initialization work in inheritance?
Answer: Static variables are inherited by subclasses, but they belong to the class where they're defined. All classes in the inheritance hierarchy share the same static variable.
class Vehicle {
protected static int count = 0;
public Vehicle() {
count++;
}
public static int getCount() {
return count;
}
}
class Car extends Vehicle {
public Car() {
super(); // Increments the same 'count' variable
}
}
class Bike extends Vehicle {
public Bike() {
super(); // Increments the same 'count' variable
}
}
// Usage
Car car1 = new Car(); // count = 1
Bike bike1 = new Bike(); // count = 2
Car car2 = new Car(); // count = 3
System.out.println(Vehicle.getCount()); // 3
System.out.println(Car.getCount()); // 3 (same variable)
System.out.println(Bike.getCount()); // 3 (same variable)
Q8: What is static import and when should you use it?
Answer: Static import allows you to access static members of a class without qualifying them with the class name. It's useful for frequently used utility methods and constants.
Benefits:
Cleaner, more readable code
Less typing for frequently used static members
Drawbacks:
Can reduce code clarity
Potential naming conflicts
Overuse can make code harder to understand
// Without static import
import java.lang.Math;
public class Calculator {
public double calculate(double x, double y) {
return Math.pow(Math.sin(x), 2) + Math.pow(Math.cos(y), 2);
}
}
// With static import
import static java.lang.Math.*;
public class Calculator {
public double calculate(double x, double y) {
return pow(sin(x), 2) + pow(cos(y), 2); // Much cleaner
}
}
// Best practice: Use for frequently used methods/constants
import static java.lang.System.out;
import static java.util.Collections.*;
public class Example {
public void demo() {
out.println("Using static import for System.out"); // Clean
List<String> list = Arrays.asList("a", "b", "c");
reverse(list); // Using Collections.reverse()
}
}
Advanced Level Questions
Q9: Explain the memory allocation of static variables and methods.
Answer: Static members are stored in the Method Area (called Metaspace in Java 8+), not in the heap memory where objects are stored.
Memory Allocation Details:
Static variables: Stored in Method Area, allocated when class is loaded
Static methods: Code stored in Method Area, accessible without instance
Instance variables: Stored in Heap memory with each object
Instance methods: Code in Method Area, but require object context
public class MemoryAllocation {
private static int staticCounter = 0; // Method Area
private int instanceCounter = 0; // Heap (with each object)
private static final String CONSTANT = "Stored in Method Area";
public static void staticMethod() {
// Method code in Method Area
// Can only access static members directly
staticCounter++;
}
public void instanceMethod() {
// Method code in Method Area, but needs object context
instanceCounter++; // Access instance variable
staticCounter++; // Can also access static variable
}
}
// Memory layout visualization:
// Method Area (Metaspace):
// - Class metadata
// - Static variables: staticCounter, CONSTANT
// - Method code: staticMethod(), instanceMethod()
//
// Heap Memory:
// - Object instances with instanceCounter for each object
Q10: What happens if you try to access 'this' or 'super' in a static context?
Answer: You cannot use this
or super
in static context because they refer to instance-specific references, while static context has no associated instance.
class Parent {
protected static String staticVar = "Parent Static";
protected String instanceVar = "Parent Instance";
}
class Child extends Parent {
private static String childStaticVar = "Child Static";
public static void staticMethod() {
System.out.println(staticVar); // Valid - accessing static member
System.out.println(Parent.staticVar); // Valid - explicit class reference
// System.out.println(this.staticVar); // Compilation error
// System.out.println(super.staticVar); // Compilation error
// System.out.println(super.instanceVar); // Compilation error
}
public void instanceMethod() {
System.out.println(this.childStaticVar); // Valid but not recommended
System.out.println(super.staticVar); // Valid but refers to static member
System.out.println(super.instanceVar); // Valid - accessing instance member
}
}
Q11: How do static nested classes differ from inner classes?
Answer: Static nested classes and inner classes have different access privileges and instantiation requirements.
public class OuterClass {
private static String outerStaticField = "Outer Static";
private String outerInstanceField = "Outer Instance";
private static int outerStaticMethod() { return 42; }
private int outerInstanceMethod() { return 24; }
// Static nested class
public static class StaticNestedClass {
public void accessMembers() {
System.out.println(outerStaticField); // Can access
System.out.println(outerStaticMethod()); // Can access
// System.out.println(outerInstanceField); // Cannot access
// System.out.println(outerInstanceMethod()); // Cannot access
// To access instance members, need outer class instance
OuterClass outer = new OuterClass();
System.out.println(outer.outerInstanceField); // Now can access
System.out.println(outer.outerInstanceMethod()); // Now can access
}
// Can have static members
public static void staticNestedMethod() {
System.out.println("Static method in nested class");
}
}
// Non-static inner class
public class InnerClass {
public void accessMembers() {
System.out.println(outerStaticField); // Can access
System.out.println(outerInstanceField); // Can access
System.out.println(outerStaticMethod()); // Can access
System.out.println(outerInstanceMethod()); // Can access
}
// Cannot have static members (except final static)
// public static void staticMethod() { } // Compilation error
public static final int CONSTANT = 10; // This is allowed
}
}
// Usage differences:
// Static nested class - no outer instance needed
OuterClass.StaticNestedClass staticNested = new OuterClass.StaticNestedClass();
OuterClass.StaticNestedClass.staticNestedMethod(); // Can call static methods
// Inner class - requires outer instance
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
Q12: Explain the class loading process and when static members are initialized.
Answer: Class loading and static initialization follow a specific sequence:
Class Loading Process:
Loading: Class bytecode is loaded into Method Area
Linking: Verification, preparation, and resolution
Initialization: Static variables and blocks are executed
public class ClassLoadingDemo {
// Step 1: Static variable declaration and initialization
private static String step1 = initStep("Step 1: Static variable initialization");
// Step 2: First static block
static {
System.out.println("Step 2: First static block");
step2 = "Initialized in first static block";
}
// Step 3: Another static variable
private static String step2;
private static String step3 = initStep("Step 3: Another static variable");
// Step 4: Second static block
static {
System.out.println("Step 4: Second static block");
System.out.println("All static members: " + step1 + ", " + step2 + ", " + step3);
}
// Helper method for initialization
private static String initStep(String message) {
System.out.println(message);
return message;
}
// Constructor - called after static initialization
public ClassLoadingDemo() {
System.out.println("Constructor called - static initialization already complete");
}
public static void triggerClassLoading() {
System.out.println("Static method called - class loading triggered");
}
}
// When ClassLoadingDemo.triggerClassLoading() is first called:
// Step 1: Static variable initialization
// Step 2: First static block
// Step 3: Another static variable
// Step 4: Second static block
// All static members: Step 1: Static variable initialization, Initialized in first static block, Step 3: Another static variable
// Static method called - class loading triggered
Tricky Interview Questions
Q13: What will be the output of this code?
class Parent {
static {
System.out.println("Parent static block");
}
public Parent() {
System.out.println("Parent constructor");
}
public static void staticMethod() {
System.out.println("Parent static method");
}
}
class Child extends Parent {
static {
System.out.println("Child static block");
}
public Child() {
System.out.println("Child constructor");
}
public static void staticMethod() {
System.out.println("Child static method");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("=== Creating Child object ===");
Child child = new Child();
System.out.println("=== Calling static methods ===");
Parent.staticMethod();
Child.staticMethod();
System.out.println("=== Method hiding demonstration ===");
Parent ref = new Child();
ref.staticMethod();
}
}
Answer: The output will be:
Parent static block
Child static block
=== Creating Child object ===
Parent constructor
Child constructor
=== Calling static methods ===
Parent static method
Child static method
=== Method hiding demonstration ===
Parent static method
Explanation:
When
Child
class is first referenced, bothParent
andChild
static blocks executeObject creation calls constructors in inheritance order: Parent → Child
Static method calls are resolved based on the class name used
Method hiding:
ref.staticMethod()
callsParent
's method becauseref
is of typeParent
Q14: What's wrong with this singleton implementation?
public class BrokenSingleton {
private static BrokenSingleton instance = new BrokenSingleton();
private static boolean initialized = false;
private BrokenSingleton() {
if (!initialized) {
System.out.println("Singleton instance created");
initialized = true;
} else {
throw new RuntimeException("Singleton already initialized");
}
}
public static BrokenSingleton getInstance() {
return instance;
}
}
Answer: The problem is with the initialization order. The static variable instance
is initialized before initialized
, so when the constructor runs, initialized
is still false
, but then it's immediately set to true
by the static variable initialization. This creates a race condition.
Corrected version:
public class CorrectSingleton {
// Correct order: initialized flag first
private static boolean initialized = false;
private static CorrectSingleton instance = new CorrectSingleton();
private CorrectSingleton() {
if (!initialized) {
System.out.println("Singleton instance created");
initialized = true;
} else {
throw new RuntimeException("Singleton already initialized");
}
}
public static CorrectSingleton getInstance() {
return instance;
}
}
// Better approach - lazy initialization
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
System.out.println("Lazy singleton instance created");
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
Q15: What happens in this static initialization scenario?
public class TrickyStatic {
private static TrickyStatic instance = new TrickyStatic();
private static int counter1;
private static int counter2 = 0;
public TrickyStatic() {
counter1++;
counter2++;
System.out.println("Constructor: counter1=" + counter1 + ", counter2=" + counter2);
}
public static void main(String[] args) {
System.out.println("Main: counter1=" + TrickyStatic.counter1 + ", counter2=" + TrickyStatic.counter2);
TrickyStatic another = new TrickyStatic();
System.out.println("After second object: counter1=" + TrickyStatic.counter1 + ", counter2=" + TrickyStatic.counter2);
}
}
Answer: The output will be:
Constructor: counter1=1, counter2=1
Main: counter1=1, counter2=0
Constructor: counter1=2, counter2=1
After second object: counter1=2, counter2=1
Explanation:
During class loading,
instance
is created first, calling constructorConstructor increments both counters:
counter1=1, counter2=1
Then
counter2
is explicitly initialized to0
, overwriting the constructor's incrementIn main,
counter1=1
(from constructor),counter2=0
(reset by initialization)Creating another object increments both again:
counter1=2, counter2=1
Q16: How does static work with generics?
Answer: Static members cannot use the generic type parameters of their enclosing class because static members belong to the class, not to any specific generic instantiation.
public class GenericClass<T> {
private T instanceField; // Valid - instance member can use T
// private static T staticField; // Compilation error - static cannot use T
private static String staticField; // Valid - concrete type
public void instanceMethod(T param) {
// Valid - instance method can use T
this.instanceField = param;
}
// public static void staticMethod(T param) { } // Compilation error
public static void staticMethod(String param) {
// Valid - static method with concrete type
staticField = param;
}
// Static generic method is allowed
public static <U> void staticGenericMethod(U param) {
System.out.println("Static generic method: " + param);
}
// Static nested class can have its own generics
public static class StaticNestedClass<U> {
private U nestedField;
public void setField(U value) {
this.nestedField = value;
}
}
}
// Usage
GenericClass<String> stringVersion = new GenericClass<>();
GenericClass<Integer> intVersion = new GenericClass<>();
// Both share the same static members
GenericClass.staticMethod("Shared static");
GenericClass.staticGenericMethod("Generic static method");
// Static nested class with its own generics
GenericClass.StaticNestedClass<Double> nested = new GenericClass.StaticNestedClass<>();
nested.setField(3.14);
Q17: What's the difference between static final and just static variables?
Answer: The difference lies in mutability and initialization requirements:
public class StaticComparison {
// static - mutable, can be changed
private static int mutableStatic = 10;
// static final - immutable, must be initialized
private static final int IMMUTABLE_STATIC = 20;
private static final List<String> FINAL_LIST = new ArrayList<>();
// Compilation error - final must be initialized
// private static final int UNINITIALIZED; // Error
static {
// Can modify static variables in static block
mutableStatic = 15;
// Cannot reassign static final variables
// IMMUTABLE_STATIC = 25; // Compilation error
// But can modify the contents of final objects
FINAL_LIST.add("Item 1"); // This is allowed
}
public static void demonstrate() {
// Can change static variables
mutableStatic = 30;
System.out.println("Mutable static: " + mutableStatic);
// Cannot reassign static final variables
// IMMUTABLE_STATIC = 40; // Compilation error
System.out.println("Immutable static: " + IMMUTABLE_STATIC);
// Can modify contents of final objects
FINAL_LIST.add("Item 2");
System.out.println("Final list: " + FINAL_LIST);
// Cannot reassign the reference
// FINAL_LIST = new ArrayList<>(); // Compilation error
}
}
Key Differences:
static
: Mutable, can be reassigned, initialized with default values if not explicitly initializedstatic final
: Immutable reference, cannot be reassigned, must be initialized before class loading completes, but object contents can be modified
Q18: How does static initialization work in multithreaded environment?
Answer: Static initialization is thread-safe by default. The JVM ensures that static blocks and static variable initialization happen exactly once, even in multithreaded environments.
public class ThreadSafeStatic {
private static volatile boolean initializationStarted = false;
private static volatile boolean initializationCompleted = false;
static {
System.out.println("Thread " + Thread.currentThread().getName() +
" started static initialization");
initializationStarted = true;
// Simulate slow initialization
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
initializationCompleted = true;
System.out.println("Thread " + Thread.currentThread().getName() +
" completed static initialization");
}
public static void checkInitialization() {
System.out.println("Thread " + Thread.currentThread().getName() +
" - Started: " + initializationStarted +
", Completed: " + initializationCompleted);
}
}
// Test with multiple threads
public class StaticThreadTest {
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
System.out.println("Thread " + Thread.currentThread().getName() + " calling static method");
ThreadSafeStatic.checkInitialization();
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
Thread t3 = new Thread(task, "Thread-3");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
// Output shows that only one thread performs initialization:
// Thread Thread-1 calling static method
// Thread Thread-2 calling static method
// Thread Thread-3 calling static method
// Thread Thread-1 started static initialization
// Thread Thread-1 completed static initialization
// Thread Thread-1 - Started: true, Completed: true
// Thread Thread-2 - Started: true, Completed: true
// Thread Thread-3 - Started: true, Completed: true
The JVM guarantees that:
Static initialization happens exactly once
All threads see the fully initialized state
No explicit synchronization is needed for static initialization
This makes static blocks and static variable initialization naturally thread-safe for the initialization process itself.
Subscribe to my newsletter
Read articles from Karam Srikanth Reddy directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Karam Srikanth Reddy
Karam Srikanth Reddy
Don't hope that events will turn out the way you want, welcome events whichever way they happen: this is the path to peace. Epictetus