Java Classes and Objects (OOP Basics)
ProgrammingLesson 5 of 65 min read
What you'll learn
- Define a class with fields, constructors and methods, and create objects from it
- Apply encapsulation using private fields and public methods
- Use inheritance with
extends,superand method overriding - Describe what an interface is and when to use one
On this page
So far you have used Java's built-in types. Real programs model things from the real world: customers, bank accounts, invoices. A class is the blueprint for such a thing and an object is one concrete instance built from that blueprint. This lesson walks through the four ideas that make up everyday object-oriented Java.
Classes, fields and objects
A class bundles data (fields) with the behaviour (methods) that works on that data. BankAccount below stores an owner and a balance, and offers deposit and withdraw. You create an object with the new keyword, which allocates memory and runs a constructor. Every object has its own copy of the fields, so two accounts never share balances.
Constructors and the this keyword
A constructor has the same name as the class and no return type. It runs once when the object is created and sets it up in a valid state. When a parameter has the same name as a field, this.owner = owner; makes it clear that the left side is the object's field and the right side is the parameter. You can write several constructors; this(owner, 0) calls another constructor of the same class, which avoids duplicating setup code.
Encapsulation
Encapsulation means hiding an object's data behind a controlled interface. Fields are private, so outside code cannot set balance = -5000 directly. Instead it must call deposit or withdraw, and those methods enforce the rules. This is why classes are so useful: the rules live in one place. Reading a private field is done with a getter such as getBalance(). If a field never changes after construction, mark it final as owner is here.
class BankAccount {
private final String owner;
private double balance;
BankAccount(String owner, double openingBalance) {
this.owner = owner;
this.balance = openingBalance;
}
BankAccount(String owner) {
this(owner, 0);
}
void deposit(double amount) {
if (amount <= 0) {
System.out.println("Deposit must be positive");
return;
}
balance += amount;
}
boolean withdraw(double amount) {
if (amount > balance) {
return false;
}
balance -= amount;
return true;
}
double getBalance() {
return balance;
}
String getOwner() {
return owner;
}
}
public class BankDemo {
public static void main(String[] args) {
BankAccount acc = new BankAccount("Rohan", 1000);
acc.deposit(500);
acc.deposit(-20);
System.out.println(acc.withdraw(2000));
System.out.println(acc.withdraw(300));
System.out.println(acc.getOwner() + " has " + acc.getBalance());
BankAccount empty = new BankAccount("Meera");
System.out.println(empty.getBalance());
BankAccount alias = acc;
alias.deposit(100);
System.out.println(acc.getBalance());
}
}Output
Deposit must be positive false true Rohan has 1200.0 0.0 1300.0
Trace it: Rohan starts with 1000, deposits 500 (1500), the negative deposit is rejected, the 2000 withdrawal fails because it exceeds the balance, and the 300 withdrawal succeeds (1200). The last three lines reveal something important: alias = acc did not copy the account. Both variables refer to the same object, so depositing through alias changed what acc sees.
Inheritance
Inheritance lets a class reuse and specialise another. Manager extends Employee means a manager is an employee, so it automatically has name and baseSalary. The constructor calls super(...) to run the parent's constructor, and @Override marks a method that replaces the parent's version. The annotation is optional but valuable, because the compiler then rejects a misspelled method name.
class Employee {
protected String name;
protected double baseSalary;
Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}
double monthlyPay() {
return baseSalary;
}
@Override
public String toString() {
return name + " earns " + monthlyPay();
}
}
class Manager extends Employee {
private double bonus;
Manager(String name, double baseSalary, double bonus) {
super(name, baseSalary);
this.bonus = bonus;
}
@Override
double monthlyPay() {
return super.monthlyPay() + bonus;
}
}
public class Inheritance {
public static void main(String[] args) {
Employee dev = new Employee("Anita", 60000);
Employee boss = new Manager("Vikram", 90000, 15000);
System.out.println(dev);
System.out.println(boss);
}
}Output
Anita earns 60000.0 Vikram earns 105000.0
The variable boss has the type Employee but holds a Manager. When toString() calls monthlyPay(), Java uses the real object's version, so Vikram's pay includes the bonus. This is polymorphism: the same call behaves differently depending on the actual object. The protected modifier allows access from subclasses.
Interfaces: a quick overview
A class can extend only one parent, but it can implement many interfaces. An interface is a contract: it lists methods a class promises to provide, without saying how. Code that depends on the interface does not care which class is behind it, which makes programs flexible and easy to test.
interface Payable {
double amountDue();
}
class Invoice implements Payable {
private final double amount;
Invoice(double amount) {
this.amount = amount;
}
public double amountDue() {
return amount;
}
}
class Subscription implements Payable {
private final double monthlyFee;
private final int months;
Subscription(double monthlyFee, int months) {
this.monthlyFee = monthlyFee;
this.months = months;
}
public double amountDue() {
return monthlyFee * months;
}
}
public class Interfaces {
public static void main(String[] args) {
Payable[] bills = { new Invoice(2500), new Subscription(199, 12) };
double total = 0;
for (Payable p : bills) {
total += p.amountDue();
}
System.out.println("Total due: " + total);
}
}Output
Total due: 4888.0
The loop does not know whether each bill is an Invoice or a Subscription. It only needs amountDue(). Note that interface methods are implicitly public, so implementing classes must declare them public. Rule of thumb: use inheritance for a true "is-a" relationship with shared code, and an interface when unrelated classes should share a capability.
Common mistakes
- Making fields public. It compiles, but any code can put the object in an invalid state. Prefer
privatefields with methods. - Calling a constructor that does not exist.
new BankAccount()fails witherror: constructor BankAccount in class BankAccount cannot be applied to given typesonce you have defined your own constructors. - Forgetting `new`. Declaring
BankAccount acc;and callingacc.deposit(5)fails witherror: variable acc might not have been initialized. If you setacc = null, it crashes at run time withNullPointerException. - Assuming assignment copies an object.
alias = acccopies the reference, as the demo showed. Create a new object if you need a separate copy. - Missing `super(...)` arguments. If the parent has no no-argument constructor, the child must call
super(...)explicitly, or you geterror: constructor Employee in class Employee cannot be applied to given types. - Not implementing an interface method. The compiler says
error: Invoice is not abstract and does not override abstract method amountDue() in Payable.
How this shows up in interviews and real projects
The four pillars, encapsulation, inheritance, polymorphism and abstraction, are among the most common Java interview topics, and the standard follow-up is to explain each with a small example like the ones above. In real backend code you will define many small classes for entities (orders, users) and services, and you will depend on interfaces constantly. Prefer composition over deep inheritance trees. Next, learn how to store groups of objects in Java Collections: ArrayList and HashMap.
Try it yourself
Create a Student class with private fields name and marks (an int), a constructor, a getter for each field, and a method isPassing() that returns true when marks are at least 40. Then create two students in main and print which of them pass. Bonus: reject marks outside 0 to 100.
Show a hint
Put the validation for marks inside the constructor (and a setter if you add one), so an invalid Student can never exist.
Key takeaways
- A class is a blueprint; an object is an instance created with
new, and each object has its own fields. - Constructors initialise objects, and
thisrefers to the current object, which resolves name clashes. - Encapsulation keeps fields private and exposes validated methods, so rules live in one place.
- Inheritance models "is-a" with
extends,superand@Override; interfaces define contracts a class can implement. - Assigning one object variable to another copies the reference, not the object.
Test yourself
A trick from this lesson's ride: is-a means extends; can-do means implements. Four more tricks like it wait along the route, each followed by a question.
Frequently asked questions
What is the difference between a class and an object in Java?
A class is a blueprint that defines fields and methods, while an object is a specific instance created from that class with new. One class can produce many objects, each holding its own data.
What is encapsulation in Java with an example?
Encapsulation means keeping a class's fields private and allowing access only through methods that enforce rules. For example, a BankAccount keeps balance private and only changes it through deposit and withdraw, which reject invalid amounts.
What is the difference between an abstract class and an interface in Java?
An interface defines a contract of methods that unrelated classes can implement, and a class may implement many interfaces. An abstract class can hold fields and shared code but a class can extend only one. Use an interface for a shared capability and an abstract class for a common base with shared state.