We’ll often want to initialize, or set the initial value of, some of our fields when creating a new object from a class. Constructors allow us to do so.
In Java, constructors have the same name as the class and are most often declared public (though they can have any other valid access modifier). They are declared without a return type. Any function that is named the same as the class and has no return type is a constructor.
Here is an example of a constructor definition within the HelloWorld
class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class HelloWorld {
private String message = "Hello World";
public HelloWorld(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String aMessage) {
message = aMessage;
}
}
|
This constructor allows us to create HelloWorld
objects with custom
messages. The assignment this.message = message
assigns the value
passed into the constructor to the field message
. Here’s how we
might use it:
HelloWorld goodbye = new HelloWorld("Goodbye World");
System.out.println(goodbye.getMessage()); // prints "Goodbye World"
It’s not required that every class have a constructor. If you don’t
provide one, the Java compiler will generate an empty constructor for
you, known as a default constructor. For example, when we left out a
constructor in our HelloWorld
class above, the compiler created the
following constructor for us:
public HelloWorld() {}
While this can be convenient, you almost always want to provide a constructor to properly initialize your objects.
We can provide multiple constructors for a given class in order to allow for different initialization scenarios. This is known as constructor overloading. When providing multiple constructors, we must ensure that each has a different collection of arguments, as determined by the number, order, and type of the constructor arguments.
Let’s expand upon our Student
class.
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class Student {
private String name;
private final int studentId;
private int numberOfCredits;
private double gpa;
public Student(String name, int studentId,
int numberOfCredits, double gpa) {
this.name = name;
this.studentId = studentId;
this.numberOfCredits = numberOfCredits;
this.gpa = gpa;
}
public Student(String name, int studentId) {
this.name = name;
this.studentId = studentId;
this.numberOfCredits = 0;
this.gpa = 0.0;
}
/* getters and setters omitted */
}
|
The first constructor allows for the creation of Student
objects
where the code creating the object provides initial values for each of
the fields. The second allows for the creation of Student
objects
with only name
and studentId
. The first constructor would be
most useful for creating a transfer student, where credits and a GPA
might already be non-zero. However, for all new students, it would be
safe to initialize numberOfCredits
and gpa
to be 0.
A better way to write the above constructors would be this:
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Student {
private String name;
private final int studentId;
private int numberOfCredits;
private double gpa;
public Student(String name, int studentId,
int numberOfCredits, double gpa) {
this.name = name;
this.studentId = studentId;
this.numberOfCredits = numberOfCredits;
this.gpa = gpa;
}
public Student(String name, int studentId) {
this(name, studentId, 0, 0);
}
/* getters and setters omitted */
}
|
In the example above on line 17, we use this()
to invoke another
constructor within the same class. In this case, the second constructor calls
the first with the default values for numberOfCredits
and gpa
. If you
use this syntax, the call to this()
must be the first line in the
constructor. This is a good practice not only because it makes your code
shorter, but also because it allows any initialization behavior that may
be carried out beyond just initializing variables to be contained in a
smaller number of constructors. In other words, constructors can share
initialization code. Notice from this example that a constructor doesn’t
need to require an initial value for each field as an argument.
When defining constructors, think about:
Question
A constructor is required for every class.
Question
Let’s take a look at a class called Dog
.
1 2 3 4 5 6 7 8 9 | public class Dog {
private String name;
private String breed;
public Dog(String name, String breed) {
this.name = name;
this.breed = breed;
}
}
|
What line of code would be appropriate for us to declare an instance of the Dog
class called myDog
and give it the name, “Bernie”, and the breed, “Beagle”?
Dog myDog = new Dog(Bernie,beagle);
Dog myDog = new Dog("Bernie","beagle");
Dog Bernie = new Dog("Bernie","beagle");