Java Static Variables, Methods, and Classes
Master Java Static Concepts: Learn static variables, static methods, static classes, static initialization blocks, memory management, utility patterns, and real-world examples.
Static Variables
Class-level storage
Static Methods
Class-level operations
Static Classes
Nested utility classes
Memory Management
Heap vs Stack vs Static
1. Introduction to Static in Java
The static keyword in Java is used for memory management mainly. It indicates that a particular member (variable, method, or class) belongs to the class itself, rather than to instances (objects) of the class.
What is Static?
- Class-level: Belongs to class, not objects
- Shared: Single copy shared by all instances
- No Object Needed: Can access without creating object
- Early Loading: Loaded when class is loaded
- Memory Efficiency: Single copy saves memory
- Lifecycle: Exists for program duration
When to Use Static?
- Constants: Values that don't change (Math.PI)
- Counters: Track number of objects created
- Utility Methods: Math operations, validation
- Factory Methods: Create objects (getInstance())
- Singleton Pattern: Ensure single instance
- Shared Resources: Configuration, cache, pool
- Main Method: Program entry point
The Static Concept Explained
Think of static members as "class citizens" and instance members as "object citizens". Static members live with the class blueprint, while instance members live with each object created from that blueprint. Just like a factory blueprint (static) vs actual cars produced (instance).
Real-world Analogy
Static = Company Headquarters
- Shared by all branches (objects)
- Exists before branches open
- Single location for entire company
- Managers can call HQ without visiting a branch
- Company policies apply to all branches
Instance = Individual Branch
- Each branch has its own staff, inventory
- Created when branch opens
- Independent of other branches
- Can access HQ resources
- Has branch-specific data
// Demonstrating basic static concepts
class Company {
// Static variable (class variable)
static String companyName = "TechCorp Inc.";
static String headquarters = "Silicon Valley";
static int totalEmployees = 0; // Shared by all objects
// Instance variables (object variables)
String branchName;
String location;
int branchEmployees;
// Static block (runs when class is loaded)
static {
System.out.println("Company class loaded. Initializing static resources...");
System.out.println("Company: " + companyName);
System.out.println("Headquarters: " + headquarters);
}
// Constructor
public Company(String branchName, String location, int branchEmployees) {
this.branchName = branchName;
this.location = location;
this.branchEmployees = branchEmployees;
totalEmployees += branchEmployees; // Update static variable
System.out.println("Created branch: " + branchName);
}
// Static method (class method)
public static void displayCompanyInfo() {
System.out.println("\n=== Company Information ===");
System.out.println("Company Name: " + companyName);
System.out.println("Headquarters: " + headquarters);
System.out.println("Total Employees: " + totalEmployees);
// Cannot access instance variables here!
// System.out.println(branchName); // COMPILE ERROR
}
// Instance method (object method)
public void displayBranchInfo() {
System.out.println("\n=== Branch Information ===");
System.out.println("Branch: " + branchName);
System.out.println("Location: " + location);
System.out.println("Branch Employees: " + branchEmployees);
// Can access static variables
System.out.println("Company: " + companyName);
System.out.println("Total Company Employees: " + totalEmployees);
}
// Another static method
public static double calculateRevenuePerEmployee(double totalRevenue) {
if (totalEmployees > 0) {
return totalRevenue / totalEmployees;
}
return 0;
}
}
public class BasicStaticExample {
public static void main(String[] args) {
System.out.println("=== Basic Static Concepts ===\n");
// Access static members WITHOUT creating object
System.out.println("1. Accessing static members:");
System.out.println("Company Name: " + Company.companyName);
System.out.println("Headquarters: " + Company.headquarters);
Company.displayCompanyInfo();
// Create objects (instances)
System.out.println("\n2. Creating branches (objects):");
Company nyBranch = new Company("New York", "Manhattan", 50);
Company laBranch = new Company("Los Angeles", "Downtown", 75);
Company chiBranch = new Company("Chicago", "Loop", 40);
// Display branch info
nyBranch.displayBranchInfo();
laBranch.displayBranchInfo();
chiBranch.displayBranchInfo();
// Static variable is shared - all see same value
System.out.println("\n3. Static variable sharing:");
System.out.println("Total employees (from NY branch): " + nyBranch.totalEmployees);
System.out.println("Total employees (from LA branch): " + laBranch.totalEmployees);
System.out.println("Total employees (from Company class): " + Company.totalEmployees);
// All refer to same memory location
System.out.println("\n4. Static method usage:");
double revenue = 10000000.0;
double revenuePerEmployee = Company.calculateRevenuePerEmployee(revenue);
System.out.println("Revenue per employee: $" + revenuePerEmployee);
// Update static variable
System.out.println("\n5. Modifying static variable:");
Company.totalEmployees += 10; // Add corporate employees
System.out.println("Updated total employees: " + Company.totalEmployees);
// Instance variable vs static variable
System.out.println("\n6. Instance vs Static:");
System.out.println("Each branch has its own 'branchEmployees':");
System.out.println("NY: " + nyBranch.branchEmployees);
System.out.println("LA: " + laBranch.branchEmployees);
System.out.println("CHI: " + chiBranch.branchEmployees);
System.out.println("\nAll share same 'totalEmployees': " + Company.totalEmployees);
// Cannot call instance method without object
// Company.displayBranchInfo(); // COMPILE ERROR
// Cannot access instance variable without object
// System.out.println(Company.branchName); // COMPILE ERROR
}
}
2. Static Variables (Class Variables)
Static variables are class-level variables that are shared among all instances of the class. They are initialized only once, at the start of program execution, and exist for the lifetime of the program.
// Comprehensive static variables example
import java.util.ArrayList;
import java.util.List;
class Bank {
// ========== STATIC VARIABLES ==========
// 1. Static constant (final static)
public static final String BANK_NAME = "Global Bank";
public static final double INTEREST_RATE = 4.5;
public static final int MAX_ACCOUNTS_PER_CUSTOMER = 5;
// 2. Static variable for counting objects
private static int totalAccountsCreated = 0;
// 3. Static variable for shared resource
private static double totalBankBalance = 0.0;
// 4. Static collection shared by all instances
private static List allAccountNumbers = new ArrayList<>();
// 5. Static variable with lazy initialization
private static BankStatistics statistics = null;
// 6. Static variable for configuration
private static Configuration config = new Configuration();
// 7. Static variable tracking class state
private static boolean isBankOpen = true;
// ========== INSTANCE VARIABLES ==========
private String accountNumber;
private String accountHolder;
private double balance;
private AccountType type;
// ========== CONSTRUCTOR ==========
public Bank(String accountHolder, double initialBalance, AccountType type) {
this.accountNumber = generateAccountNumber();
this.accountHolder = accountHolder;
this.balance = initialBalance;
this.type = type;
// Update static variables
totalAccountsCreated++;
totalBankBalance += initialBalance;
allAccountNumbers.add(accountNumber);
System.out.println("Account created: " + accountNumber +
" for " + accountHolder);
}
// ========== STATIC METHODS ==========
public static int getTotalAccountsCreated() {
return totalAccountsCreated;
}
public static double getTotalBankBalance() {
return totalBankBalance;
}
public static List getAllAccountNumbers() {
// Return copy to prevent modification
return new ArrayList<>(allAccountNumbers);
}
public static BankStatistics getStatistics() {
if (statistics == null) {
statistics = new BankStatistics();
}
return statistics;
}
public static boolean isBankOpen() {
return isBankOpen;
}
public static void closeBank() {
isBankOpen = false;
System.out.println("Bank is now closed for the day");
}
public static void openBank() {
isBankOpen = true;
System.out.println("Bank is now open");
}
// Static utility method
private static String generateAccountNumber() {
return "ACC" + (100000 + totalAccountsCreated);
}
// ========== INSTANCE METHODS ==========
public void deposit(double amount) {
if (isBankOpen && amount > 0) {
balance += amount;
totalBankBalance += amount; // Update static variable
System.out.println("Deposited $" + amount + " to " + accountNumber);
}
}
public void withdraw(double amount) {
if (isBankOpen && amount > 0 && balance >= amount) {
balance -= amount;
totalBankBalance -= amount; // Update static variable
System.out.println("Withdrew $" + amount + " from " + accountNumber);
}
}
public void transfer(Bank recipient, double amount) {
if (isBankOpen && amount > 0 && balance >= amount) {
this.balance -= amount;
recipient.balance += amount;
// totalBankBalance unchanged (money stays in bank)
System.out.println("Transferred $" + amount + " from " +
accountNumber + " to " + recipient.accountNumber);
}
}
// ========== GETTERS ==========
public String getAccountNumber() {
return accountNumber;
}
public String getAccountHolder() {
return accountHolder;
}
public double getBalance() {
return balance;
}
public AccountType getType() {
return type;
}
@Override
public String toString() {
return String.format("Account[%s: %s - $%.2f (%s)]",
accountNumber, accountHolder, balance, type);
}
}
// ========== SUPPORTING CLASSES ==========
enum AccountType {
SAVINGS, CHECKING, BUSINESS, STUDENT
}
class BankStatistics {
private int transactionsToday = 0;
private double averageBalance = 0.0;
public void recordTransaction() {
transactionsToday++;
}
public double calculateAverageBalance() {
int totalAccounts = Bank.getTotalAccountsCreated();
if (totalAccounts > 0) {
return Bank.getTotalBankBalance() / totalAccounts;
}
return 0.0;
}
public void displayStatistics() {
System.out.println("\n=== Bank Statistics ===");
System.out.println("Total Accounts: " + Bank.getTotalAccountsCreated());
System.out.println("Total Bank Balance: $" + Bank.getTotalBankBalance());
System.out.println("Average Balance: $" + calculateAverageBalance());
System.out.println("Transactions Today: " + transactionsToday);
System.out.println("Bank Open: " + Bank.isBankOpen());
}
}
class Configuration {
private int maxWithdrawalLimit = 5000;
private double overdraftFee = 35.0;
private String currency = "USD";
public int getMaxWithdrawalLimit() {
return maxWithdrawalLimit;
}
public double getOverdraftFee() {
return overdraftFee;
}
public String getCurrency() {
return currency;
}
}
public class StaticVariablesExample {
public static void main(String[] args) {
System.out.println("=== Static Variables (Class Variables) ===\n");
System.out.println("1. Accessing static constants:");
System.out.println("Bank Name: " + Bank.BANK_NAME);
System.out.println("Interest Rate: " + Bank.INTEREST_RATE + "%");
System.out.println("Max Accounts: " + Bank.MAX_ACCOUNTS_PER_CUSTOMER);
System.out.println("\n2. Creating bank accounts:");
Bank account1 = new Bank("Alice Johnson", 5000.0, AccountType.SAVINGS);
Bank account2 = new Bank("Bob Smith", 3000.0, AccountType.CHECKING);
Bank account3 = new Bank("Charlie Brown", 10000.0, AccountType.BUSINESS);
System.out.println("\n3. Static variable sharing demonstration:");
System.out.println("Total accounts created: " + Bank.getTotalAccountsCreated());
System.out.println("Total bank balance: $" + Bank.getTotalBankBalance());
System.out.println("\n4. Performing transactions:");
account1.deposit(1000.0);
account2.withdraw(500.0);
account3.transfer(account1, 2000.0);
System.out.println("\n5. After transactions:");
System.out.println("Total bank balance: $" + Bank.getTotalBankBalance());
System.out.println("Account 1 balance: $" + account1.getBalance());
System.out.println("Account 2 balance: $" + account2.getBalance());
System.out.println("Account 3 balance: $" + account3.getBalance());
System.out.println("\n6. Shared static collection:");
List allAccounts = Bank.getAllAccountNumbers();
System.out.println("All account numbers: " + allAccounts);
System.out.println("\n7. Singleton pattern with static variable:");
BankStatistics stats1 = Bank.getStatistics();
BankStatistics stats2 = Bank.getStatistics();
System.out.println("Same statistics object? " + (stats1 == stats2));
stats1.recordTransaction();
stats1.recordTransaction();
stats1.displayStatistics();
System.out.println("\n8. Static variable for class state:");
System.out.println("Bank open? " + Bank.isBankOpen());
Bank.closeBank();
System.out.println("Bank open? " + Bank.isBankOpen());
// Try transaction when bank is closed
account1.deposit(100.0); // Should not work
Bank.openBank();
account1.deposit(100.0); // Should work now
System.out.println("\n9. Memory demonstration:");
System.out.println("Each Bank object has:");
System.out.println(" - Instance variables: accountNumber, accountHolder, balance, type");
System.out.println(" - Shared static variables: totalAccountsCreated, totalBankBalance, etc.");
System.out.println("\nMemory layout:");
System.out.println("Heap: 3 Bank objects (each ~40 bytes)");
System.out.println("Static Area: 1 copy of static variables");
System.out.println("Total memory: ~120 bytes (heap) + ~50 bytes (static)");
System.out.println("Without static, would need: ~170 bytes * 3 = 510 bytes");
System.out.println("\n10. Creating more accounts to see counter:");
Bank account4 = new Bank("Diana Prince", 7500.0, AccountType.SAVINGS);
Bank account5 = new Bank("Ethan Hunt", 2500.0, AccountType.STUDENT);
System.out.println("\nFinal statistics:");
stats1.displayStatistics();
System.out.println("\n=== Static Variable Rules ===");
System.out.println("1. Single copy shared by all instances");
System.out.println("2. Loaded when class is loaded (before object creation)");
System.out.println("3. Can be accessed without object: ClassName.variable");
System.out.println("4. Can be accessed with object: object.variable (not recommended)");
System.out.println("5. Default value based on type (0, null, false)");
System.out.println("6. Can be public, private, protected, or default");
System.out.println("7. Can be final (constant)");
System.out.println("8. Exist for program lifetime");
}
}
Static Variables - Rules & Characteristics
| Feature | Static Variable | Instance Variable | Explanation |
|---|---|---|---|
| Declaration | static type name; |
type name; |
Static keyword required |
| Memory | Static area (method area) | Heap (with object) | Different memory locations |
| Copies | One per class | One per object | Static shared, instance unique |
| Loading Time | When class loads | When object creates | Static earlier than instance |
| Access | ClassName.variable | object.variable | Static doesn't need object |
| Lifetime | Program duration | Object lifetime | Static lives longer |
| Default Value | Based on type | Based on type | Both get default values |
| Scope | Class level | Object level | Different scopes |
| Use Case | Counters, constants, shared resources | Object state, individual properties | Different purposes |
| Thread Safety | Not thread-safe (shared) | Thread-safe per object | Static requires synchronization |
Memory Layout: Static vs Instance Variables
Static Memory Area
Single copy for entire class
Heap Memory (Object 1)
Unique to each object
Heap Memory (Object 2)
Unique to each object
Key Points:
- Static variables: One copy in static memory area
- Instance variables: Each object has its own copy in heap
- All objects reference the same static variables
- Static area exists when class loads, heap objects exist when created
3. Static Methods (Class Methods)
Static methods belong to the class rather than any object instance. They can be called without creating an object of the class and can only access static members directly.
// Comprehensive static methods example
import java.util.Arrays;
class MathUtils {
// Static constants
public static final double PI = 3.141592653589793;
public static final double E = 2.718281828459045;
public static final double GOLDEN_RATIO = 1.618033988749895;
// Static variable for method call tracking
private static int methodCallCount = 0;
// ========== STATIC METHODS ==========
// 1. Basic static method
public static double add(double a, double b) {
methodCallCount++;
return a + b;
}
public static double subtract(double a, double b) {
methodCallCount++;
return a - b;
}
public static double multiply(double a, double b) {
methodCallCount++;
return a * b;
}
public static double divide(double a, double b) {
methodCallCount++;
if (b == 0) {
throw new IllegalArgumentException("Division by zero");
}
return a / b;
}
// 2. Static method with array parameter
public static double average(double... numbers) {
methodCallCount++;
if (numbers == null || numbers.length == 0) {
return 0.0;
}
double sum = 0.0;
for (double num : numbers) {
sum += num;
}
return sum / numbers.length;
}
// 3. Static method returning complex object
public static Statistics calculateStatistics(double[] data) {
methodCallCount++;
if (data == null || data.length == 0) {
return new Statistics(0, 0, 0, 0);
}
double sum = 0.0;
double min = data[0];
double max = data[0];
for (double value : data) {
sum += value;
if (value < min) min = value;
if (value > max) max = value;
}
double mean = sum / data.length;
return new Statistics(sum, mean, min, max);
}
// 4. Static factory method
public static Circle createCircle(double radius) {
methodCallCount++;
return new Circle(radius);
}
public static Rectangle createRectangle(double width, double height) {
methodCallCount++;
return new Rectangle(width, height);
}
// 5. Static method with generic type
public static boolean contains(T[] array, T element) {
methodCallCount++;
if (array == null) return false;
for (T item : array) {
if (item == null && element == null) return true;
if (item != null && item.equals(element)) return true;
}
return false;
}
// 6. Static method that calls other static methods
public static double circleArea(double radius) {
methodCallCount++;
return multiply(PI, multiply(radius, radius));
}
public static double circleCircumference(double radius) {
methodCallCount++;
return multiply(2, multiply(PI, radius));
}
// 7. Static method with recursion
public static int factorial(int n) {
methodCallCount++;
if (n < 0) {
throw new IllegalArgumentException("Factorial undefined for negative numbers");
}
if (n == 0 || n == 1) {
return 1;
}
return n * factorial(n - 1);
}
// 8. Static method with validation
public static boolean isValidEmail(String email) {
methodCallCount++;
if (email == null || email.isEmpty()) {
return false;
}
// Simple email validation
return email.contains("@") &&
email.contains(".") &&
email.indexOf("@") < email.lastIndexOf(".");
}
// 9. Static utility method
public static String formatCurrency(double amount) {
methodCallCount++;
return String.format("$%.2f", amount);
}
// 10. Static method that throws exception
public static double sqrt(double number) throws IllegalArgumentException {
methodCallCount++;
if (number < 0) {
throw new IllegalArgumentException("Cannot calculate square root of negative number");
}
return Math.sqrt(number);
}
// Static getter for static variable
public static int getMethodCallCount() {
return methodCallCount;
}
public static void resetMethodCallCount() {
methodCallCount = 0;
}
// CANNOT have instance methods here (no 'this' reference)
// public void instanceMethod() { } // Would need non-static context
// ========== STATIC NESTED CLASSES ==========
static class Circle {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getArea() {
return MathUtils.circleArea(radius);
}
public double getCircumference() {
return MathUtils.circleCircumference(radius);
}
}
static class Rectangle {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getArea() {
return multiply(width, height);
}
public double getPerimeter() {
return multiply(2, add(width, height));
}
}
static class Statistics {
private double sum;
private double mean;
private double min;
private double max;
public Statistics(double sum, double mean, double min, double max) {
this.sum = sum;
this.mean = mean;
this.min = min;
this.max = max;
}
@Override
public String toString() {
return String.format("Sum: %.2f, Mean: %.2f, Min: %.2f, Max: %.2f",
sum, mean, min, max);
}
}
}
// Another example: String utilities
class StringUtils {
// Private constructor to prevent instantiation
private StringUtils() {
throw new UnsupportedOperationException("Utility class - cannot be instantiated");
}
public static boolean isNullOrEmpty(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();
}
public static boolean isPalindrome(String str) {
if (str == null) return false;
String cleaned = str.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
return cleaned.equals(reverse(cleaned));
}
public static int countWords(String str) {
if (isNullOrEmpty(str)) return 0;
return str.trim().split("\\s+").length;
}
}
// Singleton pattern using static method
class ConfigurationManager {
// Static variable for singleton instance
private static ConfigurationManager instance;
// Private constructor
private ConfigurationManager() {
// Load configuration
System.out.println("ConfigurationManager initialized");
}
// Static factory method (singleton pattern)
public static ConfigurationManager getInstance() {
if (instance == null) {
instance = new ConfigurationManager();
}
return instance;
}
public void loadConfig() {
System.out.println("Loading configuration...");
}
}
public class StaticMethodsExample {
public static void main(String[] args) {
System.out.println("=== Static Methods (Class Methods) ===\n");
System.out.println("1. Basic math operations:");
System.out.println("5 + 3 = " + MathUtils.add(5, 3));
System.out.println("10 - 4 = " + MathUtils.subtract(10, 4));
System.out.println("6 * 7 = " + MathUtils.multiply(6, 7));
System.out.println("15 / 3 = " + MathUtils.divide(15, 3));
System.out.println("\n2. Using static constants:");
System.out.println("PI = " + MathUtils.PI);
System.out.println("E = " + MathUtils.E);
System.out.println("Golden Ratio = " + MathUtils.GOLDEN_RATIO);
System.out.println("\n3. Array operations:");
double[] numbers = {2.5, 3.7, 1.8, 4.2, 5.1};
System.out.println("Average: " + MathUtils.average(numbers));
System.out.println("Statistics: " + MathUtils.calculateStatistics(numbers));
System.out.println("\n4. Factory methods:");
MathUtils.Circle circle = MathUtils.createCircle(5.0);
MathUtils.Rectangle rect = MathUtils.createRectangle(4.0, 6.0);
System.out.println("Circle area: " + circle.getArea());
System.out.println("Rectangle area: " + rect.getArea());
System.out.println("\n5. Generic method:");
String[] fruits = {"Apple", "Banana", "Orange"};
System.out.println("Contains 'Banana'? " + MathUtils.contains(fruits, "Banana"));
System.out.println("Contains 'Grape'? " + MathUtils.contains(fruits, "Grape"));
System.out.println("\n6. Recursive method:");
System.out.println("Factorial of 5: " + MathUtils.factorial(5));
System.out.println("\n7. Validation methods:");
System.out.println("Is 'test@email.com' valid? " + MathUtils.isValidEmail("test@email.com"));
System.out.println("Is 'invalid' valid? " + MathUtils.isValidEmail("invalid"));
System.out.println("\n8. String utilities (pure static class):");
System.out.println("Is null or empty? " + StringUtils.isNullOrEmpty(""));
System.out.println("Reverse 'hello': " + StringUtils.reverse("hello"));
System.out.println("Is 'racecar' palindrome? " + StringUtils.isPalindrome("racecar"));
System.out.println("Word count: " + StringUtils.countWords("Java is awesome"));
// Try to instantiate StringUtils (should fail)
// StringUtils utils = new StringUtils(); // COMPILE ERROR
System.out.println("\n9. Singleton pattern with static method:");
ConfigurationManager config1 = ConfigurationManager.getInstance();
ConfigurationManager config2 = ConfigurationManager.getInstance();
System.out.println("Same instance? " + (config1 == config2));
config1.loadConfig();
System.out.println("\n10. Method call tracking:");
System.out.println("Total method calls: " + MathUtils.getMethodCallCount());
MathUtils.resetMethodCallCount();
System.out.println("After reset: " + MathUtils.getMethodCallCount());
// Demonstrate what CANNOT be done
System.out.println("\n=== Static Method Restrictions ===");
System.out.println("1. Cannot access instance variables directly");
System.out.println("2. Cannot call instance methods directly");
System.out.println("3. Cannot use 'this' or 'super' keywords");
System.out.println("4. Can only directly access other static members");
System.out.println("\n=== When to Use Static Methods ===");
System.out.println("✅ Utility/helper methods (MathUtils, StringUtils)");
System.out.println("✅ Factory methods (create objects)");
System.out.println("✅ Singleton getInstance() method");
System.out.println("✅ Main method (program entry point)");
System.out.println("✅ Methods that don't need object state");
System.out.println("✅ Methods that operate only on parameters");
System.out.println("\n=== When NOT to Use Static Methods ===");
System.out.println("❌ Methods that need object state");
System.out.println("❌ Methods that override/implement");
System.out.println("❌ Methods using polymorphism");
System.out.println("❌ Methods that modify object state");
}
}
Static Methods vs Instance Methods
| Aspect | Static Method | Instance Method |
|---|---|---|
| Keyword | static in declaration |
No static keyword |
| Belongs to | Class | Object (instance) |
| Calling | ClassName.method() | object.method() |
| Memory | Loaded with class | Loaded with object |
| Access to | Only static members | Both static and instance |
| this/super | Cannot use | Can use |
| Overriding | Cannot be overridden | Can be overridden |
| Polymorphism | No (compile-time binding) | Yes (runtime binding) |
| Use Case | Utilities, factories | Object behavior |
Static Method Restrictions
- Cannot directly access instance variables
- Cannot directly call instance methods
- Cannot use 'this' or 'super' keywords
- Cannot be abstract (except in interfaces)
- Cannot be synchronized on instance
- Early binding (compile-time polymorphism)
- Cannot be overridden (but can be hidden)
When to Use Static Methods
- Utility/helper methods (Math.abs())
- Factory methods (Calendar.getInstance())
- Singleton pattern (getInstance())
- Main method entry point
- Methods that don't need object state
- Methods that only work with parameters
- Performance-critical code (no object creation)
// Common mistakes with static methods
class Employee {
private String name; // Instance variable
private int id; // Instance variable
private static int totalEmployees = 0; // Static variable
public Employee(String name, int id) {
this.name = name;
this.id = id;
totalEmployees++;
}
// Instance method - OK
public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("ID: " + id);
System.out.println("Total Employees: " + totalEmployees);
}
// Static method - COMMON MISTAKES:
// MISTAKE 1: Trying to access instance variables
/*
public static void printName() {
System.out.println(name); // COMPILE ERROR!
// Cannot make static reference to non-static field
}
*/
// MISTAKE 2: Trying to call instance method
/*
public static void callDisplay() {
displayInfo(); // COMPILE ERROR!
// Cannot make static reference to non-static method
}
*/
// MISTAKE 3: Using 'this' keyword
/*
public static void printThis() {
System.out.println(this.name); // COMPILE ERROR!
// Cannot use 'this' in static context
}
*/
// CORRECT: Accessing static variable
public static void printTotalEmployees() {
System.out.println("Total Employees: " + totalEmployees); // OK
}
// CORRECT: Can access instance through parameter
public static void printEmployeeInfo(Employee emp) {
System.out.println("Name: " + emp.name); // OK - has object reference
System.out.println("ID: " + emp.id); // OK - has object reference
}
}
// Method hiding vs overriding
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 HIDES the parent static method (not override)
public static void staticMethod() {
System.out.println("Child static method");
}
// This OVERRIDES the parent instance method
@Override
public void instanceMethod() {
System.out.println("Child instance method");
}
}
public class StaticMethodMistakes {
public static void main(String[] args) {
System.out.println("=== Common Static Method Mistakes ===\n");
Employee emp1 = new Employee("Alice", 101);
Employee emp2 = new Employee("Bob", 102);
// Correct usage
Employee.printTotalEmployees();
Employee.printEmployeeInfo(emp1);
// Method hiding demonstration
System.out.println("\n=== Method Hiding vs Overriding ===");
Parent parent = new Parent();
Child child = new Child();
Parent parentRefToChild = new Child();
// Static method - compile-time binding
parent.staticMethod(); // Parent static method
child.staticMethod(); // Child static method
parentRefToChild.staticMethod(); // Parent static method (HIDING)
// Instance method - runtime binding
parent.instanceMethod(); // Parent instance method
child.instanceMethod(); // Child instance method
parentRefToChild.instanceMethod(); // Child instance method (OVERRIDING)
System.out.println("\n=== Key Points ===");
System.out.println("1. Static methods are bound at compile-time");
System.out.println("2. Instance methods are bound at runtime");
System.out.println("3. Static method in subclass HIDES parent method");
System.out.println("4. Instance method in subclass OVERRIDES parent method");
System.out.println("5. Reference type determines static method call");
System.out.println("6. Object type determines instance method call");
}
}
4. Static Classes & Nested Static Classes
In Java, static classes are nested classes declared as static. They don't have access to instance members of the outer class and can be instantiated without an instance of the outer class.
// Comprehensive static classes example
// ========== OUTER CLASS ==========
class University {
// Static variables of outer class
private static String universityName = "Tech University";
private static int establishedYear = 1950;
private static int totalStudents = 0;
// Instance variables
private String departmentName;
private int departmentStudents;
// Constructor
public University(String departmentName, int departmentStudents) {
this.departmentName = departmentName;
this.departmentStudents = departmentStudents;
totalStudents += departmentStudents;
}
// ========== STATIC NESTED CLASS ==========
// Can be public, private, protected, or package-private
public static class Student {
// Can have its own static variables
private static int nextStudentId = 1000;
// Instance variables
private int studentId;
private String name;
private String major;
private double gpa;
// Constructor
public Student(String name, String major) {
this.studentId = nextStudentId++;
this.name = name;
this.major = major;
this.gpa = 0.0;
}
// Can access static members of outer class DIRECTLY
public void displayUniversityInfo() {
System.out.println("University: " + universityName);
System.out.println("Established: " + establishedYear);
System.out.println("Total Students: " + totalStudents);
}
// CANNOT access instance members of outer class
/*
public void displayDepartmentInfo() {
System.out.println(departmentName); // COMPILE ERROR!
// Cannot make static reference to non-static field
}
*/
// Instance methods
public void enroll() {
System.out.println(name + " (ID: " + studentId +
") enrolled in " + major);
}
public void updateGPA(double newGPA) {
this.gpa = newGPA;
System.out.println(name + "'s GPA updated to: " + gpa);
}
// Static method in static nested class
public static int getNextStudentId() {
return nextStudentId;
}
// Getters
public int getStudentId() { return studentId; }
public String getName() { return name; }
public String getMajor() { return major; }
public double getGpa() { return gpa; }
@Override
public String toString() {
return String.format("Student[%d: %s - %s (GPA: %.2f)]",
studentId, name, major, gpa);
}
}
// ========== ANOTHER STATIC NESTED CLASS ==========
private static class Statistics {
// Private static nested class - only accessible within University
private static int graduatesThisYear = 0;
public static void recordGraduation() {
graduatesThisYear++;
totalStudents--; // Can access outer class static variable
}
public static void displayStatistics() {
System.out.println("\n=== University Statistics ===");
System.out.println("University: " + universityName);
System.out.println("Total Students: " + totalStudents);
System.out.println("Graduates This Year: " + graduatesThisYear);
System.out.println("Next Student ID: " + Student.getNextStudentId());
}
}
// ========== STATIC NESTED ENUM ==========
public static enum DegreeType {
BACHELOR("Bachelor's Degree", 4),
MASTER("Master's Degree", 2),
DOCTORATE("Doctorate", 4),
ASSOCIATE("Associate Degree", 2);
private final String displayName;
private final int typicalYears;
DegreeType(String displayName, int typicalYears) {
this.displayName = displayName;
this.typicalYears = typicalYears;
}
public String getDisplayName() {
return displayName;
}
public int getTypicalYears() {
return typicalYears;
}
}
// ========== OUTER CLASS METHODS ==========
public void displayDepartmentInfo() {
System.out.println("\n=== Department Info ===");
System.out.println("Department: " + departmentName);
System.out.println("Students: " + departmentStudents);
System.out.println("University: " + universityName);
// Can access static nested classes
Statistics.displayStatistics();
}
public void graduateStudent(Student student) {
System.out.println("\n" + student.getName() + " is graduating!");
Statistics.recordGraduation();
departmentStudents--;
}
// Static method in outer class
public static void displayUniversityOverview() {
System.out.println("\n=== University Overview ===");
System.out.println("Name: " + universityName);
System.out.println("Established: " + establishedYear);
System.out.println("Total Students: " + totalStudents);
// Can create instances of static nested class
Student prospective = new Student("Prospective", "Undecided");
System.out.println("Next available ID: " + Student.getNextStudentId());
}
}
// ========== EXAMPLE OF COMPLETE UTILITY CLASS ==========
// Often used for organization and encapsulation
final class MathConstants {
// Private constructor to prevent instantiation
private MathConstants() {
throw new AssertionError("Cannot instantiate utility class");
}
// Static nested class for geometric constants
public static class Geometry {
public static final double PI = 3.141592653589793;
public static final double E = 2.718281828459045;
public static final double GOLDEN_RATIO = 1.618033988749895;
// Private constructor for nested static class too
private Geometry() {}
}
// Static nested class for physics constants
public static class Physics {
public static final double GRAVITY = 9.80665;
public static final double LIGHT_SPEED = 299792458;
public static final double PLANCK = 6.62607015e-34;
private Physics() {}
}
// Static nested class for conversion factors
public static class Conversions {
public static final double INCH_TO_CM = 2.54;
public static final double POUND_TO_KG = 0.453592;
public static final double MILE_TO_KM = 1.60934;
private Conversions() {}
}
}
// ========== BUILDER PATTERN WITH STATIC NESTED CLASS ==========
class Computer {
private final String cpu;
private final int ram;
private final int storage;
private final boolean hasGraphicsCard;
private final String operatingSystem;
// Private constructor
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.hasGraphicsCard = builder.hasGraphicsCard;
this.operatingSystem = builder.operatingSystem;
}
// Static nested Builder class
public static class Builder {
// Required parameters
private final String cpu;
private final int ram;
// Optional parameters - initialized to default values
private int storage = 256;
private boolean hasGraphicsCard = false;
private String operatingSystem = "Windows 10";
// Constructor for required fields
public Builder(String cpu, int ram) {
this.cpu = cpu;
this.ram = ram;
}
// Setters for optional parameters (return Builder for chaining)
public Builder storage(int storage) {
this.storage = storage;
return this;
}
public Builder graphicsCard(boolean hasGraphicsCard) {
this.hasGraphicsCard = hasGraphicsCard;
return this;
}
public Builder operatingSystem(String operatingSystem) {
this.operatingSystem = operatingSystem;
return this;
}
// Build method
public Computer build() {
return new Computer(this);
}
}
// Getters
public String getCpu() { return cpu; }
public int getRam() { return ram; }
public int getStorage() { return storage; }
public boolean hasGraphicsCard() { return hasGraphicsCard; }
public String getOperatingSystem() { return operatingSystem; }
@Override
public String toString() {
return String.format("Computer[CPU: %s, RAM: %dGB, Storage: %dGB, GPU: %s, OS: %s]",
cpu, ram, storage, hasGraphicsCard ? "Yes" : "No", operatingSystem);
}
}
public class StaticClassesExample {
public static void main(String[] args) {
System.out.println("=== Static Classes & Nested Static Classes ===\n");
System.out.println("1. Creating University departments:");
University csDept = new University("Computer Science", 300);
University engDept = new University("Engineering", 250);
System.out.println("\n2. Creating Students (using static nested class):");
// Note: Can create Student without University instance
University.Student student1 = new University.Student("Alice", "Computer Science");
University.Student student2 = new University.Student("Bob", "Engineering");
University.Student student3 = new University.Student("Charlie", "Mathematics");
student1.enroll();
student2.enroll();
student3.enroll();
student1.updateGPA(3.8);
student2.updateGPA(3.5);
student3.updateGPA(3.9);
System.out.println("\n3. Students accessing university info:");
student1.displayUniversityInfo();
System.out.println("\n4. Using static nested enum:");
University.DegreeType bachelors = University.DegreeType.BACHELOR;
University.DegreeType masters = University.DegreeType.MASTER;
System.out.println(bachelors.getDisplayName() + " typically takes " +
bachelors.getTypicalYears() + " years");
System.out.println(masters.getDisplayName() + " typically takes " +
masters.getTypicalYears() + " years");
System.out.println("\n5. Department displaying info:");
csDept.displayDepartmentInfo();
System.out.println("\n6. University static method:");
University.displayUniversityOverview();
System.out.println("\n7. Using MathConstants utility class:");
System.out.println("PI: " + MathConstants.Geometry.PI);
System.out.println("Gravity: " + MathConstants.Physics.GRAVITY);
System.out.println("Inch to CM: " + MathConstants.Conversions.INCH_TO_CM);
// Try to instantiate (should be prevented by private constructor)
// MathConstants constants = new MathConstants(); // COMPILE ERROR
System.out.println("\n8. Builder pattern with static nested class:");
Computer gamingPC = new Computer.Builder("Intel i9", 32)
.storage(1000)
.graphicsCard(true)
.operatingSystem("Windows 11")
.build();
Computer officePC = new Computer.Builder("Intel i5", 16)
.operatingSystem("Ubuntu 22.04")
.build();
System.out.println("Gaming PC: " + gamingPC);
System.out.println("Office PC: " + officePC);
System.out.println("\n=== Key Characteristics of Static Nested Classes ===");
System.out.println("1. Can be instantiated without outer class instance");
System.out.println("2. Can access outer class STATIC members directly");
System.out.println("3. CANNOT access outer class INSTANCE members directly");
System.out.println("4. Can be public, private, protected, or package-private");
System.out.println("5. Can have static and instance members");
System.out.println("6. Useful for logical grouping, encapsulation");
System.out.println("7. Commonly used for Builder pattern, constants, utilities");
System.out.println("\n=== When to Use Static Nested Classes ===");
System.out.println("✅ Builder pattern implementation");
System.out.println("✅ Constants organization (MathConstants.Geometry.PI)");
System.out.println("✅ Utility classes within a class");
System.out.println("✅ Node classes in data structures");
System.out.println("✅ Related classes that don't need outer instance");
System.out.println("✅ Enum definitions within a class");
System.out.println("\n=== Memory Considerations ===");
System.out.println("Static nested classes don't hold reference to outer instance");
System.out.println("More memory efficient than non-static inner classes");
System.out.println("Can be serialized independently");
}
}