6.2. Writing Constructors¶
In Unit 2, we learned how to create objects by calling constructors. To
review, a call to a constructor consists of the word new
followed by the
name of the class being constructed, and then an argument list in parentheses.
For example, here is how we create World
, Turtle
, and Person
objects.
// To create a new object, write:
// ClassName variableName = new ConstructorName(arguments);
World world = new World();
Turtle t = new Turtle(world);
Person p = new Person("Pat", "pat@gmail.com", "123-456-7890");
Now it’s time to learn to write our own constructors.
6.2.1. Constructor Signature¶
In the source code for a class, constructors are usually written after the instance variables and before any methods.
The signature of a constructor is similar to the signature of a method except
there is no return type, not even void
, and instead of a method name, the
name of the constructor is the same as the name of the class. The constructors
you write will almost always be marked public
. Like methods, constructors
also have a parameter list specified in parenthesis that declare the
variables that will be used to hold the arguments passed when the constructor is
called.
public class ClassName
{
/* Instance variable declarations go here, before constructors */
/* Constructor - same name as Class, no return type */
public ClassName()
{
/* Implementation not shown */
}
/* Method definitions go here, after constructors */
}
Note
Constructors must have the same name as the class! Constructors have no return type!
6.2.2. The Job of a Constructor¶
The job of a constructor is 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.
Classes can have zero or more constructors but they should all produce an object in a valid state.
The easiest way to write a constructor is to not write one. If you do not
write a constructor your class will automatically get what is called the
default no-argument constructor. This constructor will initialize all your
instance variables to the default value for their type: 0 for int
and
double
, false
for boolean
, and null
for all reference types. If
those default values are sufficient to put your object into a valid state you may
not need to write a constructor at all.
Usually, however, if you are writing a class that has instance variables, you need to initialize your instance values to some other values. In that case you probably need to write a constructor that takes arguments and uses them to initialize your instance variables.
For example, consider the constructor from the Person
class from the last
section.
public Person(String name, String email, String phoneNumber)
{
this.name = name;
this.email = email;
this.phoneNumber = phoneNumber;
}
This constructor ensures that all three of the instance variables (name
,
email
, and phoneNumber
) in Person
are initialized to the values
provided by whatever code called the constructor. For example, in the
constructor call new Person("Pat", "pat@gmail.com", "123-456-7890")
, the
argument “Pat” is passed into the parameter variable initName
, which the
constructor then assigns to the instance variable name
.
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. With this
constructor in place, for instance, there’s no way to construct a Person
object without providing the three required String
values.
Sometimes you will want to write more than one constructor so there are different ways of making an instance of your class. One reason to do that is to make it convenient to create instances from different kinds of arguments. This is called overloading and we discussed it in Chapter 2 from the perspective of calling constructors.
For instance, suppose we were writing a program that had another class
AddressBookEntry
which had getters for name, email, and phone number. In
that program it might be useful to write another Person
constructor like
this:
public Person(AddressBookEntry address) {
{
name = address.getName();
email = address.getEmail();
phoneNumber = address.getPhoneNumber();
}
Note that in this constructor we don’t need to use this
to refer to the
instance variables because those names are not shadowed by the parameter
address
.
Sometimes you might still even want to provide a no-argument constructor. If
there’s a valid object that you can create without any arguments, you could
write a no-argument constructor for Person
like:
public Person()
{
name = "Anonymous";
email = "unknown";
phoneNumber = "unknown";
}
It’s up to you to decide if this is actually a useful value to have or if it
would be better to force the users of the Person
class to choose the values
themselves. You shouldn’t, however, reflexively create a no-argument
constructor.
public class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public void setFirst(String first) { this.first = first; } public void setLast(String last) { this.last = last; } }
- Determines the amount of space needed for an object and creates the object
- The object is already created before the constructor is called but the constructor initializes the instance variables.
- Names the new object
- Constructors do not name the object.
- Return to free storage all the memory used by this instance of the class.
- Constructors do not free any memory. In Java the freeing of memory is done when the object is no longer referenced.
- Initialize the instance variables in the object
- 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.
5-2-2: What best describes the purpose of a class’s constructor?
The following class defines a Fraction
with the instance variables
numerator
and denominator
. It uses 2 constructors. Note that the
no-argument constructor sets the default instance variable values to 1 rather
than 0 since a fraction with 0 in the denominator is not valid. Try to guess
what it will print before you run it. Hint! Remember to start with the
main
method! You can also 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.
6.2.3. Advanced AP Topic: Reference parameters¶
When you pass object references as parameters to constructors or methods, those
references refer to the same objects as the references in the caller. If the
objects are immutable, like String
objects it probably doesn’t matter at
all.
On the other hand, if the objects are mutable, meaning their instance variables can change after they are constructed, then storing the passed-in reference in an instance variable in your object can lead to surprising results: if some other code changes the object it will change for you too.
Sometimes that’s exactly what you want. But if you are assuming that the value stored in your instance variable is not going to change, or at least not unless your code changes it, you might want to protect your code from other code unexpectedly changing the value stored in your instance variable by making a copy of the object passed to the constructor and storing the copy in the instance variable instead.
How to make the copy will depend on the class of the object but often you can just construct a new object of the appropriate class using values from the original object as shown below.
public class Person
{
private String name;
private Address addr; // Assumes an Address class is already defined
// constructor: initialize instance variable and call Address constructor to
// make a copy
public Person(String name, Address addr)
{
name = name;
addr = new Address(addr.getStreet(), addr.getCity(), addr.getState());
}
}
An even better way to avoid the confusion caused by objects changing out from
under you is to write classes like Address
to be immutable, with their own
instance variables set in their constructor and never changed.
6.2.4. Programming Challenge : Student Class¶
We encourage you to work in pairs for this challenge to create a Student class with constructors.
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?
Write a Student class below that has your 4 instance variables and write a constructor that has 4 parameters to set all of the instance variables.
Add a print() method that uses System.out.println to print out all the instance variables.
Add a main method that constructs at least 2 Student objects using the constructor with different values and then calls their print() methods.
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.
6.2.5. Design a Class for your Community¶
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 5.1 Community Challenge for the class name and its 3 instance variables that you created. Copy them into the active code exercise below.
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 constructor and then calls their print() methods.
Copy your class with its 3 instance variables from Lesson 5.1 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.
6.2.6. Summary¶
Constructors are used to set the initial state of an object, which includes initial values for all instance variables.
When no constructor is written, Java provides a no-argument default constructor, and the instance variables are set to their default values (
0
forint
,0.0
fordouble
,false
forboolean
andnull
for objects likeString
).Constructor parameters are local variables to the constructor and provide data to initialize instance variables.
6.2.7. AP Practice¶
Cat c = new Cat (“Oliver”, 7);
-
The age 7 is less than 10, so this cat would not be considered a senior cat.
Cat c = new Cat (“Max”, “15”);
-
An integer should be passed in as the second parameter, not a string.
Cat c = new Cat (“Spots”, true);
-
An integer should be passed in as the second parameter, not a boolean.
Cat c = new Cat (“Whiskers”, 10);
-
Correct!
Cat c = new Cat (“Bella”, isSenior);
-
An integer should be passed in as the second parameter and isSenior would be undefined outside of the class.
5-2-7: 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 name, int age)
{
this.name = name;
this.age = age;
this.isSenior = age >= 10;
}
}
Which of the following statements will create a Cat object that represents a cat that is considered a senior cat?
- I only
- Option III can also create a correct Cat instance.
- II only
- Option II will create a cat that is 0 years old with 5 kittens.
- III only
- Option I can also create a correct Cat instance.
- I and III only
- Good job!
- I, II and III
- Option II will create a cat that is 0 years old with 5 kittens.
5-2-8: 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 name, int age, int kittens)
{
this.name = name;
this.age = age;
this.kittens = kittens;
}
public Cat(String name, int age)
{
this.name = name;
this.age = age;
this.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 Cat(String c, boolean h) { c = "black"; h = true; }
-
The constructor should be changing the instance variables, not the local variables.
public Cat(String c, boolean h) { c = "black"; h = "true"; }
-
The constructor should be changing the instance variables, not the local variables.
public Cat(String c, boolean h) { c = color; h = isHungry; }
-
The constructor should be changing the instance variables, not the local variables.
public Cat(String c, boolean h) { color = black; isHungry = true; }
-
The constructor should be using the local variables to set the instance variables.
public Cat(String c, boolean h) { color = c; isHungry = h; }
-
Correct!
5-2-9: Consider the following class definition.
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?
Cat c = new Cat("black", true);