Java OOP Concepts Complete Tutorial
Master Object-Oriented Programming in Java with comprehensive examples and explanations
Introduction to OOP
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects", which contain data (attributes) and code (methods). Java is a pure object-oriented language that implements OOP principles.
Why OOP?
- Modularity: Code is organized into classes and objects
- Reusability: Classes can be reused in different programs
- Maintainability: Easier to maintain and update code
- Scalability: Easy to extend and add new features
- Security: Data hiding through encapsulation
Four Pillars of OOP
- Encapsulation: Bundling data and methods together
- Inheritance: Creating new classes from existing ones
- Polymorphism: One interface, multiple implementations
- Abstraction: Hiding implementation details
Classes and Objects
A class is a blueprint or template for creating objects. An object is an instance of a class.
Defining a Class
public class Car {
// Attributes (Instance Variables)
String brand;
String model;
int year;
double price;
// Methods
void start() {
System.out.println("Car is starting...");
}
void stop() {
System.out.println("Car has stopped.");
}
void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
System.out.println("Year: " + year);
System.out.println("Price: $" + price);
}
}
Creating Objects
public class Main {
public static void main(String[] args) {
// Creating an object
Car myCar = new Car();
// Setting attributes
myCar.brand = "Toyota";
myCar.model = "Camry";
myCar.year = 2023;
myCar.price = 30000.0;
// Calling methods
myCar.start();
myCar.displayInfo();
myCar.stop();
}
}
Class Components
- Instance Variables: Variables declared inside class but outside methods
- Methods: Functions that define behavior of objects
- Constructors: Special methods for object initialization
- Blocks: Static and instance initialization blocks
- Nested Classes: Classes defined inside other classes
Encapsulation
Encapsulation is the mechanism of wrapping data (variables) and code (methods) together as a single unit. It also provides data hiding by making variables private and providing public methods to access them.
Benefits of Encapsulation
- Data hiding and security
- Flexibility to change implementation
- Better control over data validation
- Easier maintenance
Example: Encapsulated Class
public class BankAccount {
// Private variables - data hiding
private String accountNumber;
private double balance;
private String accountHolder;
// Public constructor
public BankAccount(String accountNumber, String accountHolder) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = 0.0;
}
// Public getter methods
public String getAccountNumber() {
return accountNumber;
}
public double getBalance() {
return balance;
}
public String getAccountHolder() {
return accountHolder;
}
// Public setter methods with validation
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
} else {
System.out.println("Invalid deposit amount!");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
} else {
System.out.println("Invalid withdrawal amount or insufficient funds!");
}
}
public void displayBalance() {
System.out.println("Account Holder: " + accountHolder);
System.out.println("Account Number: " + accountNumber);
System.out.println("Balance: $" + balance);
}
}
Using the Encapsulated Class
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount("ACC123", "John Doe");
account.deposit(1000.0);
account.deposit(500.0);
account.withdraw(300.0);
account.displayBalance();
// Cannot directly access private variables
// account.balance = 10000; // Compilation error!
}
}
Inheritance
Inheritance is a mechanism where a new class (child/subclass) acquires the properties and behaviors of an existing class (parent/superclass). It promotes code reusability and establishes an "is-a" relationship.
Syntax
class ParentClass {
// Parent class members
}
class ChildClass extends ParentClass {
// Child class members
}
Example: Inheritance
// Parent class
class Animal {
String name;
int age;
void eat() {
System.out.println(name + " is eating.");
}
void sleep() {
System.out.println(name + " is sleeping.");
}
}
// Child class
class Dog extends Animal {
String breed;
void bark() {
System.out.println(name + " is barking!");
}
void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Breed: " + breed);
}
}
// Usage
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.name = "Buddy";
myDog.age = 3;
myDog.breed = "Golden Retriever";
myDog.eat(); // Inherited from Animal
myDog.sleep(); // Inherited from Animal
myDog.bark(); // Own method
myDog.displayInfo();
}
}
Types of Inheritance
- Single Inheritance: One child class extends one parent class
- Multilevel Inheritance: Chain of inheritance (A → B → C)
- Hierarchical Inheritance: Multiple children from one parent
- Multiple Inheritance: Not supported in Java (use interfaces instead)
Method Overriding
When a child class provides a specific implementation of a method that is already defined in the parent class, it's called method overriding.
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks: Woof! Woof!");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows: Meow! Meow!");
}
}
Polymorphism
Polymorphism means "many forms". It allows objects of different types to be treated as objects of a common type. In Java, polymorphism is achieved through method overriding and method overloading.
Types of Polymorphism
- Compile-time Polymorphism (Method Overloading): Multiple methods with same name but different parameters
- Runtime Polymorphism (Method Overriding): Child class overrides parent class method
Method Overloading (Compile-time)
class Calculator {
// Method overloading - same name, different parameters
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
String add(String a, String b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3)); // Calls int version
System.out.println(calc.add(5, 3, 2)); // Calls three-parameter version
System.out.println(calc.add(5.5, 3.2)); // Calls double version
System.out.println(calc.add("Hello", "World")); // Calls String version
}
}
Method Overriding (Runtime)
class Shape {
void draw() {
System.out.println("Drawing a shape");
}
double calculateArea() {
return 0;
}
}
class Circle extends Shape {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
void draw() {
System.out.println("Drawing a circle");
}
@Override
double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
double length, width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
void draw() {
System.out.println("Drawing a rectangle");
}
@Override
double calculateArea() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Shape shape1 = new Circle(5.0);
Shape shape2 = new Rectangle(4.0, 6.0);
shape1.draw(); // Calls Circle's draw()
System.out.println("Area: " + shape1.calculateArea());
shape2.draw(); // Calls Rectangle's draw()
System.out.println("Area: " + shape2.calculateArea());
}
}
Abstraction
Abstraction is the process of hiding implementation details and showing only essential features. In Java, abstraction is achieved using abstract classes and interfaces.
Abstract Class
An abstract class cannot be instantiated. It may contain abstract methods (without body) and concrete methods (with body).
// Abstract class
abstract class Vehicle {
String brand;
String model;
// Concrete method
void displayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Model: " + model);
}
// Abstract method - must be implemented by child classes
abstract void start();
abstract void stop();
abstract double calculateFuelEfficiency();
}
// Concrete class extending abstract class
class Car extends Vehicle {
Car(String brand, String model) {
this.brand = brand;
this.model = model;
}
@Override
void start() {
System.out.println("Car engine started");
}
@Override
void stop() {
System.out.println("Car engine stopped");
}
@Override
double calculateFuelEfficiency() {
return 15.5; // km per liter
}
}
// Another concrete class
class Motorcycle extends Vehicle {
Motorcycle(String brand, String model) {
this.brand = brand;
this.model = model;
}
@Override
void start() {
System.out.println("Motorcycle engine started");
}
@Override
void stop() {
System.out.println("Motorcycle engine stopped");
}
@Override
double calculateFuelEfficiency() {
return 35.0; // km per liter
}
}
Key Points about Abstract Classes
- Cannot be instantiated directly
- Can have both abstract and concrete methods
- Can have constructors, instance variables, and static methods
- Child class must implement all abstract methods
- Can have final methods
Interfaces
An interface is a reference type in Java that contains only abstract methods, default methods, static methods, and constants. It's a contract that classes must follow.
Defining an Interface
interface Drawable {
// Constant (implicitly public, static, final)
String COLOR = "Black";
// Abstract method (implicitly public and abstract)
void draw();
// Default method (Java 8+)
default void display() {
System.out.println("Displaying drawable object");
}
// Static method (Java 8+)
static void printInfo() {
System.out.println("This is a Drawable interface");
}
}
interface Resizable {
void resize(int percentage);
}
// Class implementing interface
class Circle implements Drawable, Resizable {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle with radius: " + radius);
}
@Override
public void resize(int percentage) {
radius = radius * (1 + percentage / 100.0);
System.out.println("Circle resized. New radius: " + radius);
}
}
Interface vs Abstract Class
| Interface | Abstract Class |
|---|---|
| Only abstract methods (before Java 8) | Can have both abstract and concrete methods |
| Multiple inheritance supported | Single inheritance only |
| Only constants (public static final) | Can have instance variables |
| Cannot have constructors | Can have constructors |
| All methods are public by default | Can have any access modifier |
Constructors
A constructor is a special method that is called when an object is created. It has the same name as the class and doesn't have a return type.
Types of Constructors
- Default Constructor: No parameters, provided by compiler if no constructor is defined
- Parameterized Constructor: Takes parameters to initialize object
- Copy Constructor: Creates object from another object
Constructor Examples
class Student {
String name;
int age;
String course;
// Default constructor
Student() {
name = "Unknown";
age = 0;
course = "Not assigned";
System.out.println("Default constructor called");
}
// Parameterized constructor
Student(String name, int age, String course) {
this.name = name;
this.age = age;
this.course = course;
System.out.println("Parameterized constructor called");
}
// Copy constructor
Student(Student other) {
this.name = other.name;
this.age = other.age;
this.course = other.course;
System.out.println("Copy constructor called");
}
void display() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Course: " + course);
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student(); // Default constructor
Student s2 = new Student("John", 20, "Computer Science"); // Parameterized
Student s3 = new Student(s2); // Copy constructor
s2.display();
s3.display();
}
}
Constructor Chaining
One constructor can call another constructor of the same class using this().
class Person {
String name;
int age;
String city;
Person() {
this("Unknown", 0, "Unknown"); // Calling parameterized constructor
}
Person(String name, int age) {
this(name, age, "Unknown"); // Constructor chaining
}
Person(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
}
Access Modifiers
Access modifiers control the visibility and accessibility of classes, methods, and variables.
Types of Access Modifiers
| Modifier | Same Class | Same Package | Subclass | Different Package |
|---|---|---|---|---|
private |
✓ | ✗ | ✗ | ✗ |
default |
✓ | ✓ | ✗ | ✗ |
protected |
✓ | ✓ | ✓ | ✗ |
public |
✓ | ✓ | ✓ | ✓ |
Example
class AccessExample {
private int privateVar = 1;
int defaultVar = 2;
protected int protectedVar = 3;
public int publicVar = 4;
private void privateMethod() {
System.out.println("Private method");
}
void defaultMethod() {
System.out.println("Default method");
}
protected void protectedMethod() {
System.out.println("Protected method");
}
public void publicMethod() {
System.out.println("Public method");
}
}
Static Keyword
The static keyword is used for memory management. Static members belong to the class rather than instances.
Static Variables
class Counter {
static int count = 0; // Static variable - shared by all instances
Counter() {
count++; // Incremented for each object creation
}
static void displayCount() { // Static method
System.out.println("Total objects created: " + count);
}
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
Counter.displayCount(); // Output: 3
}
}
Static Methods
- Can be called without creating an object
- Can only access static variables and methods
- Cannot use
thisorsuper
Static Block
class StaticBlockExample {
static int value;
// Static block - executed when class is loaded
static {
value = 100;
System.out.println("Static block executed");
}
StaticBlockExample() {
System.out.println("Constructor executed");
}
}
Final Keyword
The final keyword is used to restrict modification.
Uses of Final
- Final Variable: Cannot be reassigned (constant)
- Final Method: Cannot be overridden
- Final Class: Cannot be extended
final class MathUtils { // Final class - cannot be extended
final double PI = 3.14159; // Final variable - constant
final double calculateArea(double radius) { // Final method - cannot be overridden
return PI * radius * radius;
}
}
this and super Keywords
this Keyword
this refers to the current object instance.
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name; // 'this' refers to current object
this.age = age;
}
void display() {
System.out.println("Name: " + this.name);
System.out.println("Age: " + this.age);
}
}
super Keyword
super refers to the parent class object.
class Animal {
String name;
Animal(String name) {
this.name = name;
}
void display() {
System.out.println("Animal: " + name);
}
}
class Dog extends Animal {
String breed;
Dog(String name, String breed) {
super(name); // Calling parent constructor
this.breed = breed;
}
@Override
void display() {
super.display(); // Calling parent method
System.out.println("Breed: " + breed);
}
}
Best Practices
OOP Best Practices in Java
- Always use encapsulation: Make instance variables private
- Use meaningful names: Class names should be nouns, method names should be verbs
- Follow single responsibility: Each class should have one reason to change
- Prefer composition over inheritance: Use "has-a" relationship when possible
- Use interfaces for contracts: Define behavior through interfaces
- Avoid deep inheritance hierarchies: Keep inheritance levels shallow
- Use final for constants: Make constants final and static
- Override equals() and hashCode(): When comparing objects
- Use @Override annotation: When overriding methods
- Document your code: Use JavaDoc comments
Common Mistakes to Avoid
- Making all variables public
- Creating too many static members
- Deep inheritance hierarchies
- Not using interfaces when appropriate
- Ignoring encapsulation
Sign up here with your email
ConversionConversion EmoticonEmoticon