Skip to main content
PProfileMatch

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, super and method overriding
  • Describe what an interface is and when to use one

On this page

  1. Classes, fields and objects
  2. Constructors and the this keyword
  3. Encapsulation
  4. Inheritance
  5. Interfaces: a quick overview
  6. Common mistakes
  7. How this shows up in interviews and real projects

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.

BankDemo.java
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.

Inheritance.java
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.

Interfaces.java
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 private fields with methods.
  • Calling a constructor that does not exist. new BankAccount() fails with error: constructor BankAccount in class BankAccount cannot be applied to given types once you have defined your own constructors.
  • Forgetting `new`. Declaring BankAccount acc; and calling acc.deposit(5) fails with error: variable acc might not have been initialized. If you set acc = null, it crashes at run time with NullPointerException.
  • Assuming assignment copies an object. alias = acc copies 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 get error: 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 this refers 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, super and @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.

Ride this lesson

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.