Java Object-Oriented Programming
Introduction
Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around objects rather than functions and logic. Java was designed from the ground up as an object-oriented language, making it an excellent platform to learn and apply OOP concepts.
In this tutorial, we'll explore the core principles of object-oriented programming in Java, which will provide the foundation you need for Spring development later on. By understanding these concepts, you'll be able to write more maintainable, reusable, and organized code.
Core OOP Concepts in Java
1. Classes and Objects
A class is a blueprint or template that defines the characteristics and behaviors of a specific type. An object is an instance of a class.
Example: Creating a Simple Class
// Define a class called Person
public class Person {
// Fields (attributes)
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Methods (behaviors)
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
// Getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Creating and Using Objects
public class Main {
public static void main(String[] args) {
// Create Person objects
Person person1 = new Person("John", 25);
Person person2 = new Person("Sarah", 30);
// Call methods on the objects
person1.introduce(); // Output: Hello, my name is John and I am 25 years old.
person2.introduce(); // Output: Hello, my name is Sarah and I am 30 years old.
// Modify object state
person1.setAge(26);
person1.introduce(); // Output: Hello, my name is John and I am 26 years old.
}
}
2. Encapsulation
Encapsulation is the bundling of data (fields) and methods that operate on that data within a single unit (class), and restricting direct access to some of the object's components.
Key Benefits:
- Data hiding
- Increased security
- Control over data access and modification
public class BankAccount {
private double balance; // private field - encapsulated
private String accountNumber;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// Public methods to access and modify the private fields
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
} else {
System.out.println("Cannot deposit negative amount.");
}
}
public void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
} else {
System.out.println("Invalid withdrawal amount or insufficient funds.");
}
}
}
Example usage:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount("123456789", 1000.0);
// Access through methods
System.out.println("Initial Balance: $" + account.getBalance());
account.deposit(500.0);
System.out.println("Current Balance: $" + account.getBalance());
account.withdraw(200.0);
System.out.println("Current Balance: $" + account.getBalance());
// This would be invalid due to encapsulation:
// account.balance = 1000000; // Compile error: balance has private access
}
}
Output:
Initial Balance: $1000.0
Deposited: $500.0
Current Balance: $1500.0
Withdrawn: $200.0
Current Balance: $1300.0
3. Inheritance
Inheritance allows a class to inherit fields and methods from another class. It promotes code reuse and establishes an "is-a" relationship between classes.
// Parent/base/super class
public class Vehicle {
private String make;
private String model;
private int year;
public Vehicle(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
public void displayInfo() {
System.out.println("Vehicle: " + year + " " + make + " " + model);
}
public void start() {
System.out.println("Vehicle started");
}
// Getters and setters
public String getMake() { return make; }
public String getModel() { return model; }
public int getYear() { return year; }
}
// Child/derived/sub class
public class Car extends Vehicle {
private int numDoors;
public Car(String make, String model, int year, int numDoors) {
super(make, model, year); // Call to parent constructor
this.numDoors = numDoors;
}
// Override parent method
@Override
public void displayInfo() {
System.out.println("Car: " + getYear() + " " + getMake() + " " + getModel() +
" with " + numDoors + " doors");
}
// Child-specific method
public void honk() {
System.out.println("Beep beep!");
}
}
Example usage:
public class Main {
public static void main(String[] args) {
// Create a Vehicle
Vehicle vehicle = new Vehicle("Generic", "Transport", 2020);
vehicle.displayInfo();
vehicle.start();
// Create a Car
Car car = new Car("Toyota", "Corolla", 2022, 4);
car.displayInfo(); // Uses overridden method
car.start(); // Inherited from Vehicle
car.honk(); // Car-specific method
}
}
Output:
Vehicle: 2020 Generic Transport
Vehicle started
Car: 2022 Toyota Corolla with 4 doors
Vehicle started
Beep beep!
4. Polymorphism
Polymorphism means "many forms" and allows objects to be treated as instances of their parent class rather than their actual class. There are two types of polymorphism in Java:
- Compile-time polymorphism (method overloading)
- Runtime polymorphism (method overriding)
Method Overloading (Compile-time Polymorphism)
public class Calculator {
// Method overloading - same method name but different parameters
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3)); // Output: 8
System.out.println(calc.add(4.5, 3.2)); // Output: 7.7
System.out.println(calc.add(1, 2, 3)); // Output: 6
}
}
Method Overriding (Runtime Polymorphism)
// Base class
public class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// Derived classes
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// Example of runtime polymorphism
public class Main {
public static void main(String[] args) {
// Create an array of Animal objects
Animal[] animals = new Animal[3];
animals[0] = new Animal();
animals[1] = new Dog();
animals[2] = new Cat();
// Polymorphic calls to makeSound()
for (Animal animal : animals) {
animal.makeSound(); // The method called depends on the actual object type
}
}
}
Output:
Animal makes a sound
Dog barks
Cat meows
5. Abstraction
Abstraction is the concept of hiding complex implementation details and showing only the necessary features. In Java, abstraction is achieved through abstract classes and interfaces.
Abstract Classes
// Abstract class - cannot be instantiated
public abstract class Shape {
// Abstract method - no implementation in the abstract class
public abstract double calculateArea();
// Concrete method - has implementation
public void displayInfo() {
System.out.println("This is a shape with area: " + calculateArea());
}
}
// Concrete subclasses
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@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 double calculateArea() {
return width * height;
}
}
Interfaces
An interface is a completely abstract type that contains only abstract methods and constants.
// Interface definition
public interface Drawable {
void draw(); // Abstract method (public and abstract by default)
// Default method (added in Java 8)
default void display() {
System.out.println("Displaying drawable object");
}
}
// Class implementing the interface
public class Circle implements Drawable {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle with radius: " + radius);
}
}
// Class implementing multiple interfaces
public class Rectangle implements Drawable, Resizable {
private double width;
private double height;
// Implementations of all methods from both interfaces...
}
Example usage:
public class Main {
public static void main(String[] args) {
// Using abstract classes
Shape circle = new Circle(5.0);
Shape rectangle = new Rectangle(4.0, 6.0);
circle.displayInfo(); // Output: This is a shape with area: 78.53981633974483
rectangle.displayInfo(); // Output: This is a shape with area: 24.0
// Using interfaces
Drawable drawable = new Circle(3.0);
drawable.draw(); // Output: Drawing a circle with radius: 3.0
drawable.display(); // Output: Displaying drawable object
}
}
Real-World Application: Building a Library Management System
Let's apply OOP concepts to build a simple library management system:
// Abstract class for all library items
public abstract class LibraryItem {
private String title;
private String itemId;
private boolean checkedOut;
public LibraryItem(String title, String itemId) {
this.title = title;
this.itemId = itemId;
this.checkedOut = false;
}
public abstract String getItemType();
public void checkOut() {
if (!checkedOut) {
checkedOut = true;
System.out.println(getItemType() + " checked out: " + title);
} else {
System.out.println(getItemType() + " is already checked out: " + title);
}
}
public void returnItem() {
if (checkedOut) {
checkedOut = false;
System.out.println(getItemType() + " returned: " + title);
} else {
System.out.println(getItemType() + " was not checked out: " + title);
}
}
// Getters and setters
public String getTitle() { return title; }
public String getItemId() { return itemId; }
public boolean isCheckedOut() { return checkedOut; }
}
// Book class inheriting from LibraryItem
public class Book extends LibraryItem {
private String author;
private int pages;
public Book(String title, String itemId, String author, int pages) {
super(title, itemId);
this.author = author;
this.pages = pages;
}
@Override
public String getItemType() {
return "Book";
}
public String getAuthor() { return author; }
public int getPages() { return pages; }
}
// DVD class inheriting from LibraryItem
public class DVD extends LibraryItem {
private String director;
private int runtime;
public DVD(String title, String itemId, String director, int runtime) {
super(title, itemId);
this.director = director;
this.runtime = runtime;
}
@Override
public String getItemType() {
return "DVD";
}
public String getDirector() { return director; }
public int getRuntime() { return runtime; }
}
// Library class to manage items
public class Library {
private List<LibraryItem> items;
public Library() {
this.items = new ArrayList<>();
}
public void addItem(LibraryItem item) {
items.add(item);
System.out.println("Added " + item.getItemType() + ": " + item.getTitle());
}
public LibraryItem findItem(String itemId) {
for (LibraryItem item : items) {
if (item.getItemId().equals(itemId)) {
return item;
}
}
return null;
}
public void listAvailableItems() {
System.out.println("Available Library Items:");
for (LibraryItem item : items) {
if (!item.isCheckedOut()) {
System.out.println(item.getItemType() + " - " + item.getTitle() + " (ID: " + item.getItemId() + ")");
}
}
}
}
Example usage:
public class Main {
public static void main(String[] args) {
Library library = new Library();
// Add items to the library
library.addItem(new Book("The Great Gatsby", "B001", "F. Scott Fitzgerald", 180));
library.addItem(new Book("To Kill a Mockingbird", "B002", "Harper Lee", 281));
library.addItem(new DVD("Inception", "D001", "Christopher Nolan", 148));
// List available items
library.listAvailableItems();
// Check out an item
LibraryItem item = library.findItem("B001");
if (item != null) {
item.checkOut();
}
// List available items again
library.listAvailableItems();
// Return the item
item.returnItem();
// List available items after return
library.listAvailableItems();
}
}
Output:
Added Book: The Great Gatsby
Added Book: To Kill a Mockingbird
Added DVD: Inception
Available Library Items:
Book - The Great Gatsby (ID: B001)
Book - To Kill a Mockingbird (ID: B002)
DVD - Inception (ID: D001)
Book checked out: The Great Gatsby
Available Library Items:
Book - To Kill a Mockingbird (ID: B002)
DVD - Inception (ID: D001)
Book returned: The Great Gatsby
Available Library Items:
Book - The Great Gatsby (ID: B001)
Book - To Kill a Mockingbird (ID: B002)
DVD - Inception (ID: D001)
Summary
In this tutorial, we've covered the fundamental concepts of Object-Oriented Programming in Java:
- Classes and Objects: The fundamental building blocks of OOP where classes define the blueprint and objects are instances of those classes.
- Encapsulation: The bundling of data and methods that operate on that data, with access control for increased security and maintainability.
- Inheritance: The ability for a class to inherit properties and methods from a parent class to promote code reuse.
- Polymorphism: The ability for objects to take on many forms depending on the context, including method overloading and overriding.
- Abstraction: The concept of hiding implementation details and showing only necessary features through abstract classes and interfaces.
Understanding these concepts is crucial for Java development and provides the foundation needed for Spring Framework, which heavily relies on OOP principles.
Additional Resources and Exercises
Resources
- Oracle's Java Tutorials on OOP Concepts
- Java Documentation on Abstract Classes
- Java Documentation on Interfaces
Exercises
-
Employee Management System: Create a class hierarchy for an employee management system with an abstract
Employee
class, and concrete classes likeManager
,Developer
, andDesigner
. Implement methods for calculating salary and bonuses. -
Shape Calculator: Extend the Shape example by adding more shapes like
Triangle
,Square
, andTrapezoid
. Implement a method to calculate perimeter for each shape. -
Bank Account System: Create a system with different types of bank accounts (
SavingsAccount
,CheckingAccount
,FixedDepositAccount
) inheriting from an abstractBankAccount
class. Implement appropriate methods for deposit, withdrawal, and interest calculation. -
Animal Kingdom Simulator: Create a class hierarchy representing different animals using inheritance and polymorphism. Include methods for movement, eating, and making sounds that are appropriate to each type of animal.
By completing these exercises, you'll reinforce your understanding of OOP principles in Java and be better prepared for learning Spring Framework.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)