Skip to main content
Object Oriented Design, Analysis and Programming

Constructors, Static Members, this, final and Software Development Life Cycle

Published: 2026-08-19
Level: postgraduate
Audience: Postgraduate students in Object Oriented Design, Analysis and Programming

Prerequisite Knowledge

This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.

Previously Covered in This Subject

  • Classes and Objects as Blueprints - covered in Lecture 16
  • Inheritance - One Class Acquiring Properties of Another - covered in Lecture 16
  • The Software Engineering Lifecycle and Development Models - covered in Lecture 1
  • The Software Development Process and Life Cycle - covered in Lecture 3

This lecture ties together how Java objects come to life and how they share data. It builds the full story of constructors — from the invisible default that the compiler supplies to the parameterized form that gathers initialization into the moment of creation — then contrasts class-level and instance-level members through static variables, methods and blocks, uses this to resolve identity inside an object, seals state with final to build immutable shapes, and finally places all of those building blocks inside the larger Software Development Life Cycle illustrated with IRCTC.

17.1 Constructors — Purpose, Rules and Automatic Invocation

17.1.1 Purpose and Basic Idea

Hook: How does a new object arrive already usable, without a separate "please initialize me" call that you might forget? What if the setup ran automatically the instant memory was reserved?

Every time you write new Account() Java does two things in order: it reserves memory for the new object and it immediately runs a special routine that prepares that memory. That routine is the constructor — a special member that runs automatically when an object is made to initialize the object.

Intuition — the hotel room setup: Think of a constructor like the housekeeping setup that happens the moment a guest checks in. The room (memory) is allocated first, then housekeeping (the constructor) enters automatically to make the beds, put towels out, and set defaults before the guest uses anything. You do not call housekeeping with a room-number dot call; check-in itself triggers it. A normal method like getData is different — it is like room service, which you must explicitly request with a1.getData() (object dot method). If you forget the dot call, room service never comes, but housekeeping always runs at check-in.

Where the analogy breaks: housekeeping can be called again later; a constructor runs only once, at birth, and you cannot re-run it on the same object with a dot call.

Formalize — what a constructor is and is not

  • An instance variable (also called a data member or field) is a variable that belongs to each object, such as int acc or String name.
  • A constructor is a block named exactly like its class, with no return type — not even void — whose body initializes the new object's fields. Syntax: ClassName(parameter-list) { // initialize fields }.
  • It is invoked implicitly by the new operator: ABC a1 = new ABC(); finds ABC() and executes it before new completes. You never write a1.ABC();.
  • A method such as int getData() { return 0; } has a return type and is invoked explicitly with the dot operator: a1.getData();.

Why no return type? A constructor implicitly returns the new object's reference, so declaring void or int would turn it into an ordinary method. The bare name-plus-parentheses is the signal to the compiler: "this is the initialization routine."

The creation line ABC a1 = new ABC(); therefore means: allocate an ABC object, run ABC() immediately, and store the resulting reference in a1. The dot call a1.getData() means: go to the object that a1 points to and run getData there.

17.1.2 Rules for Defining a Constructor

Rule 1 — name equals class: The constructor name must be exactly the same as its class, including case. Class ABC → constructor ABC(). Class Account → constructor Account(). Renaming it breaks the link and the compiler treats it as a method.

Rule 2 — no explicit return type: Write public ABC() { } or Account(int a) { } with nothing before the name. Do not write void ABC() { } or int Account() { }. Adding any type, even void, makes it a method with that name, not a constructor. Note that int getData() { return 0; } is fine as a method precisely because it declares int.

These two rules explain the bare look. The compiler recognizes the bare ClassName() form and wires it to new.

A quick sense-check: if you write void Account() { } inside class Account, you have not defined a constructor — you have defined a method called Account that happens to share the class name but must be called as a1.Account() and will never run automatically at new Account().

17.1.3 How a Constructor Is Called Compared with a Normal Method

Consider a class ABC that deliberately contains both:

class ABC {
    int getData() { return 0; }   // ordinary method: has return type
    public ABC() { }              // constructor: same name, no return type
}

Calling contrast — step by step

  • Method call: ABC a1 = new ABC(); first creates a1 (constructor runs). Then a1.getData(); explicitly calls getData on that object via dot. Without the second line getData never executes.
  • Constructor call: ABC a1 = new ABC(); — the ABC() part is the call. No a1. prefix, no second statement. The new operator looks for ABC() and transfers control into it before returning the reference. Writing a1.ABC(); is not valid Java for a constructor.

This automatic tie between new and ABC() is why a class with no constructor still allows new ABC() — the compiler supplies one, as seen next.

Scope: A constructor runs only at object creation, once per new. It cannot be called later to "re-initialize" the same object; to change state after birth you need a method. Also, a constructor has no return value you can capture — attempting int x = new ABC(); is a type mismatch; the expression after new yields an object reference, not an int.

Visual intuition: picture two timelines. For a method, time flows allocate → constructor runs → reference stored → ... later, dot call → method runs. For a constructor, the middle step is fused: allocate → constructor runs as part of new → reference stored. There is no separate arrow you must draw to invoke it.

Pitfalls:

  • Adding a return type by habit: Writing void ABC() { } compiles but is not a constructor, so new ABC() will find the compiler-supplied empty default instead, and your initialization code never runs.
  • Trying to call a constructor with a dot: a1.ABC() does not compile; constructors have no object to be dotted on yet at the point they run.
  • Forgetting that the dot is mandatory for methods: getData(); without a1. fails; unlike constructors, methods need an explicit receiver.

17.1.4 Worked Example — The Account Class Without an Explicit Constructor

Account class without explicit constructor printing default values 0 null 0.0 — full trace

Setup:

class Account {
    int acc;
    String name;
    float amount;
    void display() {
        System.out.println(acc + " " + name + " " + amount);
    }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account();
        a1.display();
    }
}

Steps:

  1. Class Account declares three instance variables: int acc, String name, float amount. No code looks like Account() { }, so no explicit constructor exists.
  2. The compiler silently inserts a default constructor — conceptually Account() { } with an empty body — so that new Account() has a target. Without this insertion new Account() would fail to resolve.
  3. Execution reaches Account a1 = new Account();. The runtime allocates memory for acc, name, amount, fills them with system defaults (int0, reference → null, float0.0), then calls the empty default constructor which does nothing further.
  4. a1.display(); reads the three fields through the dot on a1 and prints acc + " " + name + " " + amount.

Output — bolded final answer:

0 null 0.0

Sense-check: 0 is the defined default for int, null for any object reference such as String, 0.0 for float. Until you assign, those are exactly what any new Account shows. The empty constructor explains why the program compiles and runs despite having no visible Account().

Real-world connection: This invisible default mirrors any framework that creates objects before you fill them — for example, Java's serialization or an ORM that first constructs an empty Account and later populates fields from a database row. The guarantee that new Account() always succeeds, even with no code you wrote, is what makes those tools possible.

17.1.5 Student Questions and Answers

Q: How does the compiler-created default constructor behave? Does it do any initialization?

A: It has an empty body — conceptually Account() { } with nothing inside. Its only job is to let new Account() succeed without error. It does not assign your own values like acc = 10 or amount = 1000. The fields keep the system defaults — 0 for int acc, null for String name, 0.0 for float amount — until you assign otherwise, either in an explicit constructor or later via a method or direct assignment. That is why the Account class without explicit constructor prints 0 null 0.0. Several students asked this in the same form; the answer is the same each time: empty body, no custom initialization, defaults remain.

Recap: A constructor is the automatic setup routine named like its class with no return type, triggered implicitly by new. A method needs an explicit object.method() dot call. The compiler-supplied empty default is why Account a1 = new Account() works and why it prints 0 null 0.0 until you supply your own initialization.

Bridge: Next, we see how to take control of that default — writing it explicitly to give objects a more useful birth state than 0 null 0.0.

Exam note: Expect to distinguish constructor invocation (implicit via new ABC(), no dot) from method invocation (a1.getData() with dot), and to reproduce the 0 null 0.0 output for a class with no explicit constructor.

17.2 Default Constructors — Implicit and Explicit Forms

17.2.1 Implicit Default Constructor Created by the Compiler

Hook: If you write a class with no constructor at all, why does new Account() still compile and run?

When a class lists no constructor, the Java compiler quietly inserts one for you. You never see it in source, but at runtime new Account() finds it.

Implicit default — the invisible ABC() { }: For a class named ABC with zero constructors, the compiler adds conceptually:

ABC() { }
  • It takes no arguments.
  • Its body is empty — it executes no statements.
  • Its access is the same as the class default (package-private unless you wrote public class).

That is why notes say "it is not necessary to write a constructor" — the empty one makes new Account() succeed. The benefit is smooth, error-free creation even when you omit construction code. However, because its body is empty, fields keep the system defaults: int0, Stringnull, float0.0, booleanfalse.

In memory terms, new Account() still allocates slots for acc, name, amount and zero-fills them before calling the empty constructor, which adds nothing. The object is valid but not yet useful — like a hotel room with bare walls.

17.2.2 Explicit Default Constructor That Initializes State

You may replace that invisible empty body with your own no-argument constructor — also called an explicit default constructor — to give every new object a useful start state.

Explicit default constructor initializing amount to 1000 — full trace

class Account {
    int acc;
    String name;
    float amount;
    Account() {
        System.out.println("the default values are:");
        amount = 1000;
    }
    void display() {
        System.out.println(acc + " " + name + " " + amount);
    }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account();
        a1.display();
    }
}

Step trace:

  1. Account a1 = new Account(); allocates acc, name, amount as 0, null, 0.0.
  2. Control enters Account() — the explicit default. First line prints the default values are:.
  3. amount = 1000; overwrites the 0.0 default. acc and name stay 0 and null because they are not assigned.
  4. Constructor returns; a1.display() prints 0 null 1000.0.

You could just as well write acc = 1; name = "unknown"; amount = 1000; inside that constructor if you wanted a fully formed default. The constructor can contain any statements — printing, calculation, validation — not only assignments. The instant new Account() executes, that body runs.

Scope: This explicit Account() is used only when you call new Account() with no arguments. If you later add a parameterized form Account(int a, String n, float amt), the compiler stops supplying the implicit empty one (see 17.3.3). To keep both paths you must write both explicitly. Also, the explicit default does not magically initialize every field — any field you omit stays at its system default.

Visual: imagine two columns, "before constructor body" vs "after". Before: acc:0 | name:null | amount:0.0. After the body with amount=1000: acc:0 | name:null | amount:1000.0. Only the assigned slot changes; others are untouched.

17.2.3 Worked Example — Two Outcomes for Default Values

Compare two runs — 0 null 0.0 vs 0 null 1000.0

Case A — no explicit constructor (implicit empty default).

class Account { int acc; String name; float amount; void display() { ... } }
// Test: Account a1 = new Account(); a1.display();
  • Compiler supplies Account() { } empty.
  • a1.display() reads defaults.

Output A — bolded:

0 null 0.0

Why: int default 0, String default null, float default 0.0. No assignment has overwritten them.

Case B — explicit default that sets amount and prints.

Using the code in 17.2.2, Account a1 = new Account(); executes the body, then a1.display();.

Output B — bolded (two lines):

the default values are:
0 null 1000.0

Line 1 comes from inside the constructor (System.out.println). Line 2 comes from display. Now amount shows 1000.0 instead of 0.0, while acc and name remain 0 and null.

Sense-check: 1000 is exactly what the constructor assigned; if you changed it to amount = 500; you would see 0 null 500.0. This pair proves you can change any data member at construction time by assigning it there.

This contrast is the canonical examinable print pattern: examiners show 0 null 0.0 vs 0 null 1000.0 and ask which constructor ran.

17.2.4 Disadvantage and Behaviour When Body Is Empty

An empty default — whether compiler-supplied or written as Account() { } — has no harm: the program still runs. The empty body only means no custom setup happens.

The real limitation appears when a class grows a second constructor:

When the implicit default disappears: As soon as you add any parameterized constructor, the compiler stops supplying the empty one. Example:

class Account {
    int acc; String name; float amount;
    Account(int a, String n, float amt) { acc = a; name = n; amount = amt; }
}
// Now Account a1 = new Account();  // compile-time error — no no-arg constructor exists

To keep both creation styles you must write both:

Account() { }  // explicit default — now exists again
Account(int a, String n, float amt) { acc = a; name = n; amount = amt; } // parameterized

Now new Account() and new Account(111, "Asha", 5000) both compile. Forgetting the explicit Account() { } after adding the parameterized form is the single most common constructor error in beginner Java.

Pitfalls:

  • Assuming an empty constructor always exists: It exists only when you wrote none. After you write one, every form you need must be written explicitly.
  • Confusing "empty means no disadvantage": Empty is safe but may leave objects in a useless state (0 null 0.0) if you expected 1000. You choose between.empty convenience and explicit useful defaults.

17.2.5 Student Questions and Answers

Q: When will the value set by a default constructor be overwritten if there is a variable with the same name in a surrounding scope?

A: Assignment is assignment — later writes overwrite earlier ones. Up to entry into the constructor the field holds its default (0, null, 0.0). When the constructor executes amount = 1000; that default is overwritten, just as any subsequent a1.amount = 2000; would overwrite 1000. If a variable in another scope (a local or a static field) shares the same identifier amount, Java's normal scope rules decide which memory location you touch: amount without qualifier inside the constructor means the field of this object; a local float amount would hide it unless you qualify with this.amount. Within the object itself, the constructor's assignment cleanly replaces the default, and after construction the field stays 1000.0 until some other code changes it.

Real-world connection: Explicit default constructors model "sensible defaults" in products — for example, a BankAccount that starts with amount = 0 or 1000 minimum balance without requiring the caller to remember to set it. Teams use this to prevent objects ever being observed in an half-built state between new and a later init() call.

Recap: The compiler's invisible Account() { } guarantees new Account() compiles but leaves 0 null 0.0. An explicit default Account() { amount = 1000; } replaces that invisibility with useful work and prints the default values are: then 0 null 1000.0.

Bridge: If a default sets one fixed start state, a parameterized constructor lets each new object start with different values supplied at the call site — without a separate insert call.

Exam note: Be ready to produce 0 null 0.0 vs 0 null 1000.0 for empty vs initializing default, and to explain that the amount = 1000 line overwrites the 0.0 default exactly as any assignment does.

17.3 Parameterized Constructors and the Insert-Method Contrast

17.3.1 Definition and Motivation

Hook: Why create an empty account and then remember to fill it in the next line — when you could hand the values at the moment of creation and never see an empty shell?

A parameterized constructor — a constructor that receives parameters — lets you supply the initial values at the instant you write new, so initialization and creation are one atomic step.

Two broad types: Every Java class ultimately offers:

  • Default / no-argument constructorAccount() with no parameters, either compiler-supplied Account() { } or your explicit Account() { amount = 1000; }.
  • Parameterized constructorAccount(int a, String n, float amt) with one or more parameters that are copied into fields.

The parameterized form removes the risky gap where an object exists but has not yet been filled. The old gap is visible in the insert pattern below.

In everyday terms, the default is "build me the standard model" while the parameterized is "build me this exact custom order with these serial numbers handed over now."

17.3.2 Worked Example — Account with Three Parameters Versus insert Method

Account with three parameters versus insert method — two ways to reach 111 Asha 5000.0

Old style — separate method named insert (requires three statements):

class Account {
    int acc; String name; float amount;
    void insert(int a, String n, float amt) {
        acc = a; name = n; amount = amt;
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
// usage
Account a1 = new Account();          // step 1: object exists with 0 null 0.0
a1.insert(111, "Asha", 5000);       // step 2: explicit fill — easy to forget
a1.display();                        // step 3: now shows intended values

Trace: new Account() uses the implicit empty default → 0 null 0.0. The insert call then overwrites: acc = 111 (parameter a → field acc), name = "Asha", amount = 5000. display finally prints 111 Asha 5000.0. The commented-out insert lines in the lecture code did exactly those three assignments, but needed a separate dot call, leaving a window between lines 1 and 2 where a1 was in a half-built 0 null 0.0 state.

New style — parameterized constructor (requires two statements):

class Account {
    int acc; String name; float amount;
    Account(int a, String n, float amt) {
        acc = a; name = n; amount = amt;
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
// usage
Account a1 = new Account(111, "Asha", 5000); // creation + initialization fused
a1.display();

Trace: new Account(111, "Asha", 5000) allocates fields (0 null 0.0) then immediately enters Account(int a, String n, float amt) with arguments a=111, n="Asha", amt=5000. Inside, acc = a copies 111 into the field, etc. By the time new returns, the object already holds 111 Asha 5000.0. Only two statements were needed: create-with-values, display.

Output — bolded (both styles converge):

111 Asha 5000.0

Sense-check: Every value printed is exactly the argument you passed; changing the call to new Account(222, "Ravi", 3000) would print 222 Ravi 3000.0. The constructor guarantees the object is never observed as 0 null 0.0 between creation and use, which the insert pattern cannot guarantee.

This fusion is why textbooks call constructors "atomic initialization" — the object is born ready.

Scope: Use the parameterized form when each instance needs distinct start values supplied by the caller (account number, name, balance). Use the default when a single sensible start exists for all (e.g., amount = 0). For classes that need both flexibilities, provide both constructors explicitly (see next subsection).

Visual: draw a sequence diagram with two lifelines, Test and Account. Old style: Test → Account : new Account() creates empty, then Test → Account : insert(111,...) fills, then Test → Account : display(). New style: Test → Account : new Account(111,...) fills inside the creation arrow; the middle insert arrow disappears entirely. One fewer arrow is one fewer place to forget.

Pitfalls:

  • Leaving the insert window: Code that does Account a = new Account(); // forgot insert then passes a to another method circulates an object still at 0 null 0.0, leading to null-name bugs.
  • Copy-paste mismatch: The insert method and the constructor body look identical (acc = a; name = n; amount = amt;), so students copy one to the other but forget to change method name to class name or to remove void.

17.3.3 What Happens to the Default Constructor When a Parameterized One Exists

The rule — compiler stops supplying the default: As soon as you write any constructor, the invisible empty default disappears. The class offers only what you wrote.

class Account {
    int acc; String name; float amount;
    Account(int a, String n, float amt) { acc = a; name = n; amount = amt; }
}
// Account a1 = new Account();        // does NOT compile — no no-arg form exists
// Account a2 = new Account(111, "Asha", 5000); // compiles

Attempting new Account() with no arguments now produces a compile-time error: constructor Account in class Account cannot be applied to given types; required: int,String,float; found: no arguments. The compiler reports exactly which signature it expected.

Fix — write both explicitly:

class Account {
    int acc; String name; float amount;
    Account() { }                                     // explicit default — restores no-arg
    Account(int a, String n, float amt) { acc = a; name = n; amount = amt; } // parameterized
}
// Now both compile:
Account a1 = new Account();                         // uses Account()
Account a2 = new Account(111, "Asha", 5000);        // uses Account(int,String,float)

This overloading of constructors is normal; a class often provides several creation paths, and the argument list at new selects the matching one.

This rule was highlighted in the lecture as a "warning" because it surprises beginners: adding one constructor removes the other automatically.

Pitfalls:

  • Framework breakage: Many frameworks (like serialization tools that create empty beans) require a no-arg constructor. Adding a parameterized form without also adding Account() silently breaks them.
  • Thinking Account() still exists "somewhere": It does not remain hidden; it is gone unless you write it.

Real-world connection: In banking code, you might offer new Account() to open a zero-balance stub and new Account(acc,name,amount) to open a pre-funded account in one step. Both must be explicitly present to support both customer journeys.

17.3.4 Student Questions and Answers

Q: Can we keep both a default and a parameterized constructor together?

A: Yes — by defining both explicitly. The compiler only auto-supplies a default when no constructor exists at all. Once you write any constructor (for example the three-parameter Account(int a, String n, float amt)), you must write every form you want to use. So to keep both new Account() and new Account(111, "Asha", 5000), write both:

Account() { }
Account(int a, String n, float amt) { acc = a; name = n; amount = amt; }

This is called constructor overloading — same name, different parameter lists — and the call at new chooses the matching list. The compiler will not merge them or keep the old implicit default for you.

Recap: A parameterized constructor moves the acc = a; name = n; amount = amt; work from a separate insert call into the birth moment, saving a line and removing the half-initialized gap. The price is that the auto-supplied empty default vanishes — keep it by writing an explicit Account() { }.

Bridge: We have finished how each object is born. Next, how do objects share data — and when is sharing intended (class variables) versus isolation intended (instance variables)?

Exam note: Be ready to contrast three lines (new + insert + display) vs two lines (new with args + display), to reproduce 111 Asha 5000.0, and to explain why new Account() fails after a parameterized constructor is added unless an explicit default is kept.

17.4 Static Variables — Class Variables

17.4.1 Definition and Single-Copy Rule

Hook: What if ten thousand account objects all need to see the same bank name — do you really want ten thousand copies of that one string?

A static variable — also called a class variable — is any field declared with the static modifier, for example static int a; or static String myClassVar;. It belongs to the class as a whole, not to any single object.

Single-copy rule: A static field has exactly one memory slot per class loader, regardless of how many objects you create. Every object sees that same slot; writing through any path rewrites the one slot for all.

Contrast:

  • Instance (non-static) without static: int a; inside class ABC → each new ABC() gets its own a.
  • Static with static: static int a; inside ABC → one a lives on ABC itself; O1 and O2 both point at it.

Syntax landmarks: declaration static String myClassVar = "Java programming"; in class StaticWhereX; recommended read StaticWhereX.myClassVar (class-name dot field); write StaticWhereX.myClassVar = "Python programming";. The fact that you can reach it without any new proves it is class-owned.

Intuition — shared whiteboard vs personal notebook: Think of an instance variable as a personal notebook — each student has their own, writing in yours does not touch theirs. A static variable is a shared whiteboard at the front of the class — one board exists, every student sees the same writing, and erasing Java programming and writing Python programming on it is visible instantly to everyone. Housekeeping (initialization) of that whiteboard happens once when the classroom (class) is set up, not per student.

Where it breaks: personal notebooks travel with the student; the whiteboard stays with the room. You cannot take a static variable with a single object when that object is garbage collected — it outlives any one instance.

17.4.2 How a Static Variable Is Shared

Sharing mechanics with a and b

Imagine class ABC declares int a; int b; (non-static) and you create O1 = new ABC() and O2 = new ABC():

  • If non-static: O1.a = 10, O1.b = 20 and O2.a = 30, O2.b = 40. Reading O1.a yields 10; O2.a yields 30. They are independent instance copies — two separate boxes.
  • If static: static int a; static int b; → one slot for a, one for b on class ABC. Initialize that slot to a = 10, b = 20. Now O1.a and O2.a both read 10 because both dots reach the same slot. If you modify via one object, O2.a = 50, then O1.a also reads 50.

This is why static is called a class variable: modification through any path (O1, O2, or ABC) is visible through every other path because there is only one underlying box.

Picture memory: non-static draws two separate houses each with rooms a and b. Static draws one central vault labelled ABC.a and ABC.b with arrows from each house pointing into the vault.

Scope: Static variables live as long as the class is loaded (typically the whole program run). They are ideal for data truly shared by all instances — counters, configuration names, caches. They are poor for per-object state like individual amount balances, because a change for one customer would leak to all.

17.4.3 Worked Examples — Access by Class Name and by Objects

Example A — Static variable StaticWhereX myClassVar Java programming accessed by class name and overwritten to Python programming

Setup (no objects yet):

class StaticWhereX {
    static String myClassVar = "Java programming";
}
class Test {
    public static void main(String args[]) {
        System.out.println(StaticWhereX.myClassVar); // Java programming
        StaticWhereX.myClassVar = "Python programming";
        System.out.println(StaticWhereX.myClassVar); // Python programming
    }
}

Steps:

  1. Class loading: StaticWhereX loads, its static slot myClassVar is created and initialized to "Java programming". No new StaticWhereX() has run.
  2. StaticWhereX.myClassVar with class-name dot reads that slot → prints Java programming.
  3. StaticWhereX.myClassVar = "Python programming"; overwrites the same slot. No object is involved.
  4. Second read StaticWhereX.myClassVar → prints Python programming.

Bolded outputs in order: Java programming then Python programming. Demonstrates class-level ownership: the variable is reachable without any object.

Example B — Static variable shared through obj1 and obj2 both printing Java programming then both Python programming after obj2 modification

class StaticWhereX {
    static String myClassVar = "Java programming";
}
class Test {
    public static void main(String args[]) {
        StaticWhereX obj1 = new StaticWhereX();
        StaticWhereX obj2 = new StaticWhereX();
        System.out.println(obj1.myClassVar); // Java programming
        System.out.println(obj2.myClassVar); // Java programming
        obj2.myClassVar = "Python programming";
        System.out.println(obj1.myClassVar); // Python programming
        System.out.println(obj2.myClassVar); // Python programming
    }
}

Steps:

  1. Two objects obj1 and obj2 are created; each logically has a dot that points to the same central myClassVar slot already holding "Java programming".
  2. obj1.myClassVar prints Java programming; obj2.myClassVar prints the same Java programming — two reads, one slot behind both.
  3. obj2.myClassVar = "Python programming"; writes through obj2's dot but lands in the shared slot, overwriting it.
  4. obj1.myClassVar now prints Python programming (visible change though obj1 was not touched); obj2.myClassVar also prints Python programming.

Bolded sequence in order: Java programming, Java programming, Python programming, Python programming. The common-box diagram captures it: one box between obj1 and obj2; obj2's write is visible to obj1. Access via object is allowed but still reaches the class slot — the compiler even warns to prefer StaticWhereX.myClassVar.

Sense-check: If myClassVar were non-static, step 3 would affect only obj2 and obj1 would still show Java programming. The fact both flip proves shared storage.

Exam note: questions deliberately test that misconception. Remember the four-print pattern Java, Java → write via one object → Python, Python.

17.4.4 Cross-Class Access of a Static Variable

Cross-class static access ABC classVar read from XYZ via ABC.classVar without object

class ABC {
    static String classVar = "Java programming";
}
class XYZ {
    public static void main(String args[]) {
        System.out.println(ABC.classVar); // Java programming
    }
}

Steps: XYZ creates no ABC object. It writes ABC.classVar — the name of the owning class dot variable — and reads the shared slot directly. The cross access is reached from XYZ via ABC.classVar without creating an object or calling a constructor. If XYZ later did ABC.classVar = "Python programming"; every future read from any class would see "Python programming". This cross-class reach works because the static field lives on the class, not inside an instance.

This also explains why static variables are sometimes described as controlled globals: reachable anywhere by ClassName.field (subject to access modifiers) without passing an object reference.

Pitfalls:

  • Treating static as per-object: Expecting obj1.myClassVar = "Python" to affect only obj1 — it affects all. Bugs of this type cause one user's change of a shared config to appear for every user.
  • Reaching via object instead of class name: obj1.myClassVar compiles but hides intent; always write StaticWhereX.myClassVar or ABC.classVar to signal "I know this is class-level."
  • Unintended lifetime: A static collection that keeps growing (e.g., static List<Account> all) lives for the entire program and can leak memory.

17.4.5 Student Questions and Answers

Q: Can a static variable be made private? And by default is a variable instance (non-static) if we do not write static?

A: Yes to both. You can write private static String myClassVar; — it remains a single shared slot (one copy per class), but the private modifier restricts who can read or write it, just as for any private member: only code inside the same class (e.g., inside StaticWhereX) can reach myClassVar directly. Other classes would need a public getter/setter to touch it, but the single-slot semantics do not change.

And yes, if you write no modifier — just int acc; or String str; — the variable is an instance variable (non-static) with one copy per object. Several students asked this private-static combination; the answer repeats: private controls access, static controls sharing; they are orthogonal. So private static means single shared class variable that is only directly accessible inside its defining class.

Real-world connection: Static variables model truly shared configuration — for example, a university system where class UniversityConfig { static String currentSemester = "2026-Spring"; } is visible to every Course object, or a counter static int totalAccountsCreated that every new Account() increments so the class knows how many accounts exist. The Java programmingPython programming overwrite models changing an application-wide feature flag that every request sees immediately.

Recap: static means one copy per class, shared — the whiteboard. Class-name dot StaticWhereX.myClassVar is the intended access; object-dot obj1.myClassVar still hits the same whiteboard. Cross-class ABC.classVar from XYZ proves no object is needed.

Bridge: If static is sharing, the complementary picture is isolation — instance variables where each object carries its own notebook. Next we contrast the two directly.

Exam note: Reproduce the Java programming, Java programmingPython programming, Python programming shared-slot pattern, and recall that private static stays shared but restricts access, while a field with no modifier is instance by default.

17.5 Instance Variables and the Contrast with Static

17.5.1 Definition of Instance Variables

Hook: When two bank customers each have an amount, should changing one's balance ever change the other's?

An instance variable — also called a non-static variable — is any field declared without static, such as int acc; String name; String str;. It belongs to one instance, not to the class.

Per-object copies: For a class ABC that declares int a; int b; without static, the number of independent copies of a and b equals the number of objects you create.

  • ABC O1 = new ABC(); ABC O2 = new ABC();
  • O1 carries O1.a and O1.b; O2 carries O2.a and O2.b.
  • O1.a = 10 and O2.a = 30 coexist; reading O1.a never reveals 30. Their slots are separate boxes.

This is the opposite of the single-slot shared whiteboard for static; instance variables are personal notebooks — each object has its own pages.

If you create ten Account objects, you get ten acc values, ten name values, ten amount values, each diverging freely.

17.5.2 Worked Example — Independent Copies for obj1 and obj2

Instance variable str instance variable independent copies — obj1 remains instance variable while obj2 becomes change text

class InstanceWhere {
    String str = "instance variable";
}
class Test {
    public static void main(String args[]) {
        InstanceWhere obj1 = new InstanceWhere();
        InstanceWhere obj2 = new InstanceWhere();
        System.out.println(obj1.str); // instance variable
        System.out.println(obj2.str); // instance variable
        obj2.str = "change text";
        System.out.println(obj1.str); // instance variable
        System.out.println(obj2.str); // change text
    }
}

Steps:

  1. str is declared without static, so each new InstanceWhere() allocates its own str slot. Field initializer = "instance variable" runs per object, so both boxes start as "instance variable".
  2. obj1.str prints instance variable; obj2.str prints the same instance variable — two reads from two independent boxes that happen to hold equal strings.
  3. obj2.str = "change text"; writes only into obj2's box; obj1's box is untouched because they are separate allocations.
  4. obj1.str still prints instance variable; obj2.str now prints change text.

Bolded outputs in order: instance variable, instance variable, instance variable, change text. The third print staying instance variable (not becoming change text) proves independence — the direct opposite of the Java programmingPython programming shared pattern where both flipped.

The same numeric pattern: O1.a = 10 and O2.a = 30 coexist after O2.a = 30; reading O1.a still yields 10. No cross-object leakage occurs.

Sense-check: Change the assignment to obj1.str = "hello" instead — then obj1 would show hello while obj2 stayed instance variable. Each object's page is isolated.

Visual: draw two houses labelled obj1 and obj2, each with its own room str. Writing change text on obj2's room leaves obj1's room unchanged. For static, both houses point to one vault; here each house has its own vault.

17.5.3 Summary of Differences

Dimension Static / class variable Instance variable
Declaration static String s; with static String s; without static
Copies One copy per class, shared by all objects One copy per object, independent
Access style Ideally ClassName.field (e.g., StaticWhereX.myClassVar) even without any object; obj.field also reaches the shared slot but hides intent Only obj.field; no class-name access without an object
Effect of write Visible to every holder immediately (whiteboard) Visible only to that one object's holder (notebook)
Lifetime Lives with the class — from class load until program end Lives with the object — created at new, dies when object is garbage collected
Typical use Shared config, counters, caches, constants (static int totalCreated) Per-object state (acc, name, amount, individual str)

Scope: Choose static for data that truly belongs to the class as a whole — for example, static int totalAccounts that counts how many Account objects have ever been created, or static String bankName that is identical for every account. Choose instance for data that varies per object — acc number, name, amount balance, or each object's str description. Mixing them causes the classic bug where a per-customer field is mistakenly made static, so updating one customer silently overwrites all.

Real-world connection: The instance variablechange text independence models per-user data: each user's shopping cart, each sensor's reading, each bank account's balance must remain isolated. The earlier Java → Python shared pattern models application metadata: service name, feature flag, maximum login retries — one value governs all.

Pitfalls:

  • Forgetting default is instance: Omitting static does not mean "shared" — it means the opposite. Beginners sometimes expect a field to be shared without adding static.
  • Accessing instance via class name: InstanceWhere.str without an object does not compile — instance data needs a obj to locate.
  • Shadowing vs sharing confusion: Changing obj2.str and seeing obj1.str unchanged is correct; do not "fix" it by adding static just to make them match.

17.5.4 Student Questions and Answers

Q: Is a field instance by default?

A: Yes. If you write int a; or String str; with no static keyword, it is an instance variable with a separate copy per object. For example, String str = "instance variable" in InstanceWhere gives obj1 and obj2 independent str slots, so obj2.str = "change text" leaves obj1.str at instance variable. This is the mirror answer to the private static question — static must be written explicitly to get sharing; absence of static means per-object isolation.

Recap: Instance variables are per-object notebooks; static variables are a single shared whiteboard. The print instance variable, instance variable → write to obj2instance variable, change text proves independence.

Bridge: The same class-vs-object divide applies to behavior: static methods can touch only class data, while instance methods can touch per-object data — and method names themselves can be overloaded regardless of static.

Exam note: Be ready to reproduce the isolated-copy pattern (instance variable stays, change text only on obj2) and to state the table contrast: shared single copy with ClassName.field vs independent per-object copies with obj.field.

17.6 Static Methods, Overloading and Inheritance Interaction

17.6.1 What a Static Method Can and Cannot Access

Static method — class-level behavior: A static method (static void assign(...)) belongs to the class, not to any one object. It can read and write static variables directly because those variables also live on the class. It cannot directly read an instance (non-static) field — because that field lives inside a particular object which the static method has no reference to.

Concretely, inside class Main { static String s = "hi"; String t = "bye"; static void m() { s = "x"; // OK } }, s is reachable but t is not — which object's t? To touch instance data a static method would need an explicit object parameter: static void m(Main obj) { obj.t = "x"; // OK via reference }. Instance methods, by contrast, implicitly have this and can reach both static and instance data.

Two more restrictions from the companion text: a static method can only directly call other static methods, and it cannot use this or super (those refer to a current object / parent instance, which does not exist for a class-level call).

In short: static context knows the class whiteboard, not any student's notebook.

17.6.2 Overloading a Static Method

Overloading — one name, multiple parameter lists: Overloading means defining two or more methods with the same name but different parameter lists (different count or types). The compiler picks the version whose parameters match the arguments at the call site. Return type alone does not distinguish overloads.

You can overload a static method exactly as you overload any method. Combining static with overloading introduces no new rules — static decides "class-level", overloading decides "which parameter shape".

Example assign in class Main:

class Main {
    static void assign(int a) { /* use a — one value */ }
    static void assign(int a, int b) { /* use a and b — two values */ }
}

Both share the name assign: assign(int) vs assign(int,int). Call Main.assign(5) matches the first; Main.assign(5, 7) matches the second. Just as for static variables, the intended call style is via the class name: Main.assign(...) with no object.

Overloading is a compile-time choice (the compiler looks at argument types), unlike overriding which is a run-time choice through inheritance. Here we stay at compile-time.

17.6.3 Accessing Static Members Through the Class Name Even with Inheritance

Whenever you see static — variable or method — think class, not object. That mental model extends into inheritance:

class ParentOne { static void showSomething() { ... } static String s = "hi"; }
class ChildOne extends ParentOne { }

You reach the member by naming the owning class: ParentOne.showSomething() or ParentOne.s or even ChildOne.showSomething() / ChildOne.s — all without new. No object is needed on that access path, with or without an inheritance hierarchy.

This was the answer to "can a child's class name be used to access a static member?" — yes, by writing the class name dot member. The access goes through the class, so creating an instance is irrelevant. Instance-style access childObj.showSomething() is technically allowed but misleading; the class-name form is the intended style to signal class-level.

Scope: Static members are not subject to polymorphic dispatch. If both parent and child declare a static method with the same signature, the call ParentOne.showSomething() vs ChildOne.showSomething() is resolved at compile time by the class name you wrote, not at runtime by an object's type. Do not expect overriding behavior for static.

17.6.4 Worked Example — Main.assign Overloads

Static method overloading Main assign with one parameter and two parameters via Main.assign

class Main {
    static void assign(int a) {
        System.out.println("one: " + a);
    }
    static void assign(int a, int b) {
        System.out.println("two: " + a + " " + b);
    }
    public static void main(String args[]) {
        Main.assign(10);      // calls assign(int a) — one-arg form
        Main.assign(10, 20);  // calls assign(int a, int b) — two-arg form
    }
}

Steps:

  1. Main.assign(10) — compiler searches for assign with one int. Finds assign(int a) and generates a call to it. Output one: 10.
  2. Main.assign(10, 20) — searches for two int params. Finds assign(int,int) and calls it. Output two: 10 20.

The two forms are independent; the one-param version might store a single static config value, the two-param version might store a pair. No extra qualification is needed because both are static and owned by Main.

Bolded outputs in order: one: 10 then two: 10 20 (or any analogous prints showing the matched overload). The pattern proves that static + overloading behaves like normal overloading — parameter count selects the method, and the class name selects the owner.

Sense-check: Changing the call to Main.assign(10, 20, 30) would fail to compile — no three-arg assign exists — exactly as for instance overloads.

Visual: a small dispatch table inside Main: key assign(1-int) → first body, key assign(2-int) → second body. The lookup uses argument count at compile time.

17.6.5 Student Questions and Answers

Q: In inheritance, can a static variable be accessed by the child's class name?

A: Yes — you access any static member by writing the name of the class you want to reach, followed by a dot and the member name. Example: ABC.classVar from class XYZ reads the static variable owned by ABC as ABC.classVar with no object — demonstrated in 17.4.4. The same holds for static methods: Main.assign(10) or ParentOne.showSomething() is the direct form. Whether you write ParentOne.s or ChildOne.s in an inheritance tree, you are naming a class and reaching its static slot without creating an instance. Instance-style access obj.s is allowed but obscures that class-level nature, so the class-name dot is the preferred answer examiners expect.

Real-world connection: static overloaded helpers model utility functions — for example, Logger.log(String msg) vs Logger.log(String msg, Throwable t) — class-level tools you call without constructing a logger object, with the parameter list selecting severity details. Inheritance access like Config.DEFAULT reachable via AppConfig.DEFAULT models shared constants inherited by sub-modules.

Recap: Static methods live on the class, reach only static data, cannot use this, and are called as ClassName.method(). Overloading them (for example Main.assign) works exactly like any overload — argument count/types pick the body — and static members remain class-reachable even through inheritance.

Bridge: If static members live with the class, when does the class itself come to life to run its setup? That is the job of the static block.

Exam note: Remember the restriction (static cannot directly read instance fields), the Main.assign(10) vs Main.assign(10,20) overload selection, and that class-name dot is the expected access for any static member, child or parent.

17.7 Static Blocks

17.7.1 Definition and Execution Time

Static block — class-level startup code: A static block is a free-standing block inside a class written as:

static {
    // statements
}

The word static plus braces marks it as belonging to the class, not to any object. It runs automatically when the class is loaded into memory — which happens before any object is created and before any static field is read or static method is called from another class. Think of it as the class's own constructor, executed once per class load.

Common uses: initializing a static variable with logic that needs more than a one-line initializer (for example computing static int b = a * 4; or opening a configuration file), printing diagnostics, or registering a driver.

In lifecycle order for a class abc: class load → static blocks and field initializers run → main continues. No new abc() is required to trigger that.

17.7.2 Multiple Static Blocks and Order

A class may contain more than one static block. When it does, they execute in exactly the order they appear in source, top to bottom, interwoven with static field initializers.

class Demo {
    static { System.out.println("first"); }
    static int x = 1;
    static { System.out.println("second: x=" + x); }
}

Accessing Demo.x would print first then second: x=1. This guarantee lets you stage initialization across separate blocks for different concerns, though one block is most common.

Scope: A static block runs exactly once per class load, not per object. Creating ten new abc() objects still runs abc's static block only the first time the class is referenced. If you need per-object setup, use a constructor, not a static block.

Visual: a timeline with a vertical marker "class abc loaded". To the left of the marker nothing has run; to the right the block's inside static block print fires, then i = 20 assignment completes, then abc.i read returns 20.

17.7.3 Worked Example — Class abc with static int i = 20

Static block in class abc printing inside static block and initializing i to 20 accessed as abc.i — execution order

class abc {
    static int i;
    static {
        System.out.println("inside static block");
        i = 20;
    }
}
class xyz {
    public static void main(String args[]) {
        System.out.println(abc.i);
    }
}

Steps:

  1. xyz.main starts; its first mention of abc.i forces the runtime to load class abc (if it was not already loaded).
  2. At class-load time, before the field read completes, the static block executes: it prints inside static block and assigns i = 20 to the class-level slot. Field static int i; without initializer had started as 0; now it becomes 20.
  3. After the block finishes, the read abc.i completes and returns 20 to System.out.println.
  4. Output order on two lines:

Bolded outputs in order:

inside static block
20

First line originates inside the block during loading; second line originates from println(abc.i) which now sees 20. If a second static block existed after the first, its statements would run immediately after i = 20, preserving source order, before the read.

Sense-check: Move System.out.println(abc.i) to be executed twice — you would still see inside static block only once, at the first access, proving "once per class load."

Extension used in companion text (UseStatic): static int a = 3; static int b; static { b = a * 4; } prints Static block initialized. then b becomes 12. Same pattern: block computes a static field after simple initialization.

Pitfalls:

  • Expecting per-instance run: Adding new abc(); new abc(); after abc.i will not re-print inside static block — newcomers often insert logic there that they expected to run per object.
  • Accessing instance fields inside static: this.i or non-static int x; inside static { } does not compile — no current object exists at class-load time.
  • Order surprises with interleaved initializers: static int i = 5; static { i = i*2; } yields 10, but reversing the order changes the result; fields are initialized top-to-bottom.

Real-world connection: Static blocks model service bootstrapping — for example, a DatabaseConfig class whose static { loadProperties(); } reads db.properties once at startup and sets static String url, static int poolSize for every future new Connection() to reuse without re-reading the file.

17.7.4 Student Questions and Answers

No direct student Q&A was logged for static blocks beyond explanation of load-time execution, but the pattern mirrors static variables and methods: the block belongs to the class, not to any instance, so it runs once per class load (see Scope above). Students often ask whether a static block needs an object — the answer is no; referencing abc.i alone triggers it, as demonstrated in the abc/xyz example.

Recap: static { ... } is class startup tied to loading, runs once before any new or ClassName.field use, respects source order across multiple blocks, and is the place to compute complex static initial state like i = 20 proven by the print inside static block before 20.

Bridge: We have completed the family of class-level members (static variables, methods, blocks). Next we return to per-object identity — how an object refers to itself while it executes.

Exam note: Be ready to reproduce the two-line order inside static block then 20, to note that abc.i needs no object, and to explain "once per class, not per object."

17.8 The this Keyword — Reference to the Current Object

17.8.1 Core Idea — Current Object Among O1 O2 O3

Hook: When three objects O1, O2, O3 all share the same code for display(), how does that one code know which object's acc to print right now?

this — a reference variable that always points to the current object — the single instance that is actively executing at this moment.

Current-object identity: If you create ABC O1 = new ABC(); ABC O2 = new ABC(); ABC O3 = new ABC(); and you call O1.getData(), then during that call this means O1. If you instead call O2.getData(), then inside that call this means O2. At any time only one instance is active per call, and this is the generic name for that active instance without hardcoding O1 or O2.

In implementation terms, when Java enters a method, it implicitly passes the receiver object as a hidden first argument bound to this. Inside the method body this.acc is precisely the acc field of that receiver.

Intuition — "me" pronoun: Think of this as the pronoun "me / myself" that a person uses. When Asha says "my name is Asha" and Ravi says "my name is Ravi", the word "my" means a different person in each utterance, but the grammar is the same. this.name is "my name" for whichever Account is currently speaking (O1, O2, O3). Outside the object you use a fixed name a1.name; inside you use the self-pronoun this.name because you do not know which of the many accounts will be the speaker until runtime.

Where it breaks: unlike English, this cannot be used in a static context — there is no "speaker" when no object is active.

That one idea — self-reference — powers all five usages listed next.

17.8.2 The Five Usages

The lecture lists five distinct purposes for this:

  1. refer to the current class instance variable (solve shadowing),
  2. invoke the current class method (this.display()),
  3. invoke the current class constructor (this(...) chaining),
  4. be passed as an argument in a method call (display(this)),
  5. be used to return the current class instance from a method (return this;).

Each is illustrated below with Account variants that reuse the same three fields int acc; String name; float amount;.

17.8.3 Usage 1 — Refer to Current Class Instance Variable and Solve Shadowing

this keyword fixing shadowing with this.acc equals acc this.name this.amount versus 0 null 0.0 output

Failure case — shadowing hides the field:

class Account {
    int acc; String name; float amount;
    Account(int acc, String name, float amount) {
        acc = acc;
        name = name;
        amount = amount;
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account(832345, "Asha", 5000);
        a1.display(); // prints 0 null 0.0
    }
}

Why 0 null 0.0? Parameter names acc, name, amount hide the fields of the same names (shadowing). Inside the constructor, every bare acc resolves to the parameter, not the field. acc = acc assigns the parameter to itself; the field this.acc is never touched and stays at 0 null 0.0.

Fix 1 — rename parameters (quick but changes call-site vocabulary): Account(int a, String n, float am) { acc = a; name = n; amount = am; }

Fix 2 — keep names, qualify fields with this (preferred):

Account(int acc, String name, float amount) {
    this.acc = acc;
    this.name = name;
    this.amount = amount;
}

Now this.acc means the field of the current object (a1.acc when a1 is being constructed) while bare acc on the right means the parameter. Values flow parameter → field. After this fix new Account(832345, "Asha", 5000) followed by display() prints:

Bolded corrected output:

832345 Asha 5000.0

instead of 0 null 0.0. This is the textbook shadowing resolution taught with this. The companion Box example shows the same: Box(double width, ...) { this.width = width; } to resolve width shadowing.

Scope: Use this.field only inside non-static (instance) contexts where a current object exists. It is unnecessary when names differ (e.g., acc = a needs no this), but harmless; it is mandatory when names collide.

Visual: two layers — parameter box on top (acc = 832345), field box below (this.acc initially 0). Arrow from top to bottom labeled this.acc = acc shows the copy that the bare acc = acc missed.

Pitfalls:

  • Assigning the wrong direction: acc = this.acc; copies field (still 0) into parameter, not the reverse — order matters.
  • Thinking this creates a new object: It does not; this is an alias for the already-allocated object that new created.

17.8.4 Usage 2 — Invoke Current Class Method

this to invoke current class method — this.display() inside insert

class Account {
    int acc; String name; float amount;
    void insert(int acc, String name, float amount) {
        this.acc = acc; this.name = name; this.amount = amount;
        this.display();
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account();
        a1.insert(111, "Asha", 5000);
    }
}

Execution: a1.insert(111, "Asha", 5000) enters insert with this == a1. After the three this.field assignments, this.display() means a1.display() because this is a1 at that moment. No separate a1.display() appears in main. Output:

Bolded:

111 Asha 5000.0

Rule: this.display() inside a method invoked by a1 is exactly a1.display(). For O2 it would be O2.display(). The self-call avoids hardcoding the variable name inside the class — the class works for any caller.

17.8.5 Usage 3 — Invoke Current Class Constructor and Constructor Chaining

Constructor chaining with this acc name calling two-parameter constructor from three-parameter constructor

class Account {
    int acc; String name; float amount;
    Account(int acc, String name) {
        this.acc = acc; this.name = name;
    }
    Account(int acc, String name, float amount) {
        this(acc, name); // calls the two-parameter form
        this.amount = amount;
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account(111, "Asha", 5000);
        a1.display(); // 111 Asha 5000.0
    }
}

Steps: new Account(111, "Asha", 5000) enters the three-parameter constructor. First line this(acc, name) is a special this(...) call that invokes the sibling two-parameter constructor Account(int acc, String name) with the same acc and name. That delegate body runs and sets this.acc and this.name. Control returns to the caller, and this.amount = amount; finishes the third field. No repetition of this.acc = acc; this.name = name; is needed.

Constraints: this(...) must be the very first statement in the constructor body, and you cannot mix this(...) and super(...) in the same constructor.

Bolded output: 111 Asha 5000.0. Delegation ensures consistent handling of shared fields regardless of which overload is used.

17.8.6 Usage 4 — Pass this as an Argument in a Method Call

Passing this as argument display this for a1 Ankit and a2 Showbith and updated a1 — full trace

class Account {
    int acc; String name; float amount;
    Account(int acc, String name) {
        this.acc = acc; this.name = name;
        display(this);
    }
    void update(int acc, String name, float amount) {
        this.acc = acc; this.name = name; this.amount = amount;
        display(this);
    }
    void display(Account obj) {
        System.out.println(obj.acc + " " + obj.name + " " + obj.amount);
    }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account(832345, "Ankit");
        Account a2 = new Account(832346, "Showbith");
        a1.update(999, "AnkitUpdated", 7000);
    }
}

Flow:

  1. new Account(832345, "Ankit") creates a1 (832345, "Ankit", 0.0) and calls display(this) where this is a1. display receives that Account as obj and prints obj's fields → 832345 Ankit 0.0.
  2. Second construction new Account(832346, "Showbith") does the same for a2832346 Showbith 0.0.
  3. a1.update(999, "AnkitUpdated", 7000) runs on a1, overwrites its three fields, then calls display(this) again — still a1 but with updated values → 999 AnkitUpdated 7000.0.

Bolded outputs in order:

832345 Ankit 0.0
832346 Showbith 0.0
999 AnkitUpdated 7000.0

This matches the walkthrough where the constructor displays each new object and the update displays the modified a1. Passing this hands the whole current identity to another routine, useful for callbacks and double-dispatch patterns.

17.8.7 Usage 5 — Return the Current Instance from a Method

Returning this from update method Account update return this and a1 equals a1.update — chaining pattern

class Account {
    int acc; String name; float amount;
    Account(int acc, String name) {
        this.acc = acc; this.name = name;
    }
    Account update(int acc, String name, float amount) {
        this.acc = acc; this.name = name; this.amount = amount;
        return this;
    }
    void display() { System.out.println(acc + " " + name + " " + amount); }
}
class TestAccount {
    public static void main(String args[]) {
        Account a1 = new Account(111, "Asha");
        a1.display(); // 111 Asha 0.0
        a1 = a1.update(222, "Asha2", 9000);
        a1.display(); // 222 Asha2 9000.0
    }
}

Steps: new Account(111, "Asha") creates a1 with amount still 0.0; first display111 Asha 0.0. a1.update(222, "Asha2", 9000) runs on a1, updates its three fields, and executes return this; which evaluates to the same object reference a1. The assignment a1 = a1.update(...) stores that returned reference back into a1 (here it points to the same object; the pattern matters when chaining calls like a1.update(...).display() or fluent APIs). Second display222 Asha2 9000.0.

This pattern enables method chaining: return this lets calls be strung together because each call yields the current object for the next dot.

Bolded outputs in order: 111 Asha 0.0 then 222 Asha2 9000.0.

Pitfalls across all five usages:

  • Using this in a static method or block: static void m() { this.x = 1; } does not compile — no current object exists.
  • this(...) not first: this(acc,name) must be statement 1; placing a print before it fails.
  • Confusing this with "the class": this is per-instance, not per-class; for class data use ClassName.staticField.

17.8.8 Student Questions and Answers

No separate doubt was logged for this beyond the shadowing demonstration, but the progression from quick-fix renaming (acc = a) to keeping names with this.acc = acc was presented as the canonical answer to "how do I keep parameter names identical without losing values?" That question recurs as: when parameters hide fields and you see 0 null 0.0, the fix is this.field = parameter. Several students traced the same confusion across the five usages; the answer consolidates to: qualify the field side with this, keep the bare name for the local/parameter side.

Real-world connection: Fluent APIs (like builder.withX(1).withY(2).build() where each withX does this.x = x; return this;) and callback registration (service.register(this)) both depend on passing or returning this as a live handle to the current object.

Recap: this is the running object's alias — "me" for O1 when O1 is active. It fixes shadowing (this.acc = acc), calls sibling code (this.display()), chains constructors (this(acc,name)), passes identity (display(this)), and enables chaining (return this).

Bridge: If this is about identity that can be shared or returned, final is about preventing change once identity is established — sealing the state.

Exam note: Be ready to diagnose 0 null 0.0 as shadowing and fix with this.acc = acc, to place this(acc,name) as first line for chaining, and to trace display(this) outputs 832345 Ankit 0.0, 832346 Showbith 0.0, 999 AnkitUpdated 7000.0 and return this producing 111 Asha 0.0222 Asha2 9000.0.

17.9 The final Keyword and Immutable Classes

17.9.1 final with Variables — Constant Variables

final variable = constant: Writing final before a variable makes it a constant — its value, assigned once, cannot be changed. Example final int a = 10; means a holds 10 forever; any later a = 20; fails at compile time.

Rules:

  • A final field must be assigned exactly once, either at declaration (final int FILE_NEW = 1;) or inside every constructor (final int acc; Account(int v){ this.acc = v; }). After the constructor finishes, the slot is frozen.
  • The convention is ALL_CAPS naming: FILE_NEW, FILE_OPEN etc., as shown in the companion text where final int FILE_OPEN = 2; final int FILE_SAVE = 3; are used as symbolic constants for menu actions.

The restriction is the point: final communicates "this value is fixed for the lifetime of this holder."

17.9.2 final with Methods — Prevent Overriding

Writing final before a method (final void show() { }) means that method cannot be overridden in a child class. A subclass class B extends A { void show() { } } attempting to provide a new body for a final show() in A triggers a compile error: cannot override ... overridden method is final.

The inheritance detail of overriding will be covered later, but the effect now is: the parent's method definition stays bound as written, no child can redefine it. The companion text notes a side effect: because the compiler knows a final method will never be overridden, it can inline small final calls for speed, resolving them at compile time (early binding) rather than runtime (late binding).

17.9.3 final with Classes — Prevent Inheritance

Writing final before a class (final class Account { }) means the class cannot be inherited. class B extends Account { } does not compile when Account is final — the class is sealed against extension, and there is no way to make a child that extends it or inherits its members.

Three orthogonal uses — remember as variable / method / class:

  • final variable → value cannot change,
  • final method → body cannot be overridden,
  • final class → class cannot be extended (implicitly final methods as well).

Together final is the restriction tool: lock a value, lock a behavior, or lock a whole hierarchy. You choose granularity per need.

Scope: Use final for variables when the value is truly constant (math constants, configuration). Use final for classes/methods when you must guarantee behavior is not altered by subclasses (security-sensitive code, immutable shapes). Declaring a class final implicitly makes all its methods final, but not its fields — you still need final on each field to freeze values.

17.9.4 Building an Immutable Class

An immutable class — a class whose instances cannot be changed after creation; a read-only shape whose state, once born, lives unchanged — is built by combining final in three places and by controlling how values enter and leave:

Recipe for immutability in Java (Account example):

  • Class is finalfinal class Account so nobody can create a subclass that adds a setter and breaks immutability.
  • Every data member is finalfinal int acc; final String name; final float amount; so each field cannot be reassigned after construction.
  • Parameterized constructor does the single allowed writeAccount(int acc, String name, float amount) { this.acc = acc; ... } is the one moment final fields may be set.
  • Getter for each fieldint getAcc() { return acc; } etc., exposes values for reading.
  • No setter → no method like setAmount(...) exists, so there is no intended path to mutate after birth.

Because there is no setter and final blocks direct assignment (a.amount = 1000 fails), the object is readable but not writable — the same birth state persists for its entire lifetime. The word immutable simply means unchangeable; this pattern is the mechanical way to achieve it. Note that getter-only plus final fields is the core; the final class prevents a subclass from sneaking in a setter later.

In plain terms: construction is the only window to write; after that the object is like a sealed letter — you can read it through the window (getters), but you cannot rewrite a line.

Pitfalls — shallow immutability: final String name freezes the reference, not necessarily the object it points to if it were mutable. With trusted primitives and String (itself immutable) this recipe is sufficient; for mutable types you would also need defensive copies in getters/constructors, but that extension is beyond this lecture's Account case.

17.9.5 Worked Example — Final Account with Getters Only

Immutable final class Account with final fields getters only — construction and read

final class Account {
    final int acc;
    final String name;
    final float amount;
    Account(int acc, String name, float amount) {
        this.acc = acc;
        this.name = name;
        this.amount = amount;
    }
    int getAcc() { return acc; }
    String getName() { return name; }
    float getAmount() { return amount; }
}

class Test {
    public static void main(String args[]) {
        Account a = new Account(111, "Asha", 5000);
        System.out.println(a.getAcc() + " " + a.getName() + " " + a.getAmount());
    }
}

Trace:

  1. final class Account blocks inheritance — class Sub extends Account would not compile.
  2. final int acc; etc., start unassigned; they will be assigned exactly once in the constructor. The final modifier tells the compiler to enforce single assignment.
  3. new Account(111, "Asha", 5000) allocates fields, enters constructor, executes this.acc = acc etc. — the sole legal write to each final field. After constructor returns, acc=111, name="Asha", amount=5000.0.
  4. Three getters getAcc, getName, getAmount return those frozen values.
  5. Test.main creates a and prints via getters:

Bolded output:

111 Asha 5000.0

No setter exists, so the only way to give the object its state is the construction call. Any later attempt to mutate must go through a blocked path.

This also demonstrates why this.acc = acc appears again: shadowing fix and final-field initialization share the same syntax — qualifying the field side with this.

17.9.6 Attempted Modification and Compiler Error

Attempted modification and compiler error the final field Account.amount cannot be assigned

If you add after construction a line that tries to change a final field:

a.amount = 1000;

the compiler rejects it while amount remains final. The lecture reported message similar to:

exception in thread "main" java.lang.Error: unresolved compilation problem:
  the final field Account.amount cannot be assigned

(The exact wording varies by compiler/IDE — some say cannot assign a value to final variable amount — but the key phrase final field ... cannot be assigned is the searchable signal.)

Steps to reproduce:

  1. Keep final float amount; and the getters-only class as above.
  2. After Account a = new Account(111, "Asha", 5000); write a.amount = 1000; or add a setter void setAmount(float v){ amount = v; } — both fail because amount is already assigned in the constructor.
  3. Removing final from the field declaration would let that assignment compile and run, silently breaking immutability. Keeping final preserves the read-only guarantee.

The same fence blocks indirect mutation through a setter, because none is provided by design. If you added one, the assignment inside it would also fail for the same reason — a final field cannot be reassigned.

Pitfalls:

  • Forgetting a field is final: Attempting to update an immutable object's state in place instead of creating a new object (e.g., a = new Account(a.getAcc(), a.getName(), 1000) is the correct replacement).
  • Assuming getter protects mutable objects: Returning a reference to a mutable field directly would let callers change internal state even without a setter; with int/float/String the risk is absent, but with arrays or collections you would need to return a copy.

17.9.7 Student Questions and Answers

No explicit Q&A was recorded on final beyond the statement that a full inheritance example of final methods and classes will appear later, but the pattern final class + final fields + constructor + getters-only was presented as the direct answer to "how do I make an unchangeable class?" Students often ask whether final on a variable means "must assign at declaration" — the answer is no: for fields, either declaration (final int a = 10;) or constructor assignment is legal; what is forbidden is second assignment.

Real-world connection: Immutable Account models safe value objects in finance — once a transaction record is created with amount 5000, immutability guarantees no later code can tamper with it, mirroring how String is immutable (String objects cannot be altered after creation; StringBuffer/StringBuilder are the mutable peers) and how java.lang.String, numeric wrappers, and LocalDate are all implemented as final immutable classes with final fields plus getters.

Recap: final locks at three granularities: variable (constant final int a = 10;), method (cannot override), class (final class Account cannot extend). Immutability composes those plus getters-only and a construction-time this.field = param write to yield a read-only object whose fields cannot be reassigned — failure proves as the final field Account.amount cannot be assigned.

Bridge: Individual objects can now be sealed. The remaining question is process: how do we organize the work of building many such objects into a complete system?

Exam note: Expect short code-output checks for final (identifier of the compiler error), and recognition that final method blocking of override and final class blocking of inheritance are deferred deeper treatment until the inheritance lecture.

17.10 Software Development Life Cycle and the IRCTC Illustration

17.10.1 What the Life Cycle Is and Why a Process Is Needed

Hook: A client asks for "a website like IRCTC" — why can't the team just open editors and start typing?

Software Development Life Cycle, often shortened to SDLC — also referred to in this session as STLC (Software Testing Life Cycle overlap) — is the process that represents all steps required to make a working software product, from initial idea through planning, building, testing, releasing, and maintaining.

When a client asks a team to solve a problem with code, immediate typing fails for practical reasons: without a clear plan, team representatives do not know when to do what, work duplicates, requirements clash, and integration becomes chaos. SDLC divides the work into parts, assigns roles (analyst, designer, developer, tester, deploy engineer), and sequences activities so that analysis informs design, design informs coding, and so on, culminating in a product that matches customer needs.

Core purpose: SDLC is the organizing process — not a single method but the set of phases (requirement analysis, defining, designing, coding, testing, deployment, maintenance) that together move a client's problem statement to a deployed, maintained solution, with feedback loops between them.

In the lecture's wording, the cycle "delineates" (defines and sequences) those steps before any program is written, providing building blocks for the project.

17.10.2 Phases — Requirement Analysis, Defining, Designing, Coding, Testing, Deployment, Maintenance

Phases shown on the slide and named in lecture order are:

  1. Requirement analysis — gather what the system must do.
  2. Defining — clarify scope, feasibility, definitions of done.
  3. Designing — choose architecture, data stores, auth, APIs.
  4. Coding (and developing) — implement the design.
  5. Testing — verify correctness, performance, usability.
  6. Deployment — release to production, installation, training.
  7. Maintenance — fix defects, add features, support users.

The cycle spans start before development, middle during development, and end after delivery.

Scope — what this course focuses on now: For coding-oriented work at this stage, only requirement analysis, designing, and coding and developing are treated as in-focus for exam and discussion. The defining phase, testing, deployment, and maintenance were acknowledged as important but explicitly set aside for later in the course. Do not expect detailed marking on deployment pipelines or maintenance models yet; know their names and positions, but focus study on the first three.

The classic contrast raised in companion material (Larman Chapter 2) is iterative/evolutionary SDLC (short fixed-length iterations, each with its own requirements → design → implementation → testing, successive enlargement) versus the sequential waterfall (attempt to define all requirements then all design before programming) — the latter is strongly associated with higher failure when applied to large, changing systems.

17.10.3 Requirement Analysis and Planning

First step — built from multiple inputs: Requirement analysis and planning forms the building block of the basic project — the blueprint of what the software must do. It is gathered from:

  • Customers / clients who state the business problem,
  • Sales department who relay market demand and contracts,
  • Market surveys that quantify user needs at scale,
  • Domain experts who know the field (railways, banking) and its edge cases.

Before code, the team contacts the client, talks to sales, surveys the market, consults experts. All available information shapes the initial project description — what features exist, what performance is required, what constraints apply. Skipping this produces a system that technically runs but solves the wrong problem.

In the lecture's phrasing, "before development" means feasibility and needs are settled; only then does design begin.

17.10.4 Designing — Choosing Architecture

Best architecture selection: In the designing phase, software designers devise the best architecture for the software — where deep technical trade-offs are made before a line of product code is committed.

Questions that must be decided here:

  • Large database: If analysis reveals a need to store huge amounts of data (millions of tickets, user records, train schedules), do you stay with a traditional relational store such as Oracle or MySQL, or move to distributed/big-data options such as cloud storage, Hadoop or MapReduce, which trade higher scalability for higher operational complexity? Weigh reliability, query optimization, size, latency, and scalability.
  • Many users / high load: How will peak booking spikes be optimized and scaled? What caching, replication, or sharding is needed?
  • Heavy data volume: How is data modeled, partitioned, and indexed so station A → station B searches stay fast?
  • Division of duties: Once the architecture is tentatively fixed, responsibilities are split among team members — database, authentication, payment-API, train-state tracking, UI — so each piece can be built in parallel.

The chosen architecture becomes the skeleton that coders fill in during the next phase; changing it after half the code is written is far costlier than deciding right now.

A vivid takeaway: the same requirement set (IRCTC) can lead to radically different designs (single Oracle instance vs Hadoop cluster on cloud) with different cost, latency, and failure modes — hence the design decision must be explicit and reviewed.

17.10.5 Coding and Developing

Coding and developing is where actual development begins and the program is built — the implementation of the design begins, concerning writing code once the problem is defined, requirements are analyzed, and architecture is defined.

Flow described: once the platform is ready (database provisioned, authentication service selected, API contracts drafted), software engineers engage in full-fledged development — a cycle of discussion with teammates and writing code that implements the assigned slice on the chosen architecture. All separately written pieces are later integrated into a single working system, which then proceeds to testing, deployment, and maintenance in later phases not emphasized now.

Convergence with companion concepts: this maps to "Implementation & Test & Integration" within each iteration of an iterative SDLC, where the system "grows incrementally" over time rather than appearing all at once.

Scope: At this stage, "coding" means fulfilling requirement analysis and designing decisions; it does not yet include detailed testing strategies or deployment scripting — those follow later.

17.10.6 Worked Illustration — Designing a System Like IRCTC

IRCTC illustration — database Oracle MySQL versus Hadoop MapReduce and Kerberos and Paytm Google Pay UPI and real-time train updates

Context: IRCTC — the Indian Railway Catering and Tourism Corporation ticket booking platform used across the country — was used as the concrete, end-to-end model to show how planning feeds design and design feeds who codes what.

Requirements discovered for a system like IRCTC (from customers, domain experts, market surveys):

  • A database in the background that must store huge amounts of data (users, trains, bookings, payments, history).
  • Optimization for peak load (festival season spikes, Tatkal windows).
  • Customer roles with distinct permissions: admin, regular user, ticket agent, railway staff, ticket-counter staff.
  • An application programming interface layer for payment merchants.
  • Broad train details: which intermediate stations a train visits, timings, seat maps, live location.

Designing for those requirements — requirement by requirement, weighing platform choices:

  • For large database: Designers weigh traditional relational Oracle or MySQL versus distributed options Hadoop or MapReduce or a cloud-native store. Criteria: expected data size, query patterns (transactional bookings need strong consistency; analytics on travel patterns tolerates eventual consistency), latency for a seat-availability check, scalability for thousands of concurrent transactions. Example decision: Oracle for strongly consistent ticket inventory plus Hadoop/MapReduce for nightly analytics.
  • For customer roles / authentication: Different actors (railway employees, admin staff, counter staff, end customers) need different access levels. Designers decide authentication and access-control mechanisms; a high-level platform such as Kerberos or a similar role-aware login system was mentioned as a design option for strong, centralized authentication and ticket inspection permissions.
  • For APIs / payments: The system must collaborate with many payment partners — Paytm, Google Pay, other UPI handling (Unified Payments Interface), credit cards, debit cards, internet banking and other merchants — so the API design must be compatible with all those choices today. A third-party merchant integration layer (often a payment gateway abstraction) is designed so new wallets can be added without rewriting core booking logic. In textbook terms this is the interface and integration design within architecture.
  • For train details — real-time updates: Design must support updates as a train moves from station A to station B through many intermediate stations. Servers used by intermediate station masters and staff must see live positions, delays, and berth availability, which drives choices about data modeling (train as entity with ordered station list), replication (push vs poll), and distribution (edge caches at stations). The same data powers passenger apps and internal control panels; consistency vs freshness is the trade-off.

From decision to coding: Once choices are fixed — say "Oracle for bookings, Hadoop for analytics, Kerberos for auth, gateway for Paytm/Google Pay/UPI, streaming replication for live location" — tasks are assigned: one subteam codes database schema and queries, another builds authentication, another integrates merchant APIs, another handles train-state tracking. Separately developed pieces are integrated into one complete IRCTC-like product, then tested, deployed, and maintained.

Sense-check: If requirement analysis missed that intermediate stations need write access (not only read), the station-update replication design would be wrong and require rework — illustrating why the earlier phases matter before coding.

This illustration shows concretely how requirement analysis (huge data, roles, payments, live data), designing (Oracle vs Hadoop/MapReduce, Kerberos, gateway, replication), and coding (split by those design seams) compose one coherent life cycle.

Pitfalls:

  • Skipping requirement sources: Designing only from the client's first statement misses sales, market, and domain input → wrong scale choices (e.g., MySQL single instance for what needs Hadoop).
  • Choosing DB on brand rather than trade-off: Oracle and Hadoop solve different problems; choice should cite scalability, reliability, query optimization — not familiarity alone.
  • Treating deployment as free: In iterative terms, early demos with stakeholders surface "yes… but" feedback that reshapes requirements long before UPI or Kerberos wiring is finalized.

Real-world connection: Any large consumer service (airline reservation, e-commerce checkout with Paytm/Google Pay/UPI, or a ride-sharing platform tracking vehicles station-to-station) follows the same SDLC: survey → architect for peak load and role access → code on chosen cloud/Hadoop/relational mix → integrate → test/deploy/maintain. The IRCTC specifics (Oracle vs Hadoop, Kerberos for staff, Paytm/Google Pay) are the lecture's chosen technology labels for those generic decisions.

17.10.7 Student Questions and Answers

No direct student Q&A was logged for the life-cycle illustration beyond the closing administrative note that slides or materials would be made available through the portal. Students often ask whether to memorize every phase equally — the guidance in 17.10.2 answers: at this stage weight requirement analysis, designing, and coding; treat testing, deployment, maintenance, and the detailed "defining" activity as acknowledged but secondary.

Recap: SDLC (also called STLC in its testing view) organizes work into requirement analysis (inputs: customers, sales, market surveys, experts) → defining → designing (best architecture: Oracle/MySQL vs cloud/Hadoop/MapReduce, Kerberos for roles, gateway for Paytm/Google Pay/UPI, replication for station updates) → coding (discuss, code, integrate) → testing → deployment → maintenance. IRCTC binds all steps to one product.

Bridge: That closes the loop from language mechanics (constructors, static, this, final) to process mechanics (how those mechanics are planned, architected, coded, and integrated into a system users actually book tickets on).

Exam note: Know the phase order by name, the three focus phases (requirement analysis, designing, coding), and for IRCTC be able to contrast Oracle/MySQL vs Hadoop/MapReduce, name Kerberos as the authentication example, and list Paytm, Google Pay, UPI plus cards as the payment-API examples with real-time station-to-station updates as the live-data design driver.

Exam Guidance Summary

No explicit mark distribution, question pattern or textbook chapter list was laid out for this session; the lecturer gave scope signals instead, and those signals shape the safest revision targets.

First, of the full Software Development Life Cycle (requirement analysis, defining, designing, coding, testing, deployment, maintenance), only requirement analysis, designing, and coding and developing are in focus for the coding-oriented portion of this course at this stage; testing, deployment, maintenance and the defining phase were called not important to discuss right now. Expect questions to name the full list but to ask in detail only about the first three.

Second, the final keyword behavior for methods (cannot be overridden) and for classes (cannot be inherited / cannot use extends) will be covered in a more detailed, inheritance-driven way later — treat that as a deferred exam detail rather than an immediate expectation; at this stage be able to state the restriction and reproduce the compiler error the final field Account.amount cannot be assigned.

Third, emphasis markers throughout the session point to likely short-answer or code-output checks that were demonstrated line by line:

  • Constructors vs methods: Constructor invocation is implicit via new ABC() (no dot), while method invocation is explicit via a1.getData() with dot. Writing void ABC(){} is a method, not a constructor.
  • Default values: Account with no explicit constructor prints 0 null 0.0; with explicit default that sets amount = 1000 it prints first the default values are: then 0 null 1000.0. The amount = 1000 overwrites the 0.0 default exactly as any later assignment would.
  • Default disappearance: When a parameterized constructor Account(int a, String n, float amt) exists, no-arg new Account() fails unless you keep an explicit Account() { }. Both forms together (Account() {} plus Account(int,String,float)) are constructor overloading.
  • Parameterized vs insert: Three lines (new + insert + display) fuse into two (new with args + display) and both print 111 Asha 5000.0; the constructor removes the half-initialized gap where an object lingers at 0 null 0.0.
  • Static variables: Shared single slot — Java programming, Java programming for both objects, then after obj2.myClassVar = "Python programming" the pair becomes Python programming, Python programming. Class-name access ABC.classVar or StaticWhereX.myClassVar works without any object and is the preferred style; cross-class ABC.classVar from XYZ proves class ownership. private static remains a single shared slot but restricts access to its own class; a field with no modifier is instance by default.
  • Instance variables: Independent copies — instance variable, instance variable then after obj2.str = "change text" the pair is instance variable, change text for obj1, obj2 respectively, the opposite of the static pattern.
  • Static methods and overloading: A static method can directly access only static data, cannot use this or directly read instance fields, and is called as Main.assign(10) / Main.assign(10,20) — the assign overloads differ by parameter count and work identically to instance overloads; in inheritance static is reached by ClassName.member without an object.
  • Static blocks: static { System.out.println("inside static block"); i = 20; } runs once at class load, before any object or abc.i read, producing inside static block then 20 on two lines; multiple blocks run in source order, once per class.
  • this: Five usages — fix shadowing (this.acc = acc turning 0 null 0.0 into 832345 Asha 5000.0), call current method (this.display()), chain constructors (this(acc,name) as first line), pass identity (display(this) yielding 832345 Ankit 0.0, 832346 Showbith 0.0, 999 AnkitUpdated 7000.0), and return identity (return this yielding 111 Asha 0.0 then 222 Asha2 9000.0). this cannot appear in static context.
  • final and immutability: final class Account with final fields plus constructor this.field = param plus getters only and no setters yields an immutable object; a.amount = 1000 after construction fails with the final field Account.amount cannot be assigned.
  • SDLC/IRCTC: Phases in order: requirement analysis (inputs: customers, sales, market surveys, domain experts), defining, designing (choices: Oracle/MySQL vs cloud/Hadoop/MapReduce for huge data, Kerberos for role-aware authentication, gateway for Paytm/Google Pay/UPI plus cards, replication for real-time station A → B updates), coding (discuss, code slices, integrate), testing, deployment, maintenance; focus for now is the first three. IRCTC was the model product tying survey through database, roles, payments, train updates to integration.

Key Industry Applications

IRCTC as the end-to-end model ticket-booking product: the cycle runs from market survey, sales and domain-expert inputs through requirement analysis, through design choices (database, authentication, merchant APIs, live station updates), through split development and integration, to testing, deployment and maintenance — illustrating how planning and design feed who codes what in a nationally used system.

A system that must store huge amounts of data forces an explicit platform choice between traditional relational stores such as Oracle or MySQL (transactional, single-instance, strong consistency for ticket inventory) and distributed big-data stacks such as cloud, Hadoop or MapReduce (scale-out, analytics, handling peak booking spikes), with trade-offs in scalability, reliability and query optimization weighed in the designing phase before platform commitment and task assignment.

Payment integration illustrating APIs: Paytm, Google Pay, UPI handling, credit cards, debit cards, internet banking and other merchants all collaborate through a third-party merchant / payment-gateway integration layer that must be compatible with all current options — a direct example of the API and integration design decisions required when required analysis reveals multiple external partners.

Role-aware authentication for distinct actors — admin, regular user, ticket agent, railway staff and ticket-counter staff — with high-level platforms such as Kerberos mentioned as a concrete design option for strong, role-aware, centrally managed login and access control, motivating the separation of authentication as a distinct design block before coding.

Distributed, real-time update of train movement from station A through many intermediate stations to station B, where station masters and control servers need live access to the same service — motivating replication, streaming or edge-caching, and careful data modeling of station order and timings so that every intermediate point sees consistent, fresh status.

The Java programming to Python programming string overwrite on a static field models any shared application configuration — for example, a single application name, feature flag, or currentSemester held once in a class variable and instantly visible to every object without per-instance duplication.

The instance variable versus change text example models per-object or per-user state that must stay isolated across instances — such as individual account balances (amount), user names, shopping carts, or per-sensor readings where each object carries its own copy that diverges independently; choosing static here would incorrectly share one balance across all accounts.

OODAP Lecture 17 notes · Constructors, Static Members, this, final and Software Development Life Cycle

Object Oriented Design, Analysis and Programming Architecture· postgraduate· 2026-08-19

Sections Breakdown

1Constructors — Purpose, Rules and Automatic Invocation

Constructors initialize objects automatically at new, named like the class with no return type; the compiler supplies an empty default causing 0 null 0.0.

2Default Constructors — Implicit and Explicit Forms

Implicit empty default ABC() { } vs explicit no-arg that sets amount to 1000, producing 0 null 0.0 vs 0 null 1000.0; implicit vanishes when any constructor is written.

3Parameterized Constructors and the Insert-Method Contrast

Parameterized constructor fuses creation and initialization (new Account(111,Asha,5000) → 111 Asha 5000.0) versus three-step insert; adding it removes implicit default unless both are written.

4Static Variables — Class Variables

Static variables are single-copy class variables accessed via ClassName.field; StaticWhereX.myClassVar shows Java programming → Python programming shared via both objects and cross-class ABC.classVar.

5Instance Variables and the Contrast with Static

Instance variables are per-object copies without static; InstanceWhere str shows instance variable staying for obj1 while obj2 becomes change text, opposite of static shared pattern.

6Static Methods, Overloading and Inheritance Interaction

Static methods belong to class, access only static data, cannot use this; Main.assign overloaded with one and two params via Main.assign; static reachable by class name even in inheritance.

7Static Blocks

Static block static { } runs once at class load before any object or field access, printing inside static block then 20 for abc.i; multiple blocks run in source order.

8The this Keyword — Reference to the Current Object

this refers to current object O1/O2/O3; five uses: fix shadowing this.acc=acc (0 null 0.0 → 832345 Asha 5000.0), call method, chain constructor this(acc,name), pass display(this), return this.

9The final Keyword and Immutable Classes

final on variable constant, method cannot override, class cannot inherit; immutable final class Account with final fields, constructor plus getters only gives read-only 111 Asha 5000.0 and error final field cannot be assigned on mutation.

10Software Development Life Cycle and the IRCTC Illustration

SDLC phases requirement analysis, defining, designing, coding, testing, deployment, maintenance with focus on first three now; IRCTC shows Oracle/MySQL vs Hadoop/MapReduce, Kerberos, Paytm/Google Pay/UPI and real-time station updates driving design before coding.

11Exam Guidance Summary

Scope signals for exam: focus requirement analysis/designing/coding, deferred final inheritance details, and code-output patterns for constructors, static, this, immutable error, IRCTC.

12Key Industry Applications

IRCTC full system, huge-data DB choice Oracle/MySQL vs Hadoop/MapReduce, Paytm Google Pay UPI gateway, Kerberos auth, live station replication, and shared vs per-object variable modeling.

Postgraduate students in Object Oriented Design, Analysis and Programming

Exam Revision Notes

Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.

Constructors — Purpose, Rules and Automatic Invocation

Must-know: Constructor runs implicitly at new ClassName(), method needs obj.method() dot; empty default yields 0 null 0.0

Top pitfall: Writing void ABC() makes a method not a constructor; trying a1.ABC() fails

Self-check: What prints for Account without explicit constructor and a1.display()?

Connects to: 17.2, 17.3

Default Constructors — Implicit and Explicit Forms

Must-know: Empty default leaves 0 null 0.0; explicit default amount=1000 yields 0 null 1000.0; later assignment overwrites defaults

Top pitfall: Assuming empty default always exists after adding a parameterized constructor

Self-check: What is output Case B with explicit default printing the default values are then display?

Connects to: 17.1, 17.3

Parameterized Constructors and the Insert-Method Contrast

Must-know: Parameterized saves a line and avoids half-initialized gap; compiler stops supplying default when any constructor exists — must write Account() explicitly to keep both

Top pitfall: Writing only parameterized then trying new Account() with no args causes compile error

Self-check: How many statements for insert style vs parameterized style to print 111 Asha 5000.0?

Connects to: 17.2, 17.4

Static Variables — Class Variables

Must-know: One copy per class shared; class-name dot StaticWhereX.myClassVar is preferred; obj2 write visible to obj1 → Java,Java then Python,Python; cross-class ABC.classVar needs no object

Top pitfall: Thinking each object has its own static copy; trying to share per-object data with static

Self-check: What prints for obj1.myClassVar and obj2.myClassVar after obj2.myClassVar=Python programming?

Connects to: 17.5, 17.6

Instance Variables and the Contrast with Static

Must-know: Without static each object has independent copy; obj2.str=change text leaves obj1 at instance variable

Top pitfall: Forgetting default is instance; accessing instance via ClassName without object

Self-check: What prints for obj1.str vs obj2.str after obj2 change in instance example?

Connects to: 17.4, 17.6

Static Methods, Overloading and Inheritance Interaction

Must-know: Static method uses ClassName.method(), only static data, overloading Main.assign(10) vs Main.assign(10,20) picks by args; static via inheritance still class-name without object

Top pitfall: Directly reading instance field inside static; expecting runtime polymorphism for static

Self-check: Which Main.assign overload matches Main.assign(10,20)?

Connects to: 17.4, 17.7

Static Blocks

Must-know: Static block runs on class load, once per class; abc.i sequence is inside static block then 20; needs no new

Top pitfall: Expecting static block per object or using this/instance inside it

Self-check: What prints first when xyz reads abc.i?

Connects to: 17.6, 17.8

The this Keyword — Reference to the Current Object

Must-know: this resolves shadowing; this(acc,name) must be first line for chaining; display(this) prints 832345 Ankit 0.0 then 832346 Showbith 0.0 then 999 AnkitUpdated 7000.0; return this yields fluent update

Top pitfall: Using this in static context; reversing assignment direction acc=this.acc; this(...) not first

Self-check: How to fix constructor that prints 0 null 0.0 when parameters hide fields?

Connects to: 17.9, 17.3

The final Keyword and Immutable Classes

Must-know: final variable=constant, final method=no override, final class=no extends; immutable = final class + final fields + constructor + getters only; mutation error final field cannot be assigned

Top pitfall: Trying to assign a.amount=1000 after construction or forgetting final class still needs final fields

Self-check: What error when assigning to final amount after construction?

Connects to: 17.8, 17.10

Software Development Life Cycle and the IRCTC Illustration

Must-know: SDLC order and focus phases requirement analysis, designing, coding; IRCTC inputs from customers/sales/market/experts; DB choice Oracle/MySQL vs Hadoop/MapReduce; Kerberos for roles; Paytm Google Pay UPI gateway; live train replication

Top pitfall: Choosing DB by brand not trade-off; skipping domain expert input

Self-check: Which SDLC phases are in focus for coding-oriented exam at this stage?

Connects to: 17.9

Was this lecture useful?

Loading comments…
🤖

BitsNotes AI Assistant

Subject Notes Assistant

Configure AI Chat

Choose how to access the chatbot
Have your own API key?

Switch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.

🔑 Enter API key above to fetch live models from provider, or enter model name manually.
OpenAI-Compatible API Support

Choose any provider preset (Gemini, DeepSeek, Kimi, GLM, MiniMax, Qwen, OpenAI, Groq, Ollama, etc.) or enter a custom endpoint URL.

Security & Privacy First

Your API key is sent directly from your browser to your specified provider. BitsNotes servers never store or see your key.