The constructor that did nothing
Riya writes a neat constructor: Student(String name) { name = name; }. It compiles without a single warning. She creates three students, prints them, and gets null, null, null. She stares at the line for ten minutes. It looks perfectly correct.
Sir, name = name should copy the name. Why is every student null?
Inside the constructor there are two things called name: the parameter and the field. The closer one, the parameter, wins. So you copied the parameter into itself.
Then how do I say "the field of this student"?
Exactly like that: this.name. Every method gets a hidden variable this that points to the object it is working on. this.name = name means "my field gets the parameter's value".
What is the this keyword?
this is a keyword that, inside an instance method or constructor, refers to the current object: the object on which the method was invoked, or the object being constructed. It is used to access fields hidden by local variables or parameters (this.field), to call another constructor of the same class (this(...)), to pass the current object as an argument, and to return it. It cannot be used in a static context, and it cannot be assigned.
"Me, the object you called." When Riya's object runs show(), this is Riya's object; when Kabir's runs it, this is Kabir's.
Words you'll meet in this lesson
s in s.show().return this so calls can be chained.this.Four uses of this
this.field | Field instead of a same-named parameter: this.name = name; |
|---|---|
this(...) | Constructor chaining; first statement of a constructor |
method(this) | Pass the current object to another object or method |
return this; | Return the current object so calls can be chained |
| Not allowed | In static methods (main too), and this = other; ("cannot assign to 'this'") |
Exam tip: this (a reference to the current object) and this(...) (a call to another constructor) are different things. Both are asked as MCQs. Neither is allowed in a static method.
The uses of this, side by side
this.field = param
Copies a parameter into the field with the same name. The most common use, in nearly every constructor and setter.
Shadowing fixPass this
new Order(this, amount): the customer hands itself to the order it creates, so the order knows who placed it.
Return this
Each call returns the same object, so the next call can follow: order.size("L").crust("Thin").
this is the object before the dot
class Box {
void whoAmI() {
System.out.println("this = " + this);
}
}
public class Main {
public static void main(String[] args) {
Box b = new Box();
System.out.println("b = " + b);
b.whoAmI();
}
}Both lines print the same class name and hash code, so this inside whoAmI() is exactly the object b refers to. (The number after @ differs between runs and machines, so your output will differ.)
When do you need to write this?
| Situation | Plain name means | Need this.? |
|---|---|---|
No parameter or local called name | The field | No (optional, some teams write it for clarity) |
Parameter called name | The parameter | Yes, to reach the field |
Local variable called name | The local variable | Yes, to reach the field |
| Inside a static method | Only a static field or a local | Not possible: there is no this |
Silent bug: name = name; compiles with no error and no warning by default. The field just keeps its default (null, 0). Many IDEs highlight it as "assignment to itself"; read those warnings.
How this gets its value
You call a method on an object
The JVM pushes a frame forriya.show();showand puts the reference fromriyainto the frame's first slot. That slot isthis; in bytecode it is read withaload_0.Plain field names use it automatically
javac compilesvoid show() { System.out.println(name); // really this.name }nameas "load this, then read its fieldname". That's how the same method code prints Riya's name for Riya and Kabir's for Kabir.A parameter with the same name hides the field
Java looks for a name in the closest scope first: local variables and parameters, then fields.void rename(String name) { this.name = name; // field = parameter }this.skips straight to the field.Pass or return the current object
Passing or returningOrder placeOrder(int amt) { return new Order(this, amt); } Wallet add(int amt) { balance += amt; return this; }thiscopies a reference, as always: no new object is created. The Order stores a reference to the same customer; the chained call works on the same wallet.No this in static code
Static methods are called on the class, not on an object, so their frame has no slot 0 reference to fill. javac reports: non-static variable this cannot be referenced from a static context.public static void main(String[] args) { this.visits++; // compile error }
The hidden this: both views
Left: the code and its result. Right: the method frame with its hidden this, and the object it points to.
this 6 steps- Create:
new Student("Riya")starts the constructor. Its parameter is calledname, just like the field. - Bug:
name = namecopies the parameter into itself. The program compiles and runs, but the student's name staysnull. - Fix:
this.name = namestores the parameter in the field of the object being built. - Method call: inside
show(),nameis short forthis.name, andthisiss's object. - Chain:
marks(80)ends withreturn this, so.city("Pune")runs on the same student. - Static: a static method isn't called on any object, so using
thisthere is a compile error.
- The constructor's frame holds two references:
this(the new object #1) and the parametername(the String "Riya"). name = namereads the parameter and writes the parameter. The object's field is never touched.this.namefollowsthisto #1 and writes there. Now the field holds "Riya".- For
s.show(), the JVM copiess's reference into the new frame'sthis(slot 0,aload_0). marks(80)sets #1's marks and returnsthis..city("Pune")is then called on that same #1.- A static method's frame starts with its parameters; there is no current object. javac stops you before the program runs.
this in real systems
You'll see this in almost every Java class you read at work, mostly in constructors and setters, and in the fluent APIs of popular libraries.
Watch it with an example: a fluent order builder
Methods that end with return this; can be chained. Watch three calls work on the same object, because this is always “the object this method was called on”.
- new Order(): creates object #1 with an empty item list.
- add("Tea"): inside the method,
thisrefers to #1. It adds Tea and ends withreturn this;, so the expression's value is #1. - .add("Vada"): is called on what the last call returned: the same #1.
thisis #1 again. - .note("less sugar"): same object again. Each call returns
this, keeping the chain going. - Fluent style: one object, built step by step in one readable line.
StringBuilder.appendand many builder classes in real libraries work exactly like this.
1. Constructors and setters everywhere
Teams name parameters after the fields they fill (pan, name, dob) because it reads clearly, and use this. to store them. IDEs generate exactly this code.
2. Fluent builders
StringBuilder.append returns this, which is why sb.append(a).append(b) works (Lesson 4.6). HTTP clients and report builders use the same style: request.header(…).timeout(…).send().
3. Objects that register themselves
A customer creates an order and passes this so the order knows its customer; a screen registers itself as the listener for a button: button.addListener(this).
4. Comparing with another object
Methods that work on two objects of the same class use this for one and a parameter for the other: a.transferTo(b, 500) checks if (other == this) to block a transfer to the same account.
Examples are simplified. Library method names above are typical patterns, not the exact API of any one product.
A fluent pizza order
class PizzaOrder {
String size = "Medium";
String crust = "Classic";
int extraCheese;
PizzaOrder size(String size) {
this.size = size;
return this; // hand back the same object
}
PizzaOrder crust(String crust) {
this.crust = crust;
return this;
}
PizzaOrder addCheese() {
this.extraCheese++;
return this;
}
void print() {
System.out.println(size + ", " + crust + " crust, extra cheese x" + extraCheese);
}
}
public class Main {
public static void main(String[] args) {
PizzaOrder order = new PizzaOrder();
order.size("Large").crust("Thin").addCheese().addCheese().print();
System.out.println(order.size("Small") == order);
}
}this.size = size;Field on the left, parameter on the right.return this;Returns the order the method was called on, so the next call can follow.this.extraCheese++;this.is optional here (no clash), but harmless.order.size("Small") == ordertrue: the method returned the very same object, not a copy.
Chaining on one wallet
Watch this appear in every add frame, always pointing to #1, and the returned reference feeding the next call.
Worked examples: real problems, solved and traced in memory
Three business problems where this does the real work. Run each solution, then step through it: in every method frame, look at the this variable and follow it to the object on the heap.
Raise and promote in one chain
Problem statement
During appraisal, HR gives Sneha a 10% raise and promotes her to "Senior Developer" in one readable statement. Write raise(percent) and promote(title) so they can be chained.
new Employee("Sneha", "Developer", 40000)
.raise(10).promote("Senior Developer")Sneha | Senior Developer | Rs 44000
Approach
- The constructor uses
this.field = parameterbecause the parameters have the same names as the fields. raiseupdatesthis.salaryand returnsthis.promoteis called on the returned reference, which is the same employee, and also returnsthis.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
A doctor books an appointment for a patient
Problem statement
In a clinic app, a doctor books a patient with doctor.book(patient, slot). The new Appointment must hold references to both the doctor and the patient. Print the appointment slip.
Doctor "Dr. Mehta" (Cardiology) Patient "Asha", slot "10:30"
10:30 | Asha with Dr. Mehta (Cardiology)
Approach
bookruns on a Doctor object, so inside itthisis that doctor.- It creates
new Appointment(this, patient, slot): the doctor passes itself along with the patient. - The appointment stores two references; nothing is copied.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Find the cheapest seller
Problem statement
Three sellers offer the same phone at different prices. Write cheaperOf(Product other) that returns whichever of this and other is cheaper, and use it in a chain to find the cheapest of the three.
("Seller A", 15999), ("Seller B", 15499), ("Seller C", 15749)Buy from Seller B at Rs 15499
Approach
- Inside
cheaperOf, two products take part:this(before the dot) andother(the argument). - Return the reference of the cheaper one: either
thisorother. No new object is created. - Chain it:
a.cheaperOf(b).cheaperOf(c). The second call runs on whichever product won the first.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 6.3 in one screen
this= reference to the current object (the one before the dot, or the one being constructed).- Every instance method and constructor has it as a hidden first variable (
aload_0). - A parameter or local with a field's name shadows it: use
this.field. name = name;compiles but does nothing useful.this(...)calls another constructor: first statement only.- Pass
thisto link objects;return this;for fluent chains. - No
thisin static methods;thiscan't be reassigned.
Interview and viva questions
What is the this keyword in Java?
A reference to the current object inside an instance method or constructor. It is used to access shadowed fields, call another constructor with this(...), pass the current object to other methods, and return the current object for method chaining.
Why can't we use this inside a static method?
A static method belongs to the class and is called without any object, so there is no current object for this to refer to. The compiler reports "non-static variable this cannot be referenced from a static context".
Is this a variable? Can we change it?
It behaves like a final local variable that the JVM fills in (slot 0 of the frame). You can read it, pass it and return it, but this = other; is a compile error ("cannot assign to 'this'").
What is the difference between this and this()?
this is a reference to the current object and can be used anywhere in instance code. this(...) is a call to another constructor of the same class and is allowed only as the first statement of a constructor.
Why do setters often return this?
So that several setters can be called in one chained expression, which reads like a sentence and is common in builders. Plain JavaBean setters, which some frameworks expect, return void instead, so teams pick one style per class.
Practice problems
Practise the four uses of this: reaching shadowed fields, chaining constructors, passing the current object and returning it. The last three are real business tasks.
Fix the shadowing bug
This Employee constructor stores nothing: every field stays at its default. Fix it using this and print the employee.
new Employee("Sneha", "E102", 42000)E102 | Sneha | Rs 42000
Show solution and explanation
class Employee {
String name;
String id;
int salary;
Employee(String name, String id, int salary) {
this.name = name; // was: name = name;
this.id = id; // was: id = id;
this.salary = salary; // was: salary = salary;
}
}
public class Main {
public static void main(String[] args) {
Employee e = new Employee("Sneha", "E102", 42000);
System.out.println(e.id + " | " + e.name + " | Rs " + e.salary);
}
}How it works: each parameter has the same name as a field, so a plain name means the parameter. this.name means "the name field of the object being built". Without the fix the output would be null | null | Rs 0.
A setter with a guard
Write setAge(int age) for a Person. It should store the age only if it is between 0 and 120, otherwise print a message and keep the old age.
setAge(25), then setAge(-4)
Invalid age -4 for Kabir Kabir is 25
Show solution and explanation
class Person {
String name;
int age;
Person(String name) {
this.name = name;
}
void setAge(int age) {
if (age < 0 || age > 120) {
System.out.println("Invalid age " + age + " for " + name);
return;
}
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Kabir");
p.setAge(25);
p.setAge(-4);
System.out.println(p.name + " is " + p.age);
}
}How it works: inside setAge, age is the new value (parameter) and this.age is the stored one (field). We check the parameter first, and only then copy it into the field.
Chain a counter
Make a Score class whose add(int points) returns this. Add 4, 6 and 2 points in one chained statement and print the total.
s.add(4).add(6).add(2)
Total: 12
Show solution and explanation
class Score {
int total;
Score add(int points) {
total += points;
return this;
}
}
public class Main {
public static void main(String[] args) {
Score s = new Score();
s.add(4).add(6).add(2);
System.out.println("Total: " + s.total);
}
}How it works: s.add(4) returns the same Score object, so .add(6) is called on it, and so on. Only one object exists the whole time.
Compare with another object
Add a method isOlderThan(Person other) that compares this person's age with another person's age. Use it for two people.
Riya 21, Arjun 45
Riya older than Arjun? false Arjun older than Riya? true
Show solution and explanation
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
boolean isOlderThan(Person other) {
return this.age > other.age;
}
}
public class Main {
public static void main(String[] args) {
Person riya = new Person("Riya", 21);
Person arjun = new Person("Arjun", 45);
System.out.println("Riya older than Arjun? " + riya.isOlderThan(arjun));
System.out.println("Arjun older than Riya? " + arjun.isOlderThan(riya));
}
}How it works: two Person objects take part: this is the one before the dot, other is the argument. Writing this.age here isn't required, but it makes the comparison read clearly.
Pass this to link objects
A Teacher can create a Batch with startBatch(name). The batch must remember its teacher. Create two batches from one teacher and print each batch with its teacher's name.
Teacher "Arjun Sir" starts "Java-Morning" and "Java-Evening"
Java-Morning by Arjun Sir Java-Evening by Arjun Sir Same teacher object: true
Show solution and explanation
class Teacher {
String name;
Teacher(String name) {
this.name = name;
}
Batch startBatch(String batchName) {
return new Batch(batchName, this);
}
}
class Batch {
String name;
Teacher teacher;
Batch(String name, Teacher teacher) {
this.name = name;
this.teacher = teacher;
}
}
public class Main {
public static void main(String[] args) {
Teacher t = new Teacher("Arjun Sir");
Batch b1 = t.startBatch("Java-Morning");
Batch b2 = t.startBatch("Java-Evening");
System.out.println(b1.name + " by " + b1.teacher.name);
System.out.println(b2.name + " by " + b2.teacher.name);
System.out.println("Same teacher object: " + (b1.teacher == b2.teacher));
}
}How it works: inside startBatch, this is the teacher it was called on. Passing it to the Batch constructor stores a reference, so both batches point to the same Teacher object, not to copies.
Constructor chaining with this(...)
A Shirt can be created with (size), (size, colour) or (size, colour, price). Defaults: colour "White", price Rs 799. Use this(...) so only one constructor assigns fields.
new Shirt("M"), new Shirt("L", "Blue"), new Shirt("XL", "Black", 999)M | White | Rs 799 L | Blue | Rs 799 XL | Black | Rs 999
Show solution and explanation
class Shirt {
String size;
String colour;
int price;
Shirt(String size) {
this(size, "White");
}
Shirt(String size, String colour) {
this(size, colour, 799);
}
Shirt(String size, String colour, int price) {
this.size = size;
this.colour = colour;
this.price = price;
}
void show() {
System.out.println(size + " | " + colour + " | Rs " + price);
}
}
public class Main {
public static void main(String[] args) {
new Shirt("M").show();
new Shirt("L", "Blue").show();
new Shirt("XL", "Black", 999).show();
}
}How it works: this(...) (with brackets) calls another constructor, while this.size (with a dot) reaches a field. Both appear here: the short constructors chain, the full one assigns.
A fluent message builder
Build an SMS text with a fluent Message class: to(name), line(text) and sign(sender) each return this; text() returns the final String.
to("Riya").line("Your order is out for delivery.").line("OTP: 4821").sign("QuickKart")Hi Riya, Your order is out for delivery. OTP: 4821 - QuickKart
Show solution and explanation
class Message {
StringBuilder body = new StringBuilder();
Message to(String name) {
body.append("Hi ").append(name).append(",\n");
return this;
}
Message line(String text) {
body.append(text).append("\n");
return this;
}
Message sign(String sender) {
body.append("- ").append(sender);
return this;
}
String text() {
return body.toString();
}
}
public class Main {
public static void main(String[] args) {
String sms = new Message()
.to("Riya")
.line("Your order is out for delivery.")
.line("OTP: 4821")
.sign("QuickKart")
.text();
System.out.println(sms);
}
}How it works: each method appends to the same StringBuilder field and returns this, so the calls read top to bottom like the message itself. text() ends the chain by returning a String instead of the builder. "QuickKart" is a made-up shop name.
Fund transfer between accounts
Write transferTo(Account other, int amount). It must refuse a transfer to the same account (other == this) and refuse if the balance is too low; otherwise move the money. Try a normal transfer, a self-transfer and an oversized one.
Riya Rs 5000, Kabir Rs 1000 riya → kabir 1500; riya → riya 100; kabir → riya 9000
Done: Rs 1500 from Riya to Kabir Rejected: Riya cannot transfer to the same account Rejected: Kabir has only Rs 2500 Riya: Rs 3500, Kabir: Rs 2500
Show solution and explanation
class Account {
String holder;
int balance;
Account(String holder, int balance) {
this.holder = holder;
this.balance = balance;
}
boolean transferTo(Account other, int amount) {
if (other == this) {
System.out.println("Rejected: " + holder + " cannot transfer to the same account");
return false;
}
if (amount > this.balance) {
System.out.println("Rejected: " + holder + " has only Rs " + balance);
return false;
}
this.balance -= amount;
other.balance += amount;
System.out.println("Done: Rs " + amount + " from " + holder + " to " + other.holder);
return true;
}
}
public class Main {
public static void main(String[] args) {
Account riya = new Account("Riya", 5000);
Account kabir = new Account("Kabir", 1000);
riya.transferTo(kabir, 1500);
riya.transferTo(riya, 100);
kabir.transferTo(riya, 9000);
System.out.println("Riya: Rs " + riya.balance + ", Kabir: Rs " + kabir.balance);
}
}How it works:
thisis the sending account,otherthe receiving one. Using both names makes the money flow obvious.other == thiscompares references: true only when both are literally the same object.- Real banks also log every transfer and do the two updates inside one database transaction so money is never lost halfway.
Shopping cart with chained adds
A Cart supports add(item, price, qty) returning this, and applyCoupon(percent) also returning this. Build a cart in one chain and print the payable amount.
add("Saree", 2499, 1), add("Bangles", 349, 2), applyCoupon(10)Added 1 x Saree Added 2 x Bangles 3 items, subtotal Rs 3197 Payable after 10% coupon: Rs 2878
Show solution and explanation
class Cart {
int items;
int subtotal;
int discountPercent;
Cart add(String item, int price, int qty) {
this.items += qty;
this.subtotal += price * qty;
System.out.println("Added " + qty + " x " + item);
return this;
}
Cart applyCoupon(int percent) {
this.discountPercent = percent;
return this;
}
int payable() {
return subtotal - subtotal * discountPercent / 100;
}
}
public class Main {
public static void main(String[] args) {
Cart cart = new Cart()
.add("Saree", 2499, 1)
.add("Bangles", 349, 2)
.applyCoupon(10);
System.out.println(cart.items + " items, subtotal Rs " + cart.subtotal);
System.out.println("Payable after 10% coupon: Rs " + cart.payable());
}
}How it works:
new Cart()creates one object; each chained call returns it, so the finalcartvariable refers to that same object.- Subtotal: 2499 + 2 × 349 = 3197. Integer arithmetic: 3197 × 10 / 100 = 319, so payable is 2878.
- Real carts usually use paise (long) or BigDecimal to avoid rounding surprises with money.
Ride assignment: the driver passes itself
When a Driver accepts a Ride, the driver calls ride.assign(this) and becomes busy. A busy driver can't accept another ride. Simulate one driver and two ride requests.
Driver "Manoj" (MH12 AB 4321) Ride 1: Kothrud → Hinjewadi, Ride 2: Baner → Wakad
Manoj accepted Kothrud -> Hinjewadi Manoj is busy, cannot take Baner -> Wakad Kothrud -> Hinjewadi: Manoj (MH12 AB 4321) Baner -> Wakad: searching...
Show solution and explanation
class Ride {
String route;
Driver driver;
Ride(String route) {
this.route = route;
}
void assign(Driver driver) {
this.driver = driver;
}
}
class Driver {
String name;
String vehicle;
boolean busy;
Driver(String name, String vehicle) {
this.name = name;
this.vehicle = vehicle;
}
boolean accept(Ride ride) {
if (busy) {
System.out.println(name + " is busy, cannot take " + ride.route);
return false;
}
ride.assign(this);
busy = true;
System.out.println(name + " accepted " + ride.route);
return true;
}
}
public class Main {
public static void main(String[] args) {
Driver manoj = new Driver("Manoj", "MH12 AB 4321");
Ride r1 = new Ride("Kothrud -> Hinjewadi");
Ride r2 = new Ride("Baner -> Wakad");
manoj.accept(r1);
manoj.accept(r2);
System.out.println(r1.route + ": " + r1.driver.name + " (" + r1.driver.vehicle + ")");
System.out.println(r2.route + ": " + (r2.driver == null ? "searching..." : r2.driver.name));
}
}How it works:
- Inside
accept,thisis Manoj's Driver object.ride.assign(this)stores a reference to him in the ride. - The second ride is refused because
busyis now true, sor2.driverstaysnull; we check for null before reading its name. - Real apps also handle the race where two riders request the same driver at the same moment (Module 12).
Real-world scenario questions (asked in company interviews)
These look like small mistakes, but each one has reached production somewhere. Decide your answer first, then open the model answer.
Every policy has a null nominee
After a refactor, every new insurance policy is saved with a null nominee, though the screen clearly sends one. The constructor has the line nominee = nominee;. Tests pass and there is no exception. Explain and fix.
The interviewer is checking: do you recognise field shadowing?
Show a strong answer
- The parameter is named
nominee, the same as the field, so inside the constructornomineemeans the parameter. The line assigns the parameter to itself; the field keeps its defaultnull. - Fix:
this.nominee = nominee; - Add a test that creates a policy and checks the nominee, and turn on the IDE or static-analysis warning for self-assignment so it can't come back silently.
Avoid: renaming the parameter to n to dodge the issue. Clear names plus this. is the standard.
“Just make main use the field”
A junior wants main to update a field attemptCount of the QuizApp class and writes this.attemptCount++. It doesn't compile, so they plan to make the field static. What would you advise?
The interviewer is checking: do you understand static context versus objects, and the cost of making things static?
Show a strong answer
mainis static, so there is no current object and nothis.- Create the object and use it:
QuizApp app = new QuizApp(); app.attemptCount++;or better, move the logic into an instance method likeapp.run(). - Making the field static would share one count across every quiz object (Lesson 6.4), which is wrong as soon as two quizzes exist.
Avoid: making fields static just to silence a compile error.
A transfer to the same account doubled the balance
Account.transferTo(Account other, long amount) reads both balances into locals, subtracts from one, adds to the other, then writes both back. When a user transfers money to their own account, the balance increases. Why, and how do you guard against it?
The interviewer is checking: can you reason about this and a parameter referring to the same object?
Show a strong answer
- When
other == this, both references point to one object. The method reads the balance twice, writes "balance − amount", then overwrites it with "balance + amount", so the final value is higher. - Guard at the start:
if (other == this) throw new IllegalArgumentException("Same account");(or reject it in validation before calling). - Update fields directly (
this.balance -= amount; other.balance += amount;) instead of stale local copies, and wrap real transfers in a database transaction.
Avoid: assuming two parameters (or this and a parameter) always refer to different objects.
A chained call that crashes only sometimes
A report builder is used as new Report().title(t).filter(f).build(). Production logs show a NullPointerException "because the return value of Report.filter(...) is null" for some customers. What do you look for?
The interviewer is checking: do you know how fluent methods must be written?
Show a strong answer
- The message says
filterreturnednull. Look for a path inside it, often an earlyreturn null;when the filter is empty, that doesn't returnthis. - Every fluent method must end every path with
return this;; handle "nothing to do" by skipping the work, not by returning null. - Add tests for each method with edge inputs (empty filter, null title) and chain them.
Avoid: adding null checks at every call site. Fix the method's contract.