Constructors vs Initializers in Java
Constructors
Constructors are a block of code similar to a method that is called when creating a new instance of a Class. Constructors are used to setup and initialize the instance variables in the class. Classes can have multiple constructors. If a constructor isn't provided in the class, the Java compiler will generate a default no-args constructor for you. An example of a class with two constructors is the following:
public class User {
private String id;
private String name;
// Constructor
public User() {
System.out.println("User() called");
}
// Constructor
public User(final String id, final String name) {
System.out.println("User(String, String) called");
// ...
}
}
The following shows what would be printed to the console using both these constructors:
// Prints:
// User() called
final var user = new User();
// Prints:
// User(String, String) called
final var user = new User("id", "name");
Instance Initializers
Instance initializers are unnamed blocks of code. They also execute when an instance of a class is created, similar to a constructor. You can have any number of instance initializers in a class. Instance initializers can be used to initialize instance variables just like constructors can. Each instance initializer will be called regardless of what constructor is used. Instance initializers execute after the super() call and before the constructor. An instance initializer block is written with the following:
public class User {
private String id;
private String name;
// Instance initializer
{
System.out.println("Initializer called");
}
public User() {
System.out.println("User() called");
}
public User(final String id, final String name) {
System.out.println("User(String, String) called");
// ...
}
When creating an instance with these two constructors, the following will be printed to the console:
// Prints:
// Initializer called
// User() called
final var user1 = new User();
// Prints:
// Initializer called
// User(String, String) called
final var user2 = new User("id", "name);
The Java compiler will place the code in each of the instance initializer blocks inside each constructor at compile time. The compiled code will look something like the following:
public class User {
private String id;
private String name;
public User() {
System.out.println("Initializer called");
System.out.println("User() called");
}
public User(final String id, final String name) {
System.out.println("Initializer called");
System.out.println("User(String, String) called");
// ...
}
Static Initializers
Static initializer blocks are similar to instance initializer blocks but are used to initialize class variables or constants. They execute when the class is first loaded. You can have multiple static initializer blocks. They are generally used when a class variable or constant needs more than one line of code to assign the class variable or constant a value. An example of a static initializer is the following:
public class User {
public static final LocalDate APP_START_UP_DATE;
public static final LocalTime APP_START_UP_TIME;
// Static initializer
static {
final var now = LocalDateTime.now();
APP_START_UP_DATE = now.toLocalDate();
APP_START_UP_TIME = now.toLocalTime();
}
}
When to Use Constructors vs Initializers
Instance initializers are rarely used because there are better ways to do things than to use instance initializers. If you need to initialize instance variables in a class, you do so in the constructor. If you have multiple constructors, you create a constructor to do all of the work that all the other constructors call.
public class User {
private String id;
private String name;
public User() {
this(null, null);
}
// Main constructor
public User(final String id, final String name) {
this.id = id;
this.name = name;
}
}
One use case for instance initializers is anonymous classes. Anonymous classes cannot have constructors, so instance initializers make sense to use in anonymous classes. Outside of that, instance initializers don't really make sense to use anywhere else. This doesn't apply to static initializers.
Conclusion
Use constructors instead of instance initializers to initialize instance variables. If you have multiple constructors, have a main constructor that all constructors call that initializes the instance variables. Use instance initializers in anonymous classes only. Use static initializers to assign values to class variables and constants when they require more than one line of code to do so.