Lesson 13Phase 2: Objects
Constructors
Building objects in a valid state from the first moment, default vs custom constructors, and overloading them.
By BasithUpdated 55 min
- constructor
- default constructor
- constructor overloading
- new flow
- this()
- initialization
You can build an object and you can give it behavior. But every object so far has started out empty and been filled in by hand, one field at a time, and nothing reminds you if you skip one. This lesson is about the part of Java that fixes that: a constructor, a piece of setup code that runs while the object is being created, so the object is complete before your code ever gets hold of it.
- Say what a constructor is and when it runs.
- Write a constructor that takes values in and stores them in the object's fields.
- Explain
this.name = name: whichnameis which, and why thethis.is needed. - Describe what
newdoes, step by step, including where a field's starting value comes from. - Predict when the free no-argument constructor exists and when it disappears.
- Write several constructors for one class and chain them together with
this(...).
new (creating classes and objects), and you give them methods that take parameters and return values. A constructor looks a lot like a method, so keep parameters fresh in mind. You also saw that a method runs on one particular object, the one before the dot. This lesson gives that object a name in your code: this. Inheritance comes later, so every example here has a single class.The problem: an object born half-finished#
Here is a Student class with three fields. We make one student and set it up by hand, the only way we know so far.
public class Main { public static void main(String[] args) { Student s = new Student(); s.name = "Mara"; s.age = 19; System.out.println(s.name + ", age " + s.age + ", gpa " + s.gpa); }} class Student { String name; int age; double gpa;}It prints Mara, age 19, gpa 0.0. The gpa is 0.0 because we forgot to set it, and Java did not stop us. As you saw when you first made objects, every field of a new object starts at a default value: 0 for an int, 0.0 for a double, false for a boolean, and null for an object type such as String. The object exists, but it is half-finished, and nothing told us.
Now scale this up. Three fields are easy to remember. A real Student might have a name, an age, a gpa, a student number, an email and a major. Every time anyone, anywhere in the program, makes a student, they must remember to set all six, with the right types, and never miss one. The first time someone forgets, a broken student travels through your program looking completely normal.
Key ideaSetting fields one by one after
newdepends on every programmer remembering every field, every time, and Java never checks that they did.
A constructor: setup that runs during new#
The fix is to attach the setup to the class itself, so it cannot be skipped. That piece of setup is called a constructor. It is a special block of code that Java runs automatically as part of new, before the new object is handed back to you. Here is the same Student, now with a constructor.
public class Main { public static void main(String[] args) { Student s = new Student("Mara", 19); System.out.println(s.name + " is " + s.age); }} class Student { String name; int age; Student(String name, int age) { this.name = name; this.age = age; }}Press Run and step through, keeping an eye on the memory view. The first step creates the Student object, with name still null and age still 0. The next step starts the constructor with the arguments "Mara" and 19. Then come two steps that set this object's name field to "Mara" and its age field to 19, and only after that is s declared and pointed at the finished object. There was never a moment when s pointed at a student without a name. The program prints Mara is 19.
What makes it a constructor and not a method
A constructor looks almost like a method, with two deliberate differences. Spot them in the code above.
- Its name is exactly the class name. The constructor for
Studentis namedStudent, with the same capital letter. This is how Java recognizes it. - It has no return type, not even
void. A method such asvoid greet()has a return type in front of its name; a constructor has nothing there. It does not hand a value back. It sets up the object.
greet.Student.void, int, String.void.new.Lamp?__init__ or the constructor method of a JavaScript class. In Java the constructor is named after the class, the fields are declared in the class body rather than created by assignment, and there is no self parameter to write. The object being built is always available as this, which the next section explains.this.name = name: reading the most confusing line#
The line this.name = name stops a lot of beginners, because the word name appears twice and both look the same. They are two different things that happen to share a name.
Inside that constructor, one name is the parameter, the value "Mara" that was just passed in. The other is the field, the slot inside the object where the student's name is meant to live. When two things in the same place share a name, the closer one wins. The parameter is closer, so a bare name means the parameter. The field is hidden, or shadowed, by it.
Key idea
this.namemeans “thenamefield of this object”. Plainnamemeans “the parameter that was passed in”. Sothis.name = namereads: take the value that was passed in, and store it in this object's own field.
The word this is how an object refers to itself from the inside. While the constructor runs for the student being created, this points at exactly that student. So this.name reaches into that one object and names its name field, the same way s.name reaches in from the outside.
Yes, and people do. If you called the parameter n instead of name, there would be no clash, and name = n would already mean the field. But naming the parameter after the field it fills is so common, and reads so clearly, that the this.field = field pattern is everywhere in real Java. Learning to read it now saves you confusion in every codebase you open.
The flow of new, in three steps#
With a constructor in the picture, it is worth being exact about what new Student("Mara", 19) does, in order. It is always the same three steps.
- 1Java makes the object. It sets aside memory for one new
Studentand gives every field its default:nameisnull,ageis0. The object exists now, but it is blank. - 2Java runs the matching constructor on that blank object, passing your arguments in. This is where
this.name = nameandthis.age = agerun and turn the blank object into a real student. - 3Only now does
newfinish and hand you a reference to the finished object, which the assignment stores ins.
You can watch this order for yourself by printing from inside the constructor. The line printed between the two outer prints can only have come from step 2, in the middle of new.
public class Main { public static void main(String[] args) { System.out.println("before new"); Student s = new Student("Mara", 19); System.out.println("after new: " + s.name); }} class Student { String name; int age; Student(String name, int age) { System.out.println("constructor running"); this.name = name; this.age = age; }}The output is before new, then constructor running, then after new: Mara. The middle line shows the constructor ran during new, before s was ready. By the time you hold s, the setup is done.
before new, then constructor running for Dune, then after new: Dune.Tap a line, then tap where it should go. Or use the arrows.
Fields with a starting value#
A field can also get its starting value right where it is declared. In a game, every new player starts with three lives, so the class can say that once: int lives = 3;. This is called a field initializer. Java stores those values during step 2, just before the constructor's own lines run, so the constructor already sees them.
public class Main { public static void main(String[] args) { Player p = new Player("Ada"); System.out.println(p.name + " has " + p.lives + " lives and " + p.score + " points"); }} class Player { String name; int lives = 3; int score; Player(String name) { System.out.println("constructor sees lives = " + lives); this.name = name; }}Step through it and watch lives in the memory view. It is 0 when the object appears, like any new field, and 3 by the time the constructor's first line prints. The output is constructor sees lives = 3 and then Ada has 3 lives and 0 points. score has no initializer, so it keeps its default of 0.
The free constructor that disappears#
Go back to the very first example. It had no constructor at all, yet new Student() worked. When you write a class with no constructor, Java quietly gives it one: an empty no-argument constructor that takes nothing and does nothing beyond the defaults. It is called the default constructor, and it is why new Student() compiled in the first example.
Here is the rule that surprises people. The moment you write any constructor of your own, the free one is gone. Java only supplies it when you have written none. So once Student has Student(String name, int age), the empty new Student() no longer exists, and using it is a compile error:
public class Main { public static void main(String[] args) { Student s = new Student(); // no longer allowed System.out.println(s.name); }} class Student { String name; int age; // Once this exists, the free no-argument constructor is gone. Student(String name, int age) { this.name = name; this.age = age; }}Main.java:3: error: constructor Student in class Student cannot be applied to given types;
Student s = new Student(); // no longer allowed
^
required: String,int
found: no arguments
reason: actual and formal argument lists differ in length
1 errorRead the message from the top. Main.java:3 points at the call on line 3. required: String,int lists what the class's only constructor takes, and found: no arguments is what the call passed.
This trips up almost everyone once. You add a constructor with parameters, and old code that said new Student() stops compiling. Nothing was deleted; the free no-argument constructor only existed because you had written none. If you still want new Student() to work, write the no-argument constructor yourself, next to the others.
new Point()?Many constructors for one class#
Sometimes there is more than one sensible way to make an object. You might know everything about a new student, or only their name, with the rest taking sensible starting values. Java lets you write several constructors for the same class, as long as their parameter lists differ. This is constructor overloading, the same idea as overloading a method, applied to the class name.
public class Main { public static void main(String[] args) { Student a = new Student("Mara", 19, 3.8); Student b = new Student("Tom", 20); Student c = new Student("Lin"); System.out.println(a.name + " " + a.age + " gpa " + a.gpa); System.out.println(b.name + " " + b.age + " gpa " + b.gpa); System.out.println(c.name + " " + c.age + " gpa " + c.gpa); }} class Student { String name; int age; double gpa; Student(String name, int age, double gpa) { this.name = name; this.age = age; this.gpa = gpa; } Student(String name, int age) { this.name = name; this.age = age; this.gpa = 0.0; } Student(String name) { this.name = name; this.age = 18; this.gpa = 0.0; }}Three constructors, all named Student, told apart by how many values they take. When you write new Student("Tom", 20), Java looks for the constructor that fits two arguments and runs that one. The output is Mara 19 gpa 3.8, then Tom 20 gpa 0.0, then Lin 18 gpa 0.0. Each student is fully set up, even the ones given less information, because each constructor fills in its own sensible defaults.
It matches on the arguments you give: how many there are, and their types. new Student("Lin") passes one String, so the one-parameter constructor runs. If no constructor matches the arguments you wrote, the program does not compile. The set of constructors is the menu of allowed ways to make a Student.
Repeating yourself, and a cleaner way to write it
Look closely at those three constructors. The first sets all three fields. The second copies two of those lines and adds a default for gpa. The third copies one line and adds defaults for the other two. The setup is written out three times, and if you ever add a fourth field, you must remember to update all three constructors. That is the forgetting problem we started with, moved inside the class.
Java has a fix: one constructor can hand the job to another with this(...). Read this(name, 18, 0.0)as “run the constructor of this same class that matches these arguments”. The full setup lives in one place, and the shorter constructors call it with their defaults filled in.
public class Main { public static void main(String[] args) { Student a = new Student("Mara", 19, 3.8); Student b = new Student("Tom", 20); Student c = new Student("Lin"); System.out.println(a.name + " " + a.age + " gpa " + a.gpa); System.out.println(b.name + " " + b.age + " gpa " + b.gpa); System.out.println(c.name + " " + c.age + " gpa " + c.gpa); }} class Student { String name; int age; double gpa; // The one real constructor: it sets everything. Student(String name, int age, double gpa) { this.name = name; this.age = age; this.gpa = gpa; } // Hands over to the full one, supplying a default gpa. Student(String name, int age) { this(name, age, 0.0); } // Hands over again, supplying a default age and gpa. Student(String name) { this(name, 18, 0.0); }}The output is the same three lines as before. Step through new Student("Lin") and read the narration: Java first runs the constructor with "Lin", and that one immediately runs the constructor with "Lin", 18, 0.0. The steps that set the three fields all come from that full constructor. One new, two constructors, and the fields are assigned in one place only.
Key ideaWrite the real setup once, in the constructor with the most parameters, and let the shorter constructors hand over to it with
this(...), filling in defaults. Then a new field means one change in one place.
Each constructor in a chain can do some work of its own after the hand-over. That raises a question about order. Decide before you run this one.
public class Main { public static void main(String[] args) { Coffee order = new Coffee(); System.out.println(order.size + ", " + order.sugars + " sugars"); }} class Coffee { String size; int sugars; Coffee(String size, int sugars) { System.out.println("full constructor"); this.size = size; this.sugars = sugars; } Coffee(String size) { this(size, 0); System.out.println("size-only constructor"); } Coffee() { this("medium"); System.out.println("no-argument constructor"); }}new Coffee() sets off a chain of three constructors, and each one prints a line. What does the program print?
All three constructors print, and the full constructor prints first. new Coffee() does start in Coffee(), but its first line, this("medium"), hands over before anything is printed. That constructor hands over again with this(size, 0). So the full constructor is the first to reach a println, and each of the others prints only after the constructor it called has finished, just as a method call finishes before the line after it runs.
Book constructors. The first stores all three values in the fields. The second takes only a title and a page count, and hands over to the first with this(...), passing true for inStock.A constructor makes sure an object starts out complete. It does not yet stop someone writing s.gpa = -5.0 a moment later and spoiling it. Making sure an object can only ever hold sensible values is the job of encapsulation, the next lesson. The keyword this has more to it as well, including how two variables can point at the same object; that is the this keyword and object references. And once classes build on other classes in inheritance, constructors gain one more move, super(...).
Try it
Answer each one in your head or on paper first, then open the answer.
this.age = age inside a constructor, what does each age refer to, and which value ends up stored where?new Thing() allowed? Now you add Thing(int x) and write nothing else. Is new Thing() still allowed?Student. Which constructor runs for new Student("Lin"), and what are age and gpa afterwards?this(name, 18, 0.0) and one that writes the three assignments itself? Does the finished object differ?int lives = 3; and its constructor ends with lives = 5;. How many lives does a new object have, and why?Practice
Work through these on your own. Each one builds on this page.
Pet class with fields name (String), species (String) and vaccinated (boolean). Give it one constructor that takes all three and stores them with the this.field = field pattern. In main, make two different pets and print all three fields of each.Pet that takes only name and species and hands over to the first with this(...), setting vaccinated to false. Make a pet with each constructor and confirm both come out fully set up. Then try new Pet() with no arguments and explain in one sentence why it does not compile.name = name instead of this.name = name. Predict what the name field holds after construction, then run it and see. Write one sentence explaining the result.Timer class with fields minutes and seconds and three constructors: Timer(int minutes, int seconds), Timer(int minutes) with 0 seconds, and Timer() for a one-minute timer. Chain them with this(...) so the fields are assigned in one constructor only. Put a println at the end of each constructor, and write down the order of the printed lines for new Timer() before you run it.Further reading#
- Providing Constructors for your Classes, the dev.java tutorial on writing constructors and the default constructor
- Constructor declarations in the Java Language Specification, section 8.8, including this(...) and the default constructor
- JEP 513: Flexible Constructor Bodies, the Java 25 change that lets a few lines come before this(...) or super(...)
Short guides on this topic
Quick answers to what people search for about it, with code to run.
Spotted a mistake, or a part that did not make sense? Report it by email