Vasavi College of Engineering (Autonomous) · CSE · B.E. III Sem · R-24
The box you cannot
break from outside.
Unit 1 named four pillars of object-oriented programming and then carefully taught you three of them — inheritance, polymorphism, abstraction. The fourth has been sitting unclaimed in your notes since Class 2. Today it becomes real, and it turns out you already learned every keyword it needs: last class's private, and Class 10's final.
“A field anyone can reach is a field anyone can corrupt — and the corruption is silent. Encapsulation is Java's way of making the corruption impossible instead of merely unlikely.”
Unit 1 named the fourth pillar. Today we make it real.
Read this page and you will know exactly what is coming, what you need to remember from earlier classes, and which four exam questions this hour is quietly preparing you for.
AFTER THIS CLASS YOU CAN
- Define encapsulation in one sentence an examiner accepts, with one example.
- Write a getter/setter pair whose setter refuses an invalid value.
- Debug a class whose
publicfield lets any code corrupt it silently. - Design an immutable class using
private+finaland no setters. - Recap all four pillars in one answer — the P1·Q11a shape.
THE ROAD THROUGH THIS CLASS · IN ORDER
CanteenCard — a full worked class whose balance cannot go negativeStudentIdprotected + final — the benefit, and the risk of skipping themPYQ P1·Q11bfinal / protected?PYQ P1·Q16aCLASS 14 OF 60 · 11 CORE-TAUGHT PAGES + 0 SELF-STUDY · FEEDS LAB 3
What encapsulation actually is — and the bug it exists to kill
We will not start with the definition. Definitions memorised before the problem is felt are the reason students write “encapsulation means data hiding” in an exam and then lose the follow-up marks. So first we are going to break a program on purpose, watch the damage happen silently, and only then give the damage a name.
Step 1 · Watch a mark of 5000 walk straight into a Student object
Here is a class so small you can hold it in your head: a Student with one field, marks. We declare that field public — which, from last class, you know means “reachable from absolutely anywhere”. Then we write a main that sets the marks twice: once sensibly, once absurdly.
Today is a new session, so we start with a new folder. Open the terminal and run these three lines exactly — the first moves you into the practice root you have been growing since Lab 0, the second creates today's folder, the third steps inside it.
class Student{ public int marks; // wide open to the whole program}public class BadDemo{ public static void main(String[] args) { Student s = new Student(); s.marks = 87; // sensible System.out.println("Marks: " + s.marks); s.marks = 5000; // absurd — and nobody stops it System.out.println("Marks: " + s.marks); }}4300%. The field being public is not a style problem — it is the hole the bad value walked through.
Step 2 · Now the word: encapsulation
You have just felt the problem. The fix has a name, and it is the fourth pillar Unit 1 promised you back in Class 2 and never cashed in. Read the three cards below in order — plain meaning first, then the picture, then the exam-shaped sentence.
In the simplest possible words: encapsulation means you keep a class's data private, and let the outside world touch it only through methods you wrote yourself.
That is the whole idea. The data stops being a public noticeboard and becomes something guarded. And because you wrote the guard, you decide what is allowed in. A mark of 5000 gets turned away at the door instead of walking in.
The college fee counter. The college keeps its cash in a locked room, not on a table in the corridor. You cannot walk in and change the ledger yourself. You go to a counter, hand over money, and the clerk checks it — right amount? right roll number? — and only then updates the record.
The locked room is your private field. The counter is your public method. The clerk's checking is your if statement. Notice the college did not become unusable by locking the room — it became trustworthy. Encapsulation does not hide data from legitimate users; it forces every visitor to come through a door where checking can happen.
The sentence to write in an answer sheet: “Encapsulation is the OOP principle of binding data and the methods that operate on that data into a single unit (a class), while restricting direct access to the data using access modifiers — typically declaring fields private and exposing controlled public methods.”
Two halves, and most students only write the second. Half one is binding — data and its behaviour live together in one class. Half two is restricting — that data is not reachable from outside. Write both halves and add one example, and this is a full-marks definition.
THE ONE-LINE ANSWER THAT LOSES MARKS
“Encapsulation means data hiding.” This is not wrong, but it is incomplete, and it is what almost everyone writes. Data hiding is the consequence; binding data with its methods is the mechanism. Also note the trap: data hiding is often listed as a separate idea from encapsulation. Safe position for an exam — encapsulation is the wrapping, and data hiding is the benefit you get from wrapping it with private.
Step 3 · See it: the capsule, the wall and the two doors
The word “encapsulation” comes from capsule — a sealed container. This diagram builds that capsule one piece at a time. In LEARNING mode press NEXT PIECE; in TEACHING mode each press of the clicker adds the next piece.
BUILD-UP · WHAT private ACTUALLY BLOCKS
THE CAPSULE · DATA SEALED IN THE MIDDLE, METHODS AS THE ONLY WAY THROUGH THE WALL
THE ONE THING TO NOTICE IN THAT PICTURE
The green arrow and the red arrow are aiming at the same integer. The difference is not what they want to do — it is which route they take. Encapsulation never removes the ability to change data. It removes the ability to change data without passing your check.
Step 4 · How Java implements it — two keyword changes and one if
Now we repair the broken program. Watch how small the repair is: public becomes private on the field, and two methods appear. Every keyword here is already yours — private and public from last class, if and return from Unit 1.
class Student{ private int marks; // sealed — only this class can touch it public void setMarks(int m) { if (m < 0 || m > 100) { System.out.println("Rejected: " + m + " is not a valid mark"); return; // leave without changing anything } marks = m; } public int getMarks() { return marks; }}public class GoodDemo{ public static void main(String[] args) { Student s = new Student(); s.setMarks(87); System.out.println("Marks: " + s.getMarks()); s.setMarks(5000); // the same attack as before System.out.println("Marks: " + s.getMarks()); }}BadDemo's, press for press. There, line 13's attack produced Marks: 5000. Here, press 21 revealed the identical attack — s.setMarks(5000) — and the console answered “Rejected” instead. Then press 22 asked the object again, and it still says 87. The bad value never got in, the object stayed valid, and the program told us what it refused instead of failing silently.GoodDemo.java. A source file's name must match its public class. Student here has no public keyword, so it is allowed to share the file. One public class per file, and the file takes its name — that rule from Class 3 is still in force.
LINE BY LINE — ONLY THE LINES THAT MATTER
Line 3 — private int marks; This single word does all the sealing. From this moment, the text s.marks written anywhere outside the Student class is not a runtime problem or a bad-practice warning — it is a compile error. The bug from the first program has become impossible to even build.
Lines 7–11 — the guard. This is the clerk at the counter. Any m below 0 or above 100 gets a message and a bare return, which exits the method immediately. Because we return before line 12, the assignment never runs. This shape — check the bad case first and leave early — is called a guard clause, and you will use it in every setter you write from now on.
Line 12 — marks = m; Only reachable if the value survived the guard. Note there is no this. needed, because the parameter is named m, not marks, so there is no name clash to resolve.
Lines 15–18 — getMarks(). The door out. It hands back the current value so the outside world can still read the mark. Encapsulation is not secrecy; reading was never the problem. Uncontrolled writing was.
TRY IT AND SEE THE COMPILER REFUSE
Do this now, it takes ten seconds. In GoodDemo.java, add the line s.marks = 5000; inside main and compile. You will get exactly this:
Step 5 · The same idea, three sizes — and the sentence to memorise
Before we close, three tiny before/after pairs. Same pattern every time: an open field that admits nonsense, and a sealed field with a door that refuses it. Read them as a set — the pattern is what you are learning, not the individual examples.
if.| WHAT YOU WRITE | WHO CAN READ THE FIELD | WHO CAN CHANGE IT | IS THE OBJECT ALWAYS VALID? |
|---|---|---|---|
public int marks; | everyone | everyone, unchecked | NO — any value at all |
private int marks; + getter + setter | everyone, via getMarks() | everyone, but only through your check | YES — the guard enforces it |
private int marks; + getter only | everyone, via getMarks() | nobody outside the class | YES — fixed after construction |
LEARN THIS SENTENCE · IT IS WORTH MARKS IN FOUR DIFFERENT QUESTIONS
“Encapsulation binds data and the methods that act on it into one class, and restricts direct access to that data using access modifiers — fields are declared private and reached only through controlled public methods.”
Followed by one example: private int marks with a setMarks() that rejects anything outside 0–100. Definition plus mechanism plus example — that is the full-marks shape, and it is exactly what the next page needs when the examiner asks for all four pillars at once.
CanteenCard, and finally the extreme case: a class with the doors out only, which cannot be changed at all after it is created.
The four pillars, in one answer
This question was waiting for today. When you met abstraction and inheritance back in Class 11, this paper was already on the syllabus — but it could not be answered honestly, because one of the four pillars had not been taught yet. Now it has. Let us collect the full four marks.
private and public (Class 13)The diagram to draw in the margin
Four boxes, one line each. Drawing this takes thirty seconds and it stops you from forgetting a pillar under exam pressure — the classic way this question is half-answered.
FOUR PILLARS HOLDING UP ONE BEAM · DRAW THIS FIRST, THEN WRITE THE LINES
Step-by-step: how to build the answer
Write the four names as a numbered list, in any order. Do this before writing any explanation. If you run out of time, the four names alone still earn you the bulk of the marks.
Give each pillar one sentence of definition. Use the mechanism word for each: private for encapsulation, extends for inheritance, overriding for polymorphism, abstract/interface for abstraction. Examiners look for these keywords.
Add one tiny example per pillar — four or five words is enough, like “private int marks with a setter that validates”. An example is what separates a 4/4 from a 2.5/4.
Close with the one-line link sentence: “Together these four principles make code modular, reusable and safe to change.” It shows you see them as a system, not four memorised words.
THE MODEL ANSWER · AS IT SHOULD LOOK ON YOUR SHEET
Q11(a) Explain the key principles of Object Oriented Programming. [4M]
The four key principles (pillars) of OOP are Encapsulation, Inheritance, Polymorphism and Abstraction.
1. Encapsulation — binding data and the methods that operate on that data into a single unit (a class), and restricting direct access to the data. Fields are declared private and accessed through public getter/setter methods.
Example: private int marks; with a setMarks() that rejects values outside 0–100.
2. Inheritance — a new class acquires the fields and methods of an existing class using the extends keyword, so common code is written once and reused.
Example: class Student extends Person — Student reuses Person's name and age.
3. Polymorphism — the same method name behaves differently depending on context. Achieved by overloading (same name, different parameters — compile time) and overriding (subclass redefines a parent method — runtime).
Example: area() defined differently in Circle and Square.
4. Abstraction — exposing only the essential behaviour and hiding the implementation details. Achieved using abstract classes and interfaces.
Example: an abstract Shape class declares area() without defining how each shape computes it.
Together, these four principles make a program modular, reusable, easier to maintain and safe to change.
ENCAPSULATION vs ABSTRACTION — THE ONE THE EXAMINER PROBES
These two sound identical to most students, and a follow-up question often asks for the difference. Hold on to this: abstraction hides complexity, encapsulation hides data. An abstract Shape hides how area is calculated (design-level, achieved with abstract/interface). A private balance hides the value itself (implementation-level, achieved with access modifiers). Abstraction is about what is shown; encapsulation is about who is allowed in.
What goes wrong in this answer — and the takeaway
By far the most common loss, and until today it was almost understandable — encapsulation is the one that gets dropped. One missing pillar is one lost mark, straight away.
The question says “explain”. A full program per pillar wastes the time you need for the other three and earns nothing extra. One short example line is the correct size.
Half a definition. Missing the binding half — that data and its methods live together in one class — usually costs half a mark.
Defining both as “hiding” with no distinction. Use the slip above: complexity vs data, design-level vs implementation-level.
KEY TAKEAWAY
Four pillars, four names, four one-line definitions, four tiny examples, one closing sentence. Encapsulation is the pillar students forget — and you just spent Part 3 building it from a real bug, so it is now the one you know best. Write it first in the exam.
Getters and setters — and the one chance they give you
In Part 3 you wrote a getter and a setter without being told the names. Now we make it formal: what the naming convention is, why Java programmers follow it so strictly, and the real reason setters exist — which is not “because we made the field private”.
The convention — and why it is not optional in practice
A getter reads a field. A setter writes it. The naming rule is mechanical, and Java's tooling depends on it:
| FIELD | GETTER | SETTER | RULE |
|---|---|---|---|
int marks | getMarks() | setMarks(int m) | get/set + field name, first letter capitalised |
String name | getName() | setName(String n) | getter returns the field's type; setter returns void |
boolean active | isActive() | setActive(boolean a) | booleans use is, not get — the one exception |
THE POINTLESS PAIR — A TRAP YOU MUST RECOGNISE
Look at this and ask yourself what has actually been protected:
s.setMarks(5000) succeeds. This is exactly as unsafe as a public field, just with more typing.This is the single most important insight in the whole class: a setter is not protection. The if inside the setter is the protection. The private keyword only guarantees that every write must pass through your method — it is then your job to put a guard in that method. Marking fields private and generating empty setters is a habit that looks like encapsulation and delivers none of it.
What a setter is actually for: four jobs
Once every write funnels through one method, that method becomes the perfect place to do work no public field could ever do. There are four standard jobs, and each one is a mark-worthy point in a “why encapsulation?” question.
Refuse impossible values. Marks outside 0–100, negative ages, empty roll numbers. The object can never hold nonsense.
Clean the value before storing. name.trim() to strip stray spaces, or rollNo.toUpperCase() so every roll number is stored the same way.
Do something extra on every change — log it, recompute a total, notify another part of the program. One place to change means one place to fix.
Provide a getter and no setter, making the field read-only from outside. Or a setter with no getter — write-only, like a password.
class Student{ private String rollNo; private int marks; public void setRollNo(String r) // NORMALISE { if (r == null || r.trim().isEmpty()) { System.out.println("Rejected: roll number cannot be blank"); return; } rollNo = r.trim().toUpperCase(); // cleaned, then stored } public void setMarks(int m) // VALIDATE { if (m < 0 || m > 100) { System.out.println("Rejected: " + m + " out of range"); return; } marks = m; } public String getRollNo() { return rollNo; } public int getMarks() { return marks; }}public class SetterJobs{ public static void main(String[] args) { Student s = new Student(); s.setRollNo(" 1602-24-733-045 "); // messy input s.setMarks(87); System.out.println("[" + s.getRollNo() + "]"); s.setMarks(-5); s.setRollNo(" "); System.out.println("Final marks: " + s.getMarks()); System.out.println("Final roll : [" + s.getRollNo() + "]"); }}" 1602-24-733-045 ", yet nothing printed — the setter cleaned it silently. Press 29 read it back: the spaces are gone. Press 30 and press 31 each attempted a bad write, and each produced a Rejected line the instant it revealed. Then presses 32 and 33 asked the object for its state, and it still holds the good values. Every output line is traceable to exactly one line of code.[ ]. A trailing space is invisible in terminal output. Wrapping the value in brackets is a standard debugging trick that makes whitespace visible — use it whenever you are testing string cleanup.
THE TWO LINES WORTH RE-READING
Line 6 — r == null || r.trim().isEmpty(). The order matters and it is not a style choice. If r is null, then r.trim() would crash with a NullPointerException. Java's || stops as soon as the left side is true, so the null check on the left shields the method call on the right. Swap them and the program crashes on null input. This is short-circuit evaluation from Unit 1 doing real safety work.
Line 13 — rollNo = r.trim().toUpperCase();. Note we do not store r. We store a cleaned version of it. The caller's messy string never enters the object. This is normalisation, and it is why every roll number in your database can be relied upon to look the same.
CARRY THIS FORWARD
Rejecting a bad value with a println is honest but weak — the calling code cannot tell that anything failed. The professional answer is to throw an exception, so a bad write cannot be ignored. That is IllegalArgumentException, and it is taught in the Exception Handling classes later in Unit 2. For now, print-and-return is exactly the right level: it keeps the guard visible without needing machinery you have not met.
CanteenCard — a balance that cannot go negative
Everything so far has been one field and one guard. Now the real thing: a class with a rule that must hold no matter what any other code does. You have a prepaid card for the college canteen. You can recharge it, you can spend from it, and the balance must never, ever fall below zero — because a card that owes money is not a card, it is a bug in the accounts.
First, the rules of the card — in plain words
Before writing code we write the rules. This is a habit worth keeping: a class that guards its data can only be written once you know exactly what “valid” means.
balance in rupees.THAT MUST
NEVER BREAKbalance is never negative. This is called an invariant — a statement about the object that is true the instant it is created and stays true after every single operation.
recharge(int amount) — adds money. Must refuse an amount that is zero or negative, because “recharging by −500” is really a secret withdrawal.spend(int amount) — removes money. Must refuse if the amount is not positive, and must refuse if the balance is smaller than the amount.getBalance() — anyone may look at the balance. Nobody may assign to it. There is deliberately no setBalance().NEW TERM · INVARIANT
Plain meaning: a rule about an object that is always true, at every moment the outside world could look at it.
Analogy: a lift has an invariant — the doors are never open while it is moving between floors. There is no instant you could catch it breaking that rule. The lift's controller enforces it, not the passenger.
Why the word matters here: notice we did not say “the setter checks for negatives”. We said “balance is never negative”. That is a promise about the whole class, and it forces a design decision: no setBalance() exists at all, because a method that assigns any value straight to the balance could break the promise no matter how carefully it checked. Instead we expose only the two operations that make business sense — recharge and spend.
THE DESIGN MISTAKE ALMOST EVERYONE MAKES HERE
Asked to encapsulate a card, most students write private int balance and then dutifully add getBalance() and setBalance(int b) — because that is the pattern they memorised. But setBalance(0) wipes a student's money, and setBalance(9999) gives away free lunches. Encapsulation is not “add a getter and setter for every field”. It is “expose only the operations that make sense, and no more”. Here, spending and recharging make sense; assigning a raw balance never does.
Now the class — one line at a time
Type this yourself. It is the longest program in this class and every line has a job. In LEARNING mode use Next line to walk through it.
class CanteenCard{ private String holder; private int balance; // INVARIANT: never negative CanteenCard(String holder) { this.holder = holder; this.balance = 0; // starts valid, not negative } public void recharge(int amount) { if (amount <= 0) { System.out.println("Recharge refused: amount must be positive"); return; } balance = balance + amount; System.out.println("Recharged " + amount + ". Balance = " + balance); } public void spend(int amount) { if (amount <= 0) { System.out.println("Spend refused: amount must be positive"); return; } if (balance < amount) { System.out.println("Spend refused: only " + balance + " left"); return; } balance = balance - amount; System.out.println("Spent " + amount + ". Balance = " + balance); } public int getBalance() { return balance; } public String getHolder() { return holder; } // NOTE: there is deliberately no setBalance()}public class CanteenCardDemo{ public static void main(String[] args) { CanteenCard c = new CanteenCard("Sneha"); c.recharge(500); c.spend(120); c.spend(1000); // more than the balance c.recharge(-500); // a withdrawal in disguise System.out.println("Final: " + c.getHolder() + " has " + c.getBalance()); }}this.holder = holder; on line 8. The constructor's parameter is called holder and so is the field. Inside the constructor, the bare name holder means the parameter, so holder = holder would assign the parameter to itself and leave the field null. this.holder forces Java to mean “the field of the object being built”. This is the this keyword from Unit 1 earning its place in the syllabus.
Why the spend guard is in that exact order
Look again at lines 25 and 30. There are two separate checks, and they are not interchangeable decoration — each one blocks a different attack.
c.spend(-200)
Without the first check, balance = balance - (-200) would add 200 to the card. A negative spend is a free recharge. Blocked by line 20.
c.spend(1000) when balance is 380
Without the second check, the balance would become −620 and the invariant would be dead. Blocked by line 24.
c.spend(380) when balance is 380
Spending the whole balance is legal — the card lands on exactly zero, which is not negative. Note the check is balance < amount, not balance <= amount; using <= here would wrongly block this valid case.
c.balance = 9999
Cannot even be compiled from outside the class. This is private doing the work no runtime check could do.
Two guards, one invariant — and a < where a <= would have been a bug.
WHAT THIS CLASS PROVES
There is no sequence of legal method calls that can put this card into a negative balance. Not a clever one, not an accidental one, not one written by a teammate who never read your class. That is a much stronger statement than “I remembered to check before subtracting” — and it is the real payoff of encapsulation. The rule lives inside the object, so it cannot be forgotten by anyone using it.
The immutable class — when the answer is “no setters at all”
The CanteenCard changes over its life: money in, money out. But some things should never change once created. Your roll number. A date of birth. A currency amount that has already been paid. For these, encapsulation goes all the way: a class where nothing can be modified after construction. Java calls this immutable, and you already use the most famous example every day — String.
The four-rule recipe
This is a recipe you should be able to write from memory, because PYQ P1·Q5 asks for exactly it. Four rules.
Make every field private and final. private stops outside code from reaching the field; final stops any code — including your own methods inside the class — from reassigning it after construction. Both are needed: private alone still lets an internal method change the value, and final alone still lets outsiders read and (if public) is merely read-only, not hidden.
Set every field once, in the constructor. This is the only moment a final field is allowed to be assigned. After the constructor finishes, the values are frozen.
Provide no setters. Getters only. Not a setter with a clever check — none. If a caller wants a different value, they create a new object.
Make the class final, and defensively copy any mutable field. Two subtle leaks that catch out even good students — explained just below, because they are what separate a 2-mark answer from a full one.
Rule 4, unpacked — the two ways an “immutable” class leaks
If your class is not final, another programmer can write class Fake extends YourClass and override your getters to return whatever they like — different values on each call. The object behaves mutably even though your fields are frozen. Marking the class final makes inheritance impossible and closes this door. This is exactly why String is declared public final class String.
final on a reference field freezes which object it points at — not that object's contents. If a field is an array or a list, returning it directly from a getter lets the caller modify your internals. The fix is a defensive copy: store a copy in the constructor, and return a copy from the getter.
A final REFERENCE PROTECTS THE ARROW · THE CONTENTS NEED A DEFENSIVE COPY
ArrayList, Date, or your own changeable classes. If every field is a primitive (int, double, boolean) or a String, there is nothing to copy, because those cannot be changed by anyone. String being immutable is precisely what makes it safe to hand out freely — the property we are building here is the same property you have been relying on since Unit 1.
Why anyone would want this
“A class you cannot change” sounds like a limitation. It is the opposite — it removes whole categories of bug:
Validate once in the constructor and the object is guaranteed correct for its entire life. No method can ever spoil it later, so you never have to re-check.
Hand the same object to ten different parts of your program with zero fear. Nobody can modify it behind your back, so there are no mysterious changes to hunt down.
Data that never changes cannot be corrupted by two threads writing at once. Immutable objects are automatically thread-safe — a point that pays off in the Multithreading classes later in Unit 2.
Keys in a HashMap must not change while stored, or they become unfindable. Immutable objects are the only completely safe keys — which is why String is the key type you see everywhere in Unit 4.
THE String PROOF — YOU HAVE SEEN THIS ALREADY
Run String s = "hello"; s.toUpperCase(); System.out.println(s); and you get hello, not HELLO. Students meet this as a puzzle in Unit 1; now you know the reason. toUpperCase() cannot modify s, because String is immutable — so it returns a brand-new String instead. To keep the result you must write s = s.toUpperCase();. That single behaviour is the whole of this Part, demonstrated by a class you have used since your first program.
Building an immutable StudentId
The recipe from Part 7, applied to something you carry in your pocket. A roll number is issued once and never changes — that is exactly the shape of an immutable class. Try it in your notebook first, then compare with mine.
YOUR TURN · 6 MIN Write a class StudentId that cannot be modified after creation.
Requirements:
- Two pieces of state: a
rollNo(String) and anadmissionYear(int). - Both set once, at creation time, and never changeable afterwards.
- The constructor must reject a blank roll number and any year before 2000 — print a message and store a safe fallback rather than crashing.
- Outside code can read both values but cannot modify either.
- Nobody may subclass
StudentIdand fake its behaviour.
Apply all four rules from Part 7. When you think you are done, ask the test question: is there any line of Java I could write outside this class that changes an existing StudentId? If you cannot find one, you have it.
Notebook first — six minutes of your own attempt is worth more than reading mine twice.
final class StudentId // RULE 4: cannot be extended{ private final String rollNo; // RULE 1 private final int admissionYear; // RULE 1 StudentId(String rollNo, int admissionYear) // RULE 2 { if (rollNo == null || rollNo.trim().isEmpty()) { System.out.println("Invalid roll number - using UNKNOWN"); this.rollNo = "UNKNOWN"; } else { this.rollNo = rollNo.trim().toUpperCase(); } if (admissionYear < 2000) { System.out.println("Invalid year - using 2000"); this.admissionYear = 2000; } else { this.admissionYear = admissionYear; } } public String getRollNo() { return rollNo; } public int getAdmissionYear() { return admissionYear; } // RULE 3: no setters exist. None. That is the point.}public class StudentIdDemo{ public static void main(String[] args) { StudentId a = new StudentId(" 1602-24-733-045 ", 2024); System.out.println(a.getRollNo() + " / " + a.getAdmissionYear()); StudentId b = new StudentId(" ", 1998); System.out.println(b.getRollNo() + " / " + b.getAdmissionYear()); }}new StudentId(" ", 1998) — produced two complaint lines at once, because both of that constructor's guards fired during that one object's construction. Press 30 then read the object back: UNKNOWN / 2000. Built from junk, it complained twice, and still came out a valid object — never half-built. Both are now frozen for life.if/else and not the print-and-return style from Part 5. A final field must be assigned exactly once on every path through the constructor. A guard clause that returns early would leave the field unassigned, and the compiler rejects that outright. So in an immutable class's constructor you use if/else to guarantee one assignment either way.
- RULE 1Lines 3–4: both fields are
private final. Hidden from outside, and frozen even from inside. - RULE 2Lines 6–26: every field assigned exactly once, inside the constructor, on every branch.
- RULE 3Line 37: no setters. To get a different roll number you must construct a new
StudentId. - RULE 4Line 1:
final classblocks subclassing. No defensive copy is needed here, becauseStringandintare already immutable. - BONUSLine 15 normalises the roll number, so
" 1602-24-733-045 "is stored clean. Validation and normalisation in one place, once, forever.
PROVE IT TO YOURSELF IN ECLIPSE
Type a.rollNo = "FAKE"; inside main. Eclipse underlines it in red before you even save: rollNo has private access in StudentId. Now try adding a setter inside the class — a public void setRollNo(String r) whose body assigns rollNo = r; → cannot assign a value to final variable rollNo. Two different walls, two different keywords — private stopped the outsider, final stopped you. Delete both lines afterwards.
“What is required to declare an immutable class?”
A two-mark question, which means it is a list question. You have just built the answer with your own hands in Part 8, so this is now only about compressing it into six or seven lines without losing a mark.
final, private, constructor, no setters.final fields and classes (Class 10) · private (Class 13) · why String qualifiesString) is a complete answer. If space remains, a three-line code sketch is a bonus, not a requirement.
THE MODEL ANSWER · AS IT SHOULD LOOK ON YOUR SHEET
Q5 What is required to declare an immutable class? Give an example. [2M]
An immutable class is one whose object state cannot be changed after it is created. To declare one:
1. Declare the class as final so it cannot be subclassed — a subclass could override methods and appear mutable.
2. Declare all fields private and final — private hides them, final prevents reassignment.
3. Initialise all fields only through the constructor.
4. Provide no setter methods — only getters.
5. For a field of a mutable type (array, collection), store and return a defensive copy so the caller cannot modify the internal object.
Example: Java's own String class — declared public final class String, all fields private and final, no setters. That is why s.toUpperCase() returns a new String instead of modifying s. Also Integer, Double and all wrapper classes.
Common mistakes & key takeaway
final on the class
The most-missed rule, because students focus on the fields. Without it, subclassing breaks immutability. Half a mark, routinely lost.
final” only
final without private leaves the value visible to everyone. Both keywords, every time.
The question explicitly says “give an example”. Writing String costs six letters and protects a full mark.
final on an array freezes its contents
It does not — see the Part 7 diagram. Mentioning the defensive copy marks you out as having actually understood the topic.
KEY TAKEAWAY
final class · private final fields · constructor-only initialisation · no setters · defensive copies. Five items, with String as the example. Recite that list and this question is two free marks every time it appears.
protected and final — benefit, risk, and when
Two different papers ask almost the same thing about the same two keywords, so we answer both here. One asks for the benefit and the risk; the other asks under what circumstances you would use them. Learn one set of material, collect eight marks.
First, get the two keywords straight
These are constantly confused, because both sound like “restriction”. They restrict completely different things.
| KEYWORD | WHAT IT RESTRICTS | ON A FIELD | ON A METHOD | ON A CLASS |
|---|---|---|---|---|
protected | Who can see it (visibility) | visible in the same package and in subclasses anywhere | subclasses may call and override it | NOT ALLOWED on a top-level class |
final | Whether it can change (modification) | value cannot be reassigned after initialisation | cannot be overridden by a subclass | cannot be extended at all |
protected answers “who is allowed in?”, final answers “can this be changed?”. They are independent, so a member can be protected final — visible to subclasses but unchangeable by them.
protected and final? What is the risk of not doing so?protected, benefit of final, risk without protected, risk without final.final (Class 10) · inheritance and overriding (Classes 10–11) · invariants (Part 6 today)MODEL ANSWER · Q11(b)
Q11(b) Benefit of declaring a member protected and final? Risk of not doing so? [4M]
BENEFIT of protected: it gives controlled visibility — the member is available to subclasses and to the same package, but hidden from unrelated outside code. A subclass can reuse a parent's member without that member being exposed publicly to the whole program.
BENEFIT of final: it guarantees the member cannot be changed. A final field cannot be reassigned and a final method cannot be overridden, so behaviour the parent class depends on stays intact. It also allows compiler optimisation and makes the class safe to share between threads.
RISK of not using protected (leaving the member public): any class anywhere can read and modify it, so the object's state can be corrupted silently and its invariants broken. Encapsulation is lost entirely.
RISK of not using final: a subclass can override a critical method and change behaviour the parent relies on, or a field can be reassigned unexpectedly, breaking correctness. An immutable design becomes impossible.
Together, protected final gives a member that subclasses may use but nobody may change — controlled access with guaranteed stability.
final or protected?MODEL ANSWER · Q16(a)
Q16(a) Under what circumstances would you declare a member final or protected? [4M]
Declare a member final when:
— the value is a constant that must never change, e.g. static final double PI = 3.14159; or a minimum pass mark.
— you are building an immutable class and every field must be frozen after construction.
— a method contains logic a subclass must not alter — a fee calculation, a security check — so overriding must be blocked.
— a class must not be extended at all, for safety or design reasons, as with String.
Declare a member protected when:
— a subclass legitimately needs the member but the outside world does not — e.g. a helper method used by every subclass of an abstract base class.
— you are designing a class meant to be inherited from, and want to share internals with child classes only.
— private would be too strict (subclasses cannot see it) but public would be too loose (everyone can).
Rule of thumb: use final for what must not change; use protected for what must be shared with children only. Default to private and widen access only when there is a reason.
ONE HONEST WARNING ABOUT protected FIELDS
Textbooks recommend protected for subclass access and exams expect that answer — so give it. But experienced Java programmers avoid protected fields in real code, because then any subclass anywhere can modify your state directly, re-opening the exact hole encapsulation closed. The professional habit is private fields with protected methods. Write the textbook answer in the exam; carry the better habit into your projects.
KEY TAKEAWAY FOR BOTH QUESTIONS
protected = who (visibility, shared with subclasses). final = whether (modification, frozen). Benefit questions want controlled access plus guaranteed stability; risk questions want silent corruption plus broken behaviour; circumstance questions want constants, immutability and inheritance-only sharing. One body of material, three question shapes.
One hour, one pillar — what you now own
We started with a card that could be broken by a single assignment, and finished with two classes that cannot be broken at all. Here is the whole journey on one page.
| PART | WHAT WE DID | THE ONE THING TO REMEMBER |
|---|---|---|
| 1–2 | Broke a public-field card with one line | A public field means anyone can put nonsense into your object |
| 3 | Fixed it with private + a validating setter | Encapsulation = bind data with its methods and hide the data |
| 4 | PYQ · the four pillars (4m) | Encapsulation, Inheritance, Polymorphism, Abstraction — all four, always |
| 5 | Getter/setter convention and its four jobs | The if inside the setter is the protection, not the setter itself |
| 6 | Built CanteenCard with an invariant | Expose the operations that make sense — not a setter per field |
| 7–8 | The immutable recipe, then StudentId | final class + private final fields + constructor only + no setters |
| 9–10 | PYQs · immutability (2m), protected/final (4m + 4m) | protected = who may see · final = whether it may change |
THE SENTENCE THAT HOLDS THE WHOLE CLASS
Make the data private, and make every way in go through a method that checks. That is encapsulation. Everything else today — getters, setters, invariants, final, immutability, defensive copies — is a variation on that one sentence.
FOUR EXAM QUESTIONS, NOW ANSWERABLE
P1·Q11(a) four pillars [4M] · P1·Q5 immutable class [2M] · P1·Q11(b) benefit/risk of protected and final [4M] · P1·Q16(a) when to use final/protected [4M]. That is 14 marks from this single hour — and each one has a model answer above, written out on a ruled sheet exactly as you should reproduce it.
Three tasks, about forty minutes
All three go in your Eclipse workspace under the JavaClass14 project. Do them before Class 15 — the interface work there assumes you are now comfortable with private fields and constructor validation.
TASK 1 · 10 MIN Break it, then fix it.
Write a Thermostat class with a public int temperature field. In main, set it to 500 and print it. Watch it accept a temperature that would melt the building.
Now make the field private, add getTemperature() and a setTemperature(int t) that accepts only 16–30 and prints a refusal otherwise. Re-run the same main and watch the 500 bounce. Write one line in your notebook explaining what changed — not in the field, but in who is in control.
TASK 2 · 15 MIN A BankAccount with an invariant.
Copy the shape of CanteenCard from Part 6, but for a bank account:
private String accountHolderandprivate double balance.deposit(double amt)— refuse zero or negative amounts.withdraw(double amt)— refuse non-positive amounts, and refuse ifbalance < amt.getBalance()only. NosetBalance().- Add a
private static final double MIN_BALANCE = 500;and refuse any withdrawal that would leave the balance below it.
Then try hard to break it from main: withdraw more than you have, deposit a negative, withdraw exactly to the minimum. The invariant to defend: the balance never falls below 500.
TASK 3 · 15 MIN An immutable Book.
Apply the four-rule recipe: final class Book with private final String title, private final String author, private final int year. Validate all three in the constructor using if/else (remember why it must be if/else and not an early return). Getters only.
Then, in main, deliberately try each of these and write down the exact Eclipse error message for each:
b.title = "New Title";- adding a
public void setTitle(String t)inside the class whose body assignstitle = t; - writing an empty
class EBook extends Bookin the same file
Three different errors, three different keywords doing three different jobs. Bring the messages to class.
spend method. The MIN_BALANCE rule is a third guard of the same shape — check balance - amt < MIN_BALANCE before subtracting, not after. The general habit: test the result before you commit it.
Where this goes next
Today you learned to lock a door. Next hour you use the same lock for a completely different purpose — a private constructor, which stops the outside world from creating objects at all. It sounds absurd until you meet the problem it solves.
CLASS 15 OF 60 · UNIT II · PART G
Abstract deepened · Singleton · nested classes
The campus Wi-Fi allows exactly one active login per roll number. How do you write a class that can only ever have one object? The answer reuses today's private in a way you would never guess — a private constructor plus a getInstance() method. We also deepen abstract from Class 11 into a working Reservation hierarchy, and meet the four kinds of nested class. PYQs P2·Q12(a) and P1·Q4 land there.
BRING YOUR THREE HOMEWORK FILES — WE REUSE Book AND BankAccount