1. Static fields: how to initialize them?
A static field (static) belongs not to an object, but to the class as a whole. Its value exists as a single instance for all objects of that class. By analogy — it’s like declaring a “shared cashbox” for all employees of a company: it doesn’t matter who shows up, the cashbox is shared by everyone.
Initialization at declaration
The simplest and most common way to initialize a static field is to assign it a value right at the declaration:
public class User {
private static int userCount = 0; // initialization right here
// ... other code
}
Such a field will be initialized when the User class is first loaded into memory.
Initialization in a static block
Sometimes you need more complex initialization logic — for example, to load data from a file or perform calculations. In this case, use a static initialization block:
public class Config {
public static String configPath;
static {
// This block will run ONCE when the class is loaded
configPath = System.getenv("APP_CONFIG_PATH");
if (configPath == null) {
configPath = "/etc/app/default.conf";
}
System.out.println("Config path initialized: " + configPath);
}
}
When is a static block executed?
- On the first reference to the class (for example, when creating the first object or calling any static method/field).
- It runs only once per class.
Accessing static fields
- Access static fields via the class name: User.userCount.
- You can also access them through an object, but that’s considered poor style (it confuses readers).
Example:
User u1 = new User();
User u2 = new User();
System.out.println(User.userCount); // correct
System.out.println(u1.userCount); // works, but not recommended!
2. final fields: when and how to initialize them?
final is a modifier that says: “This field can be assigned only once, and then it won’t change.” After initialization, the field’s value becomes immutable: for an object — it’s its fixed property, and for a class (if the field is static) — a shared constant.
Use cases:
- Constants (for example, PI).
- A unique object identifier that must not change after creation.
Requirements for initializing final fields
Java has a strict rule: every final field must be initialized either at declaration or in every constructor of the class.
Initialization at declaration
public class Circle {
public static final double PI = 3.1415926535; // class constant
private final String id = "CIRCLE"; // instance constant
}
Initialization in a constructor
Sometimes the value of a final field is known only when creating an object:
public class User {
private final int id;
public User(int id) {
this.id = id; // assign final field in the constructor
}
}
Important: if a class has multiple constructors, the final field must be initialized in each of them!
Combined example
public class Token {
private final String value;
private final long timestamp;
public Token(String value) {
this.value = value;
this.timestamp = System.currentTimeMillis();
}
}
Compilation errors for incorrect initialization
If you forget to initialize a final field, the compiler won’t let the program build:
public class Broken {
private final int x; // not initialized
public Broken() {
// x is not assigned!
}
}
// Error: variable x might not have been initialized
3. Combination of static and final: declaring class constants
The trio public static final is very common. In Java this is how you declare class constants — values that are set once and do not change during program execution. These constants belong to the class as a whole and are the same for all its objects.
Syntax and example
public class MathUtils {
public static final double PI = 3.1415926535;
public static final String APP_NAME = "MyApp";
}
Explanation:
- public — accessible everywhere.
- static — belongs to the class, not to a particular object.
- final — cannot be changed after initialization.
Usage
double area = MathUtils.PI * r * r;
System.out.println(MathUtils.APP_NAME);
Convention: constant names are usually written in UPPER_CASE_WITH_UNDERSCORES.
4. Code examples: ways to initialize static and final fields
Example 1: Simple class with a constant
public class Constants {
public static final int DAYS_IN_WEEK = 7;
public static final String COMPANY = "Daisy LLC";
}
Example 2: Static field initialized in a static block
public class AppConfig {
public static final String DEFAULT_PATH;
static {
// You can perform complex logic
String env = System.getenv("APP_PATH");
if (env != null) {
DEFAULT_PATH = env;
} else {
DEFAULT_PATH = "/usr/local/app";
}
}
}
Example 3: final instance field, initialization in constructor
public class User {
private static int nextId = 1;
private final int id;
private String name;
public User(String name) {
this.id = nextId++;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Usage:
User u1 = new User("Ivan");
User u2 = new User("Maria");
System.out.println(u1.getId()); // 1
System.out.println(u2.getId()); // 2
Example 4: Error due to incorrect final-field initialization
public class BadExample {
private final int number;
public BadExample() {
// number is not initialized!
}
}
// Compilation error: variable number might not have been initialized
5. Best practices for working with static and final fields
Use public static final only for true constants
If a value may change in the future (for example, a list of employees), don’t make it final! Constants must be truly immutable.
public static final int MAX_USERS = 1000; // good
public static final String[] USERS = new String[100]; // bad!
Although the reference USERS won’t change, the contents of the array can be modified. This can lead to unexpected bugs.
Use static blocks for complex initialization
If a constant can’t be expressed with a simple assignment (for example, it requires computation or reading from a file), use a static block.
Don’t overuse static fields
Static fields are global variables. Having too many of them can lead to hard-to-catch bugs and complicated maintenance.
Don’t make mutable objects public static final
If an object is mutable, don’t make it public and final. That will expose its internals to everyone, and someone will inevitably take advantage of it.
6. Common mistakes when initializing static and final fields
Error #1: Uninitialized final field. If you forget to initialize a final field either at declaration or in all constructors, the compiler will report an error. For example, if a class has two constructors and you forget to assign a value to the final field in one of them — you’ll get an error.
Error #2: Changing the value of a final field. Attempting to change a final field after initialization will result in a compilation error.
public class Demo {
private final int x = 5;
public void change() {
x = 10; // Error: cannot assign a value to final variable x
}
}
Error #3: Public mutable static final fields. If you make a mutable object (String[], int[], etc.) public static final, any code will be able to change its contents. This breaks encapsulation and can lead to elusive bugs.
Error #4: Using non-static fields in a static block. You can’t access instance fields from a static block because they haven’t been initialized yet (and don’t exist at that stage at all).
Error #5: Unexpected initialization order. If you access static fields in a static block that are declared later in the code, they may not be initialized yet. Always declare static fields before static blocks if you plan to use them there.
GO TO FULL VERSION