الدوال البانية (Constructors)
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
In the last lesson you built an Account with new Account() and then set each field by hand. That works, but nothing stops you from forgetting to set the number, and then you have an account with null in it. A real account should never exist without an owner.
A constructor solves this. It is a block of code that runs at the moment you use new, so the object is born with the values it needs. In the blueprint picture, the constructor is the building crew's checklist: no house is handed over until every item is ticked.
Why this matters: constructors are how every framework-managed object and every domain object in a backend starts its life. A class whose objects can be created half-empty is a source of null bugs for the entire program.
This lesson builds on Lesson 1 (classes and fields) and on method overloading from the methods module.
Learning Objectives
By the end of this lesson you will be able to:
- Write a constructor and recognise its shape: the class name, parameters, no return type.
- Explain what runs when you use
newand in which order things happen. - Explain the free no-argument constructor, and when it disappears.
- Write several overloaded constructors for one class.
- Use
this(...)to let one constructor reuse another. - Describe what a constructor should and should not do.
Written Explanation
The shape of a constructor
A constructor looks like a method, with two differences: its name is exactly the class name, and it has no return type, not even void.
public class Account {
String owner;
String number;
double balance;
Account(String owner, String number, double balance) {
this.owner = owner;
this.number = number;
this.balance = balance;
}
}You do not call a constructor by name. It runs automatically as part of new:
Account a = new Account("Ana", "A-1", 120.5);What happens here, step by step: Java creates a new object with all fields at their defaults (the table from Lesson 1), then runs the constructor body with the arguments you passed, and finally new gives you a reference to the finished object. By the time you can use a, the three fields are set.
A short word about this.owner = owner;. Inside the constructor, owner on the right is the parameter, and this.owner on the left is the field of the object being built. this means "the object I am working on". The next lesson explains this fully, including what goes wrong without it. For now, copy the pattern: this.field = parameter;.
The free no-argument constructor, and when it vanishes
In Lesson 1 you wrote new Account() and it compiled even though the class had no constructor. That is because, as the Java tutorial states, the compiler provides a no-argument default constructor for any class that declares no constructors.
The moment you write any constructor yourself, the free one is gone. With the three-argument constructor above in place, this fails:
Account c = new Account();Bad.java:3: error: no suitable constructor found for Account(no arguments)
Account c = new Account();
^
constructor Account.Account(String,String,double) is not applicable
(actual and formal argument lists differ in length)
1 errorThis is a feature, not a nuisance. By writing a constructor that requires an owner, you have said "an Account must have an owner", and the compiler now enforces it everywhere. If you still want a no-argument form, write one explicitly.
Overloading constructors
A class may have several constructors, as long as their parameter lists differ in number or types. This is the same overloading you used for methods. Java picks the one that matches the arguments:
Account(String owner, String number, double balance) { ... }
Account(String owner, String number) { ... } // new account, no money yetnew Account("Ana", "A-1", 120.5) uses the first; new Account("Omar", "A-2") uses the second.
Reusing code with this(...)
The second constructor must also set all three fields. Copying the three assignment lines would repeat code, and if the rules ever change, you would have to fix two places. Instead, one constructor can call another in the same class with this(...):
Account(String owner, String number) {
this(owner, number, 0.0);
}"Set the balance to 0.0, and let the full constructor do the work." The call this(...) has to be the first statement in the constructor. The compiler rejects it elsewhere (JDK 21):
Late.java:6: error: call to this must be first statement in constructor
this(n, 1);
^A good habit: one constructor, usually the one with the most parameters, does the real work; all other constructors forward to it with this(...).
What a constructor should do
A constructor's job is to put the object into a valid starting state. That means assigning fields, and perhaps a quick check of the arguments. It should not do heavy work: reading files, calling a web service, or talking to a database. If that work is slow or fails, new fails with it, and the failure shows up in an unexpected place. Keep constructors short and predictable, and do the heavy work in separate methods that you call deliberately.
Two traps. First, adding a return type turns a constructor into an ordinary method:
void Account(String owner) { ... } // NOT a constructor: a method that happens to be called AccountIt compiles, which makes it hard to spot. Second, a constructor cannot be called like a method (a.Account(...) is an error). It only runs through new.
Examples
Example 1: a full constructor and a short one (simple)
public class Account {
String owner;
String number;
double balance;
Account(String owner, String number, double balance) {
this.owner = owner;
this.number = number;
this.balance = balance;
}
Account(String owner, String number) {
this(owner, number, 0.0);
}
}public class Main {
public static void main(String[] args) {
Account a = new Account("Ana", "A-1", 120.5);
Account b = new Account("Omar", "A-2");
System.out.println(a.owner + " " + a.balance);
System.out.println(b.owner + " " + b.balance);
}
}Output (JDK 21):
Ana 120.5
Omar 0.0Both accounts have an owner and a number from the moment they exist. Omar's balance comes from the 0.0 that the short constructor passed along.
Example 2: a product with a default stock (practical)
A shop lets staff create a product with or without an initial stock:
public class Product {
String name;
double price;
int stock;
Product(String name, double price, int stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
Product(String name, double price) {
this(name, price, 0);
}
}Product a = new Product("Tea", 2.5, 40);
Product b = new Product("Mug", 6);
System.out.println(a.name + " " + a.stock + " / " + b.name + " " + b.stock);Output: Tea 40 / Mug 0. Notice 6 is an int and the second parameter is a double. Java widens it automatically, as in earlier modules, so the call matches the two-parameter constructor.
Example 3: a constructor that checks its input (realistic)
Real objects must not start in an impossible state. A constructor can refuse bad arguments by throwing an exception. This uses if and throw, so treat it as a preview of exceptions, which a later module teaches in full:
Account(String owner, String number, double balance) {
if (owner == null || owner.isBlank()) {
throw new IllegalArgumentException("owner is required");
}
this.owner = owner;
this.number = number;
this.balance = balance;
}If someone writes new Account("", "A-1", 0), the program stops with an error message at the exact place where the bad value came in, rather than carrying a nameless account through the whole program. A quick check like this is acceptable in a constructor; calling a database from it would not be.
Backend Insight
Backend objects are created in huge numbers, and often by code you did not write. A web framework builds a new object for each request; a database tool builds one for each row it reads. They rely on the constructors you provide, so the rules you put there become the rules for every object.
Two practical consequences. First, a constructor that does heavy work makes object creation slow and hard to test, because you cannot create the object without also triggering the file or network call. Second, required data belongs in constructor parameters: "an Order has a customer" is enforced by the compiler if the only constructor asks for one. Later modules will show how frameworks use constructors to hand objects the things they need (dependency injection), but the principle starts with what you wrote today.
Common Mistakes
Mistake 1: Giving the constructor a return type
- Incorrect:
void Account(String owner) { ... }, which is a method, not a constructor. - Correct:
Account(String owner) { ... }.
If new Account("Ana") complains that no constructor exists even though you wrote one, check for a stray void.
Mistake 2: Expecting the free no-argument constructor after writing your own
Once any constructor exists, new Account() stops compiling, as shown above. Either pass arguments, or write a no-argument constructor on purpose.
Mistake 3: Putting this(...) after other statements
It must be the first statement. Put any checks after the call, or do them in the constructor that does the real work.
Mistake 4: Doing heavy work in the constructor
Opening a file or connecting to a server in a constructor makes new slow and fragile. Keep constructors to assigning fields and quick checks.
Mistake 5: Repeating the assignments in every constructor
If three constructors each copy the same four lines, a fix must be made three times, and someone will forget one. Forward with this(...) instead.
Exercises
Try each exercise yourself before opening the solutions. The exercises continue with Product from the last lesson.
Level A: Foundation
A1. Which of these is a constructor of Product, and why? (a) void Product(String n) { } (b) Product(String n) { } (c) product(String n) { }
أظهر الحل A1
A1. Only (b). A constructor has the class name (with the same capital letter) and no return type. (a) is a method called Product because of the void; (c) is a method too, since the name does not match the class.
A2. Product has only one constructor, Product(String name, double price, int stock). Does new Product("Tea", 2.5) compile? Does new Product()?
أظهر الحل A2
A2. new Product("Tea", 2.5) does not compile, since the only constructor needs three arguments. new Product() does not either: the free no-argument constructor disappeared when the class got its own constructor.
A3. What does this(name, price, 0); do when it appears in a constructor?
أظهر الحل A3
A3. It calls another constructor of the same class (the one that takes a String, a double, and an int), passing the current name and price and a stock of 0. It must be the first statement of the constructor.
Level B: Application
B1. Write a Product class with fields name, price, stock, a constructor with all three, and an overloaded one with only name and price that sets stock to 0 by calling the first. Create two products in main and print their stock.
أظهر الحل B1
B1.
public class Product {
String name;
double price;
int stock;
Product(String name, double price, int stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
Product(String name, double price) {
this(name, price, 0);
}
}Product a = new Product("Tea", 2.5, 40);
Product b = new Product("Mug", 6);
System.out.println(a.stock + " " + b.stock); // 40 0B2. The following constructor looks right but leaves the field unset. Run it mentally and say what new Fix("x").name prints, then fix it.
public class Fix {
String name;
Fix(String name) { name = name; }
}أظهر الحل B2
B2. It prints null. Inside the constructor, name is the parameter, so name = name; assigns the parameter to itself and never touches the field, which keeps its default null. (We ran this on JDK 21 and it printed null.) The fix is this.name = name;.
B3. Rewrite this class so that the second constructor does not repeat the first one's work:
public class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
Point() { this.x = 0; this.y = 0; }
}أظهر الحل B3
B3.
public class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
Point() { this(0, 0); }
}Level C: Challenge
C1. A Member for a library needs a name and a membership number (both required) and an optional age that defaults to 18. Write the class with two constructors so that the required data cannot be left out. Show a new call for each.
أظهر الحل C1
C1.
public class Member {
String name;
String number;
int age;
Member(String name, String number, int age) {
this.name = name;
this.number = number;
this.age = age;
}
Member(String name, String number) {
this(name, number, 18);
}
}new Member("Lina", "M-7") gets age 18; new Member("Karim", "M-8", 34) sets it. There is no way to create a member without name and number, because no constructor allows it.
C2. Explain why a constructor that downloads a file from the internet is a bad idea. Give two problems it could cause.
أظهر الحل C2
C2. First, new becomes slow, and it fails if there is no network, so creating a simple object can crash a program for reasons unrelated to the object. Second, tests cannot create the object without actually downloading, so they are slow and unreliable. The download should be a separate method that a caller invokes on purpose.
Mini Challenge
Take the class you wrote for the Mini Challenge in Lesson 1, or pick a new one. Add:
- A constructor that takes every field, written with
this.field = parameter;. - A second constructor that leaves out at least one field and fills it from a sensible default using
this(...). - A
mainthat creates one object through each constructor and prints all fields.
Then deliberately break it: move the this(...) call below a print statement and read the compiler message, then call new with no arguments and read that error too. Put each change back.
Check your work: no assignment lines are duplicated between the constructors, and every object printed has no unexpected null.
Summary & Next Lesson
Key takeaways
- A constructor has the class name and no return type, and runs automatically when you use
new. - Java provides a no-argument constructor only if you write no constructor at all; once you write one, the free one is gone.
- Overloaded constructors have different parameter lists. Forward from the shorter ones to the fullest with
this(...), which must be the first statement. - Constructors should set fields and make quick checks, never do heavy work.
- Required data belongs in constructor parameters.
Next lesson
You used this.owner = owner; as a pattern. The next lesson, "Instance methods and this", explains what this really is, writes methods that work on an object's own data, and shows the exact bug that happens when this is forgotten.
Self-check
- What two things distinguish a constructor from a method?
- When does a class stop having a free no-argument constructor?
- What does overloading mean for constructors?
- Where must a
this(...)call be in a constructor? - Name one thing a constructor should not do.
أظهر الإجابات
- Its name is the class name, and it has no return type.
- As soon as you write any constructor yourself.
- Several constructors in one class with different parameter lists; Java chooses by the arguments.
- It must be the first statement.
- Heavy work, such as reading a file or calling a network service or database.
References
- Oracle, The Java Tutorials, Providing Constructors for Your Classes. Supports: a constructor uses the class name and has no return type; the compiler provides a no-argument constructor only for classes without constructors; constructors can be overloaded.
- Oracle, The Java Tutorials, Using the this Keyword. Supports:
thisrefers to the current object, a parameter can shadow a field, andthis(...)must be the first line of a constructor. - The compiler messages, the outputs, and the fact that the
name = name;bug printsnullcome from compiling and running the code on JDK 21. The check in Example 3 was also run and stops withIllegalArgumentException: owner is required; no reference page was consulted for exceptions.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.