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.
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.
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.
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;
}
}
}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.
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 constructorthis: 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?
Why this lesson exists
Syllabus mapping
Simple java program, concepts of classes, objects, constructors, methods, access control, this keyword.
Maps to course outcome CO1.