Unit 1 · Lesson 518 minAcademic review pending

A class is a blueprint. An object is a house.

A struct groups data. A class groups data with the methods allowed to touch it, and a constructor guarantees every object starts in a valid state — a guarantee C's structs never made.

Choose explanation

After this lesson

You should be able to

  • Define a class with private fields and public methods, and create objects from it.
  • Write a constructor that guarantees every object starts valid.
  • Explain what problem the this keyword solves.
01

From struct to class: one extra rule

A C struct for a student bundles roll number, name, and marks together, but any function anywhere can read or overwrite those fields directly — the struct itself enforces nothing. A Java class bundles the same data, but adds private, a keyword that makes a field invisible outside the class. Code outside the class cannot even see student.marks exists, let alone assign it a value of -50.

The only way to reach that data from outside is through methods the class chooses to expose — public methods, deliberately designed as the one legal doorway in. This is encapsulation from the earlier lesson, made real: the class controls its own data completely, and the rest of the program can only ask, never reach in directly.

A field no outside code can touch directly
public class Student {
    private int rollNumber;
    private String name;
    private double marks;

    public double getMarks() {
        return marks;
    }

    public void setMarks(double newMarks) {
        if (newMarks >= 0 && newMarks <= 100) {
            marks = newMarks;
        }
    }
}
02

A constructor guarantees a starting state

Recall from CS105ES: a declared variable does not start at zero — it holds whatever garbage was left in that memory until you assign to it. The same danger applies to an object's fields, unless something forces every field to a sensible value the moment the object is created. That something is a constructor: a special method, with the same name as the class, that Java calls automatically every single time an object is built with new.

A constructor that requires a name and starting marks makes it structurally impossible to create a Student with no name at all — the compiler will not let you call new Student() if no such constructor exists. This is the single biggest reason classes exist: not just organising data, but making entire categories of bugs unrepresentable in the first place.

Every object born valid, by construction
public class Student {
    private int rollNumber;
    private String name;
    private double marks;

    public Student(int rollNumber, String name) {
        this.rollNumber = rollNumber;
        this.name = name;
        this.marks = 0.0;   // every Student starts with a real, known value
    }
}

// Student s = new Student(101, "Kavya");   // valid — always starts consistent
// Student s = new Student();               // does not compile: no such constructor
03

this: telling apart a field from a parameter with the same name

Inside the constructor above, name appears twice with two different meanings: the parameter that was passed in, and the field that belongs to the object. Java resolves this the same way C resolved a local variable shadowing an outer one — the closer name wins, so inside that constructor, plain name always refers to the parameter, never the field.

this.name means specifically the field belonging to this particular object, cutting through that ambiguity on purpose. You do not need this when there is no naming clash, but reaching for it whenever a constructor or method parameter shares a field's name is a habit worth building now — the alternative is a constructor that silently assigns a parameter to itself and leaves the actual field untouched, a bug that compiles cleanly and does nothing you asked for.

Try it yourself

Design a BankAccount class with private fields for account number and balance. Write a constructor that sets the starting balance, and public methods deposit(double amount) and withdraw(double amount) that reject a withdrawal larger than the current balance.

Need a hint?

The balance field should never be directly accessible from outside — every change must go through deposit or withdraw, so the rejection rule cannot be bypassed.

Check the worked solution

Keeping balance private and only reachable through deposit and withdraw is what makes the withdrawal rule actually enforceable — if balance were public, any code anywhere could set it to a negative number directly, skipping the check entirely. This is encapsulation solving a real problem, not a formality: the invariant "balance never goes negative" is guaranteed by the class itself, not by every caller remembering to check first.

Quick check

Why does making the balance field private, reachable only through deposit and withdraw methods, matter more than it might first appear?

Select an answer to check your thinking.

Why this lesson exists

Syllabus mapping

Simple java program, concepts of classes, objects, constructors, methods, access control, this keyword.

Maps to course outcome CO1.