Java for Beginners: Object-Oriented Programming Fundamentals
Master object-oriented programming in Java with inheritance, polymorphism, encapsulation, and abstraction concepts along with practical examples.
Welcome to the second part of our "Java for Beginners" series! In the previous article, we covered Java basics including syntax, variables, data types, and control flow structures. Now, we'll dive deeper into what makes Java special: its robust object-oriented programming (OOP) features.
Object-oriented programming is a programming paradigm based on the concept of "objects" that contain data and code. Java was designed from the ground up with OOP principles in mind, making it an excellent language for learning these concepts.
The Four Pillars of Object-Oriented Programming
Java implements the four fundamental OOP concepts: encapsulation, inheritance, polymorphism, and abstraction. Let's explore each one with practical examples.
1. Encapsulation
Encapsulation is the bundling of data (attributes) and methods (functions) that operate on the data into a single unit called a class, while restricting direct access to some of the object's components.
public class BankAccount {
// Private variables - hidden from outside access
private String accountNumber;
private double balance;
private String ownerName;
// Constructor
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
// Public methods - controlled access to private data
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("$" + amount + " deposited successfully.");
} else {
System.out.println("Invalid deposit amount.");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("$" + amount + " withdrawn successfully.");
} else {
System.out.println("Invalid withdrawal amount or insufficient funds.");
}
}
}
In this example, the account details (accountNumber, balance, ownerName) are private and can only be accessed or modified through public methods like getBalance(), deposit(), and withdraw(). This protects the data from unauthorized access and ensures it remains valid.
2. Inheritance
Inheritance allows a class to inherit attributes and methods from another class, promoting code reuse and establishing a relationship between a parent class (superclass) and a child class (subclass).
// Parent class
public class Vehicle {
protected String brand;
protected String model;
protected int year;
public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}
public void displayInfo() {
System.out.println("Brand: " + brand + ", Model: " + model + ", Year: " + year);
}
public void start() {
System.out.println("The vehicle is starting...");
}
}
// Child class inheriting from Vehicle
public class Car extends Vehicle {
private int numDoors;
private boolean isConvertible;
public Car(String brand, String model, int year, int numDoors, boolean isConvertible) {
super(brand, model, year); // Call parent constructor
this.numDoors = numDoors;
this.isConvertible = isConvertible;
}
// Override parent method
@Override
public void displayInfo() {
super.displayInfo(); // Call parent method
System.out.println("Doors: " + numDoors + ", Convertible: " + isConvertible);
}
// Additional method specific to Car
public void honk() {
System.out.println("Beep beep!");
}
}
Here, the Car class inherits properties and methods from the Vehicle class while adding its own unique attributes and behaviors.
3. Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables a single interface to represent different underlying forms (data types).
public class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
public double calculateArea() {
return 0.0;
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle");
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
@Override
public double calculateArea() {
return width * height;
}
}
// Using polymorphism
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 method
shape2.draw(); // Calls Rectangle's draw method
System.out.println("Circle area: " + shape1.calculateArea());
System.out.println("Rectangle area: " + shape2.calculateArea());
}
}
In this example, both Circle and Rectangle are treated as Shape objects, but they behave differently when their methods are called, demonstrating polymorphism in action.
4. Abstraction
Abstraction focuses on hiding implementation details while showing only the necessary features of an object. Java implements abstraction through abstract classes and interfaces.
// Abstract class
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// Abstract method - no implementation
public abstract void makeSound();
// Concrete method with implementation
public void sleep() {
System.out.println(name + " is sleeping...");
}
}
// Interface
public interface Swimmable {
void swim();
}
// Concrete class implementing abstract class and interface
public class Dog extends Animal implements Swimmable {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " says: Woof!");
}
@Override
public void swim() {
System.out.println(name + " is swimming doggy-style!");
}
}
Here, Animal is an abstract class that defines common properties and behaviors for all animals, while the Swimmable interface defines a capability that some animals might have.
Exception Handling
Exception handling is a crucial part of writing robust Java applications. Java provides a mechanism to handle runtime errors gracefully:
public class ExceptionExample {
public static void main(String[] args) {
try {
// Code that might throw an exception
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index error: " + e.getMessage());
} catch (Exception e) {
System.out.println("General error: " + e.getMessage());
} finally {
System.out.println("This block always executes, regardless of exceptions.");
}
}
}
Collections Framework
Java provides a rich Collections Framework for storing and manipulating groups of objects:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class CollectionsExample {
public static void main(String[] args) {
// ArrayList - dynamic array
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
System.out.println("Second fruit: " + fruits.get(1));
// HashSet - unique elements only
HashSet<Integer> uniqueNumbers = new HashSet<>();
uniqueNumbers.add(5);
uniqueNumbers.add(10);
uniqueNumbers.add(5); // Duplicate, won't be added
System.out.println("Set size: " + uniqueNumbers.size()); // Outputs 2
// HashMap - key-value pairs
HashMap<String, Integer> ages = new HashMap<>();
ages.put("John", 25);
ages.put("Sarah", 30);
System.out.println("Sarah's age: " + ages.get("Sarah"));
}
}
Conclusion
In this second part of our Java for Beginners series, we've explored the core object-oriented programming concepts that make Java powerful and flexible. We've covered encapsulation, inheritance, polymorphism, and abstraction with practical examples. We've also touched on exception handling and the Collections Framework, which are essential tools for Java developers.
By understanding these concepts, you now have a solid foundation for building more complex Java applications. Practice implementing these concepts in your own projects to reinforce your learning.
Remember that mastering Java, like any programming language, takes time and practice. Don't be discouraged if some concepts seem challenging at first—keep coding, experimenting, and building your skills incrementally.
Happy coding, and welcome to the world of object-oriented programming with Java!