As weβve seen, instance variables define the structure that all instances of a class share. And each object stores its own value for each instance variable in its object data. But how do those instances get created and how are their instance variables initialized?
Thatβs the job of the constructorβto set the initial values for the objectβs instance variables to useful values. But what does βusefulβ mean? Sometimes we describe the values of all an objectβs instance variables at a given time as the objectβs state. And we say an object is in a valid state when all its instance variables have values that let us use the object by invoking its public methods.
So another way to describe the job of a constructor is to set the objectβs instance values so itβs in a valid state and ready to be used. As weβll see, classes can have zero or more constructors but they should all produce an object in a valid state.
For instance, it doesnβt really make sense to call greet on a Person object unless itβs name instance variable has been set. And canVote only works if age has been initialized. Which is why the constructor in the Person class takes arguments that let it initialize both of those variables.
As weβll see, constructors are similar in many ways to methods: they can take arguments and contain code that is run when they are invoked. But unlike a method, a constructor is called exactly once for each object, when the object is created. This is in contrast to methods that can be called whenever we want, for as long as we have a reference to an object.
In the source code for a class, constructors are usually written after the instance variables and before any methods. Which make sense because first we need to see the instance variables to understand the basic structure of a class. But before we can look at methods and see how to use the class we need to see how those variables are initialized. And to understand that, we need to look at the classes constructors.
The simplest way to get a constructor is to not write one at all! If a class does not contain an explicit constructor, Java creates what is called the default constructor automatically. The default constructor takes no arguments and does nothing. But that doesnβt mean the objectβs instance variables are not initialized.
Instance variables can be declared with and without initializers. Hereβs a class that has one of each as well as an instance method to show their values and a main to instantiate an object and call the method.
public class Demo {
private String a;
private String b = "I have a value";
public void showValues() {
System.out.println("a: " + uninitialized);
System.out.println("b: " + initialized);
}
public static void main(String[] args) {
Demo d = new Demo();
d.showValues();
}
}
We can see that both instance variables have a value but one of them is null. Every instance variable in an object has a value from the moment the object is created. To ensure that, if an instance variable declaration doesnβt assign a value, the variable will start out with the approriate zero value for its type, 0, 0.0, false, or null. But otherwise the variable is assigned the value specifed in the declaration. These assignments happen even before the constructor runs so code in the constructor can rely on instance variables being initialized.
One important note: if you do write a constructor, Java will not generate the default constructor for you. This is a good thing because it lets you make sure that instances of your class are always properly initialized. For example, because our Person class contains an explicit constructor, thereβs no way to construct a Person object without providing the required name and age values so there will never be an instance of Person missing those values.
Technically someone could call our Person constructor like new Person(null, 0) but then theyβd kind of get what they deserve. And some Java programmers, especiallly when working in large systems, would write the constructor to check that the arguments themselves are valid and to fail immediately if they are not.
In general it is good style to initialize variables in their declaration if possible. But thatβs not always the case. Often we need information from the code that is constructing the object to know what the values in the object should be. In those cases we actually have to write a constructor.
In SectionΒ 3.1Β Writing methods we learned about method signatures, the combination of the return type, method name, and parameters that identify a method. Constructors also have a signature but it is structured slightly differently.
The main difference is a constructor has no return type, not even void, and instead of a method name, the name of the constructor is always the same as the name of the class. This is because constructors donβt return a value; they initialize a brand new object. Here, for example, is the constructor from our Person class:
public Person(String name, int age) {
this.name = name;
this.age = age;
}
As we can see, there is no return type between public and Person. And Person is the name of the class this constructor came from. After the name comes a list of parameters which work just like parameters in a method: they are local variables that will hold the values of the arguments passed to the constructor. And constructors donβt need a return statement because they donβt return a value.
To invoke a constructor we use the keyword new, just like we did with arrays, but instead of specifying the type and dimensions of an array, we follow new with the class name and an argument list in parentheses that matches a constructor in that class. So to invoke the Person constructor above we would write an expression like:
When we invoke a constructor with new two things happen: first a new object is created and the memory needed to hold all its instance variables is allocated somewhere in the computerβs memory. Then, after the instance variable are initialized either to their default zero value or the value specified in the declaration, the constructor is run with this being the newly created object. The code in the constructor can then finish initializing the object however is appropriate. In the case of the above constructor invocation, the constructor will set the instance variable name to the value "Joe" and set age to 18. The value of a constructor invocation is a reference to the new object just like the value of an array creation expression is a reference to the newly created array.
Constructor signatures are an important part of the API when we use a class from a library someone else has written. In order to use most classes, such as the Turtle class we first used in SectionΒ 3.2Β APIs and Libraries we need to look up how to construct an instance of the class. Usually weβll look at the classβs documentation which will list the signatures of the constructors available. Knowing the parameter list of a constructor gives us the information we need to invoke the constructor. The documentation may also give us more information about how the constructorβs arguments will be used or restrictions. For instance, in a real Person class, we might document that the age argument has to be a positive number.
Constructors can also be private and sometimes thatβs useful to control how instances of a class are created. And there is a level of accessibility inbetween public and private that requires a deeper understanding of Java packages than we want to get into. But both of those options are well outside the AP curriculum. For the purposes of the AP exam classes and their constructors are always public.
Constructors cannot have the static modifier that weβve used with methods until this unit. Thatβs only an option for methods and variables which will discuss more in ChapterΒ 9Β Objects.
Itβs also possible to define more than one constructor in a class. As long as they all have different signatures, meaning the number or the types of their parameters are different, we can have as many constructors as we want. Such constructors are said to be overloaded.
One common reason to write overloaded constructors is when some of the instance variables in the class have reasonable default values. In such cases we can write one constructor that takes arguments for all the instance variables that need to be initialized and then write other constructors that omit certain parameters and instead initialize the unspecified variables to default values.
For instance, in our Person class, suppose we wanted to be able to make objects to represent anonymous people but we wanted all anonymous people to be named the same thing: "Anonymous". Rather than requiring all the code that wants to construct an anonymous Person to pass that specific String to the two-argument constructor weβve already written, we can write a new constructor like this:
However thereβs an even better way to write that constructor using the this keyword, that weβve previously used to access instance variables. In the context of a constructor, we can use this followed by an argument list in parentheses to invoke another constructorβs code to let it initialize the object. So we could write that one-argument constructor like this:
public Person(int age) {
this("Anonymous", age);
}
Then when we evaluate new Person(20) the argument 20 is passed to that constructor which then passes it along to the original constructor with the "Anonymous" as the name argument. In this case it doesnβt simplify things much since both constructors are so simple. But in more complicated classes that do more complicated initialization it lets us write the complicated stuff in just one place. The typical pattern for writing overloaded constructors is to write one main constructor that takes all the arguments that could possibly be passed, and then write other constructors that take fewer arguments and invoke the main constructor using this(), passing default values as needed.
Given the Turtle class in the figure above and a World object world1, which of the following code segments will correctly create an instance of a Turtle object at x, y coordinates 50, 150?
Another reason to overload constructors is to allow callers to construct objects with arguments of a type different than what the class really needs. As a trivial example, consider class that stores an ammount in an int variable. The obvious constructor would look like:
But maybe sometimes itβd be convenient to construct an amount from a String containing a numeric value. In that case we could write another constructor:
public Amount(String value) {
this(Integer.parseInt(value));
}
Activity8.3.2.
If you see a class definition on the AP exam, like the one below for a class called Date, you should be able to pick out the attributes (instance variables) and the constructors and know how to use them.
Constructors are public and have the same name as the class. Click on the constructor headers which are the first line of the constructors showing their name and parameters.
public class Date {private int year;private int month;private int day;public Date(){ /** Implementation not shown */ }public Date(int year, int month, int day){ /** Implementation not shown */ }public void print(){ /** Implementation not shown */ }}
Given the Date class in the previous problem and assuming that months in the Date class are numbered starting at 1, which of the following code segments will create a Date object for the date September 20, 2020 using the correct constructor?
A constructor initializes the instance variables to their default values or in the case of a parameterized constructor, to the values passed in to the constructor.
The following class defines a Fraction with the instance variables numerator and denominator. Complete the body of its 2 constructors. The no-argument constructor should set the default instance variable values to 1 rather than 0 since a fraction with 0 in the denominator is not valid. The constructor with parameters should copy those parameters into the instance variables. What will the code print out? Try tracing through it. View it in the Java visualizer by clicking on the Show CodeLens button below.
The following class defines a Car with the instance variables model and year, for example a Honda 2010 car. However, some of the code is missing. First, fill in the code to create a Car constructor. Then, fill in the code to call the constructor in the main method to create 2 Car objects. The first car should be a 2023 Ford and the second car should be a 2010 Honda. Run your program and make sure it works and prints out the information for both cars.
First, brainstorm in pairs to do the Object-Oriented Design for a Student class. What data should we store about Students? Come up with at least 4 different instance variables. What are the data types for the instance variables?
Create a class Student with 4 instance variables, a constructor, and a print method. Write a main method that creates 2 Student objects with the constructor and calls their print() method.
In the last lesson, you came up with a class of your own choice relevant to you or your community. In this lesson, you will add a constructor to this class.
Consult your completed worksheet or your code in lesson 3.3 Community Challenge for the class name and its 3 instance variables that you created. Copy them into the active code exercise below.
Copy your class with its 3 instance variables from lesson 3.3 Community Challenge. Add a constructor with 3 parameters to set all of the instance variables to the given parameters. Write a print() method that uses System.out.println to print out all the instance variables. Write a main method that constructs at least 2 objects of your class using the constructors and then calls their print() methods.
(AP 1.13.C.1) An object is typically created using the keyword new followed by a call to one of the classβs constructors. new ClassName() creates a new object of the specified class and calls a constructor.
(AP 1.13.A.2) A constructor signature consists of the constructorβs name, which is the same as the class name, and the ordered list of parameter types.
(AP 1.13.A.3) Constructors are said to be overloaded when there are multiple constructors with different signatures. They must differ in the number, type, or order of parameters.
(AP 1.13.C.3) A constructor argument is a value that is passed into a constructor when the constructor is called. The arguments passed to a constructor must be compatible in order and number with the types identified in the parameter list in the constructor signature.
(AP 1.13.C.3) When calling constructors, arguments are passed using call by value. Call by value initializes the parameters with copies of the arguments.
(AP 1.13.C.4) A constructor call interrupts the sequential execution of statements, causing the program to first execute the statements in the constructor before continuing. Once the last statement in the constructor has been executed, the flow of control is returned to the point immediately following where the constructor was called.
(AP 3.4.A.1) An objectβs state refers to its attributes and their values at a given time and is defined by instance variables belonging to the object. This defines a has-a relationship between the object and its instance variables.
(AP 3.4.A.2) A constructor is used to set the initial state of an object, which should include initial values for all instance variables. When a constructor is called, memory is allocated for the object and the associated object reference is returned. Constructor parameters, if specified, provide data to initialize instance variables.
(AP 3.4.A.4) When no constructor is written, Java provides a no-parameter constructor, and the instance variables are set to default values according to the data type of the attribute. This constructor is called the default constructor. (Note that AP 3.4.A.3 is covered in lesson 3.6).
public class Cat
{
private String color;
private String breed;
private boolean isHungry;
public Cat()
{
color = "unknown";
breed = "unknown";
isHungry = false;
}
public Cat(String c, String b, boolean h)
{
color = c;
breed = b;
isHungry = h;
}
}
I. Cat a = new Cat();
II. Cat b = new Cat("Shorthair", true);
III. String color = "orange";
boolean hungry = false;
Cat c = new Cat(color, "Tabby", hungry);
Consider the definition of the Cat class below. The class uses the instance variable isSenior to indicate whether a cat is old enough to be considered a senior cat or not.
public class Cat {
private String name;
private int age;
private boolean isSenior;
public Cat(String n, int a) {
name = n;
age = a;
if (age >= 10) {
isSenior = true;
} else {
isSenior = false;
}
}
}
Which of the following statements will create a Cat object that represents a cat that is considered a senior cat?
Consider the following class definition. Each object of the class Cat will store the catβs name as name, the catβs age as age, and the number of kittens the cat has as kittens. Which of the following code segments, found in a class other than Cat, can be used to create a cat that is 5 years old with no kittens?
public class Cat
{
private String name;
private int age;
private int kittens;
public Cat(String n, int a, int k)
{
name = n;
age = a;
kittens = k;
}
public Cat(String n, int a)
{
name = n;
age = a;
kittens = 0;
}
/* Other methods not shown */
}
I. Cat c = new Cat("Sprinkles", 5, 0);
II. Cat c = new Cat("Lucy", 0, 5);
III. Cat c = new Cat("Luna", 5);
public class Cat {
private String color;
private boolean isHungry;
/* missing constructor */
}
The following statement appears in a method in a class other than Cat. It is intended to create a new Cat object c with its attributes set to βblackβ and true. Which of the following can be used to replace missing constructor code in the class definition so that the object c below is correctly created?