Java Constructors and the this Keyword
Give each object a deliberate starting state. Learn how constructors accept values, how this distinguishes fields from parameters, and how shorter creation forms can share one validated initialization path.
Before you start: review objects and references, instance fields and methods, and overloaded parameter lists. All examples are complete, editable Java 21 programs. Use Run code; no standard input is needed.
What you will be able to do
- Distinguish a constructor from an ordinary method.
- Pass starting values through a new expression.
- Use this.field to distinguish a field from a same-named parameter.
- Explain default constructors versus explicitly written no-argument constructors.
- Delegate constructor overloads with this(...) using Java 21 rules.
- Validate creation inputs and test successful and rejected initialization.
Move required setup into the creation step
In the previous lesson, main created an empty stock item and assigned its name afterward. That split leaves a moment when the object exists but the caller has not supplied its intended values. If setup is forgotten or spread across several callers, different items can begin in inconsistent states.
A constructor is the class's initialization routine used during instance creation. new StockItem("Pens", 12) supplies both starting values in one expression. The caller can read the statement as a request for a named item beginning with twelve units rather than an unfinished item that needs more assignments.
A constructor does not replace ordinary behavior methods. Creating an item with twelve units is initialization; selling three later is an action on an existing item. Keep these responsibilities separate. This lesson remains an in-memory teaching model, not a stock database or a checkout integration.
Main.java · Supply starting values at creation
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pens | stock: 12
A constructor uses the class name and has no return type
The declaration StockItem(String name, int quantity) has exactly the class name, a parameter list, and a body. It has no return type, not even void. A declaration written void StockItem(...) would be an ordinary method with an unfortunate name, not the constructor used by a new expression.
The arguments "Pens" and 12 are passed to the selected constructor's parameters. After initialization completes normally, the new expression supplies the reference that main stores in pens. Do not write return this to make that happen; constructors do not return an ordinary result value the way summary does.
Our first small example simply assigns values to show the syntax. It is not yet the validated model used in the lab. As you read it, locate the fields outside the constructor, the parameters inside its parentheses, and summary as the separate instance method called after creation.
Read the creation statement step by step
new StockItem("Pens", 12)selects the constructor whose parameters accept a String and an int.- The parameters receive the supplied values.
- The constructor assigns the fields on the new instance.
- After normal completion, the expression supplies the reference stored in pens.
pens.summary()is a later ordinary instance-method call.
The constructor is not called again each time you read a field or print a summary.
this.name means the field; name means the parameter
The field and constructor parameter can both be called name. Inside the constructor, the parameter hides the bare field name. In this.name = name, the left side selects the current object's field, and the right side reads the supplied parameter. They are different variables even though the spelling after the dot matches.
Writing name = name instead assigns the parameter to itself and leaves the field untouched. The same issue applies to quantity = quantity. Such code can compile and still produce null and zero in a report, which makes this a useful debugging exercise rather than merely a punctuation rule.
this refers to the instance currently being initialized or receiving an instance-method call. It does not refer to the class or automatically to the variable named pens in main. When a second item is created, the same constructor body initializes that second instance's fields.
Main.java · The field and parameter are different variables
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pens | stock: 12
Notebooks | stock: 4
Default and no-argument do not mean the same thing
If a class declares no constructors, Java supplies a default constructor. DraftItem therefore allows new DraftItem() even though no constructor appears in its source. The fields still begin with their language defaults: null for the reference and zero for the int. Java does not invent a useful item name for you.
Once you declare any constructor, Java does not also add that default constructor. A class with only StockItem(String, int) cannot be created with new StockItem(). If a no-argument form is meaningful, write it explicitly rather than assuming it remains available.
No-argument describes a parameter list with no arguments. Default constructor is the specific constructor supplied by the compiler when none is declared. The StockItem() overload later in this lesson is an explicitly written no-argument constructor, not the compiler-provided default constructor.
Main.java · An implicit default constructor does not invent domain values
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Name: null
Quantity: 0
Offer convenient creation forms without copying the rules
The validated StockItem has three creation forms: no arguments for Unnamed with zero stock, a name for a named item with zero stock, and name plus quantity for an explicitly stocked item. The parameter lists select the overload, just as they did for ordinary overloaded methods.
The shorter constructors call this("Unnamed", 0) or this(name, 0). Here this(...) means invoke another constructor of the same class for this same instance. It is not a field access and it does not allocate another StockItem. There is still only one newly created item for that new expression.
Keep validation and assignments in the full constructor. Then every supported creation path passes through one policy instead of maintaining three copies. A convenience constructor must supply genuinely valid defaults; it should not be a hidden escape hatch that accepts data rejected by the full form.
Main.java · Three creation forms, one validation policy
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Unnamed | stock: 0
Notebooks | stock: 0
Pens | stock: 12
Predict the name-only creation path
new StockItem("Notebooks")selects the one-parameter constructor.this(name, 0)delegates to the two-parameter constructor on that same instance.- The full constructor validates Notebooks and zero, then assigns both fields.
- The convenience constructor finishes. Only one StockItem has been created.
Trace delegation before the rest of the caller's body
The Parcel example prints solely to reveal execution order. new Parcel() enters the no-argument constructor, delegates to Parcel(String), and only then resumes the remaining no-argument body. Main's ready message comes after both constructors finish. Normal model constructors should avoid unnecessary console side effects.
For this course's Java 21 baseline, an explicit this(...) constructor invocation must be the first statement in its constructor body. Put shared validation in the target constructor instead of writing a preliminary statement before delegation. Newer Java versions have different constructor-body rules, so keep the version context attached to this example.
A constructor cannot delegate directly or indirectly back to itself. Such a cycle is rejected at compile time; it is not something you should run until a stack overflow occurs. this.field and this(...) are two different syntactic uses of the same keyword, and only the latter starts constructor delegation.
Main.java · Follow a delegated constructor's execution order
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Full constructor: Standard
No-argument body resumes
Ready: Standard
Reject invalid inputs instead of returning an invalid item
Our policy requires a non-null, nonblank name and a quantity from zero through 1000. The full constructor checks name before calling its String methods, rejects an out-of-range quantity, then stores the stripped name and quantity. strip removes surrounding Java-defined whitespace; this is a stated normalization policy, not a complete international name-validation system.
throw new IllegalArgumentException(...) ends initialization with an error rather than pretending the invalid request succeeded. The small try/catch in main demonstrates the rejection without ending the whole example: try contains the request, catch handles that specific exception, and getMessage reads its explanation. Full exception handling is a later topic; here you only need this narrow demonstration pattern.
When construction throws, the new expression does not complete normally, so its assignment does not happen. Do not describe that as no memory ever being allocated. The important caller-facing guarantee here is that no successfully initialized invalid item is returned through the expression. Validate before assigning fields and do not publish this from inside a constructor.
Main.java · Reject invalid creation and continue deliberately
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Rejected: Quantity must be 0..1000.
Pens | stock: 12
Shared constructor code does not mean shared object state
Both item constructors eventually run the same full constructor body, but each new expression initializes its own instance. Two pens items can share the same name text and still have different quantities. The two reports show twelve and zero, and reference comparison confirms they are different objects.
Delegation with this(name, 0) reuses initialization on the current instance. Writing new StockItem(name, 0) inside the convenience constructor would instead create a separate temporary item and would not initialize the outer object's fields. The distinction is about which object receives the assignments, not just whether the code looks similar.
Initial validation also does not protect all future state. These fields remain package-accessible in this one-file example, so another caller could later assign an invalid quantity directly. Constructors establish a valid starting point; the next lesson adds access control and intentional methods to protect ongoing changes.
Main.java · Constructor delegation still creates independent items
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Same name: true
Same object: false
Pens | stock: 12
Pens | stock: 0
this is an object reference, not a variable-name shortcut
The small Tag class returns this == other from an instance method to make the reference meaning visible. first.sameAs(alias) is true because both names reach the same instance; first.sameAs(second) is false even though both labels contain Shelf A. The current receiver determines the value of this.
You cannot use this as a current-object reference in a static method because a static method has no current instance. Main can still create a Tag and call its instance methods through a reference. Making constructors or this-based behavior static is not a repair for a missing receiver.
You will mostly use this for clear field access in this lesson, not for fluent APIs or passing partly built objects to other services. Keeping construction self-contained avoids exposing incomplete state and makes it easier to reason about each instance's starting values.
Main.java · this refers to the current object
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Alias: true
Separate tag: false
Test every creation path, including rejected requests
Start with the three valid overloads and compare every field and summary. Test zero and exactly 1000 as accepted quantities, then negative one and 1001 as rejected values. Also try extreme int values so the validation rule is not accidentally written as a check for only one bad number.
For names, test null, empty text, whitespace-only text, and a padded valid name. Check that invalid names fail through both the one-argument and full constructor, because delegation is supposed to preserve one validation policy. A valid padded name should be stored without surrounding whitespace while the caller's original String remains unchanged.
Finally verify identity and failure behavior: repeated creation returns independent items, repeated queries do not change fields, and a failed assignment of a newly constructed item leaves any previously stored reference alone. These tests check the constructor's contract rather than merely whether one happy-path line prints.
Predict a failed replacement
If selected already refers to pens, selected = new StockItem("Invalid", -1) must finish evaluating its right side before assignment. That constructor throws, so selected still refers to pens after the specific error is caught. Neither resetting selected nor altering pens is part of this failed request.
Repair exercise: the fields were never assigned
The constructor receives Pens and 12, but the report prints null and zero. It compiles because both assignments target parameters rather than fields. Repair those two assignments without changing main.
Main.java · Find the shadowed assignments
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
The starter compiles but currently prints:
null | stock: 0
Reveal the explained repair
Inside the constructor, the bare names resolve to the parameters. Assigning a parameter to itself never changes the object's fields. this.name = name and this.quantity = quantity explicitly select the instance fields on the left while keeping the supplied parameters on the right. This repairs initialization without renaming the class or adding static state.
Main.java · Assign the receiving object's fields
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pens | stock: 12
Independent lab: create valid items through every overload
Create stock items through one validated full constructor and two convenience constructors. Demonstrate valid starts, normalized names, independent instances, and rejected creation without changing a previously selected item. This is a local console exercise with no persistent inventory.
- Declare public Main and a separate package-private StockItem in Main.java. StockItem has instance String name and int quantity, plus static final int MAX_QUANTITY = 1000.
- Implement StockItem(String name, int quantity): reject null or blank name with IllegalArgumentException and message Name must not be blank.; reject quantity outside 0..1000 with message Quantity must be 0..1000. Check the name first.
- After validation, assign this.name = name.strip() and this.quantity = quantity. Do not silently clamp invalid quantities, replace rejected names, or print from this constructor.
- Implement StockItem(String name) by delegating with this(name, 0), and StockItem() with this("Unnamed", 0). Follow the Java 21 first-statement rule and keep validation in the full constructor.
- Implement read-only String summary() returning name + " | stock: " + quantity. In main create padded Pens with twelve units, Notebooks with default zero, and the no-argument default item, then print their summaries and an identity check.
- Keep selected pointing to pens, attempt to replace it with an invalid negative-quantity item, and catch the specific exception. Also demonstrate blank-name rejection through the one-argument constructor. Print both messages, confirm selection is preserved, and report the original item again.
Follow the supplied creation and rejection sequence. Expected output:
Pens | stock: 12
Notebooks | stock: 0
Unnamed | stock: 0
Independent items: true
Rejected quantity: Quantity must be 0..1000.
Rejected name: Name must not be blank.
Selection preserved: true
Existing item: Pens | stock: 12
Main.java · Build validated constructors
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Hint 1: begin with the full constructor
Check name for null before calling isBlank. Reject a bad name before checking quantity so error precedence is deterministic. After both checks succeed, strip the name and assign fields with this. Keep exception messages exactly as specified.
Hint 2: delegate, then test the caller
The shorter constructors contain this(...) as their first statement under Java 21. Put no duplicate validation there. In main, keep a reference to a valid item before attempting the invalid replacement, catch only the specified rejection, and compare selected with the original afterward.
Reveal the complete explained solution
Main.java · Complete validated item creation
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
The full constructor strips the padded name and stores twelve. Both convenience forms delegate to that same validation and assignment code. The invalid quantity and blank name throw before their creation expressions complete normally. The failed selected assignment therefore leaves selected referring to pens, whose fields and report remain unchanged.
Test changes and boundaries
Predict the selected overload, stored values, and success or error before running. Test the fields directly as well as the summary. Reset restores the original program.
- Test every overload and verify fields as well as summaries. The explicit no-argument form produces Unnamed and zero; a name-only form preserves the normalized supplied name and zero.
- Accept quantities 0 and 1000. Reject -1, 1001, Integer.MIN_VALUE, and Integer.MAX_VALUE with the quantity message, not a silently clamped result.
- Reject null, empty, space-only, and tab/newline-only names through both name-taking overloads. Verify a padded valid name is stripped and the caller's String is unchanged.
- Create two separate items with identical names and quantities. Their summaries may match, but their references must differ. Repeated queries must preserve both fields.
- Start with a valid selected item, then attempt an invalid replacement. Verify the caught error, unchanged reference and fields, and successful creation afterward. Check that writing a constructor removes the implicit default form unless you explicitly provide a no-argument overload.
Common mistakes and how to diagnose them
Adding void to the constructor declaration
void StockItem(...) declares a method, not a constructor. Remove the return type and match the class name exactly. The new expression selects constructors, not ordinary methods with class-like names.
Assigning a shadowing parameter to itself
name = name compiles but leaves the field unchanged. Use this.name = name to distinguish the current object's field from the parameter holding the supplied value.
Assuming the implicit no-argument constructor still exists
Declaring any constructor suppresses the compiler-provided default constructor. Call an available parameter list or explicitly provide a meaningful no-argument constructor that delegates to valid defaults.
Writing statements before this(...) on Java 21
An explicit constructor delegation call must be first in this course's Java 21 baseline. Put validation in the target constructor. Do not copy rules into every convenience constructor or rely on newer-version syntax.
Creating another object instead of delegating
new StockItem(name, 0) creates a different instance. It does not initialize the current object's fields. Use this(name, 0) when the goal is constructor chaining on the current instance.
Catching an error but still assuming assignment succeeded
If a constructor throws, the new expression does not produce a normally initialized result for assignment. Keep a deliberate failure path and do not use a local variable that was never definitely assigned. An existing reference used as the assignment target retains its earlier value.
Frequently asked questions
Does a constructor return the object?
A constructor has no declared return type and cannot return an object value. The new expression supplies a reference after initialization completes normally. You do not write return this inside the constructor.
What is the difference between this.name and this(name, 0)?
this.name selects a field on the current instance. this(name, 0) invokes another constructor of the same class for that same instance. It does not create a second item.
Can a constructor be overloaded?
Yes. Different parameter lists provide distinct creation forms. The lab keeps them consistent by delegating the shorter overloads to one full constructor that performs validation and assignments.
Why throw an exception instead of returning false?
A constructor cannot return a boolean result in place of its initialized object. In this exercise invalid arguments throw a specific exception, and main demonstrates handling it. A separate factory API could choose another result design, but that is not the contract taught here.
Does valid construction make the whole object permanently safe?
No. It establishes initial state. Our package-accessible fields still permit later writes that bypass policy. The next lesson adds encapsulation so ongoing mutations can also be controlled.
One validated starting point, several convenient entry paths
Constructors initialize instances; ordinary methods perform later behavior. this.field selects the current object's state, while this(...) delegates initialization on the same instance. A compiler-provided default constructor exists only when no constructor is declared.
Before moving on, explain why parameter self-assignment leaves fields unchanged, why delegation does not create another item, and why a rejected new expression does not overwrite a previous reference. Next: modifiers and encapsulation, where you will protect the valid state after creation as well.
Can you explain how each item is initialized?
Answer all six questions and read the explanations. Answers are temporary on this page and do not save course progress.
References and related learning
The item scenario, explanations, examples, and exercises are original SovranCode material. References below document the Java 21 language and String API rules used here.
- Java 21 specification: constructor declarations and delegation
- Java 21 specification: this expression
- Java 21 String API: strip and isBlank
Review member ownership · Review overload selection · Review object identity · Java course roadmap.
Last updated: