Java for Beginners: Object-Oriented Programming Concepts
Explore Java's object-oriented programming features including classes, objects, inheritance, polymorphism, and encapsulation.
Welcome to the second part of our "Java for Beginners" series! In our previous post, we covered the basics of Java and wrote our first program. Today, we'll explore one of Java's most powerful features: Object-Oriented Programming (OOP).
Object-oriented programming is a programming paradigm that uses "objects" to model real-world entities. 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 OOP
Java implements the four fundamental principles of object-oriented programming:
- Encapsulation: Bundling data and methods that operate on that data within a single unit (class)
- Inheritance: Creating new classes that inherit properties and behaviors from existing classes
- Polymorphism: The ability of different objects to respond to the same method in different ways
- Abstraction: Hiding complex implementation details and showing only the necessary features
Let's explore each of these concepts in detail.
Classes and Objects
In Java, a class is a blueprint for creating objects. It defines the properties (attributes) and behaviors (methods) that objects of that class will have.
Creating a Class
Here's a simple example of a Car
class:
public class Car {
// Attributes (instance variables)
private String make;
private String model;
private int year;
private double fuelLevel;
// Constructor
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
this.fuelLevel = 100.0; // Full tank by default
}
// Methods
public void drive() {
if (fuelLevel > 0) {
System.out.println("The " + year + " " + make + " " + model + " is driving.");
fuelLevel -= 10;
} else {
System.out.println("Out of fuel!");
}
}
public void refuel() {
fuelLevel = 100.0;
System.out.println("The car has been refueled.");
}
// Getters and setters
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public int getYear() {
return year;
}
public double getFuelLevel() {
return fuelLevel;
}
}
Creating Objects
Once we have a class, we can create objects (instances) of that class:
public class Main {
public static void main(String[] args) {
// Create two Car objects
Car myCar = new Car("Toyota", "Corolla", 2020);
Car friendsCar = new Car("Honda", "Civic", 2019);
// Use the objects
myCar.drive();
friendsCar.drive();
System.out.println("My car's fuel level: " + myCar.getFuelLevel());
myCar.refuel();
System.out.println("After refueling: " + myCar.getFuelLevel());
}
}
Encapsulation
In our Car
class example, we've already implemented encapsulation by:
- Making the attributes
private
so they can't be accessed directly from outside the class - Providing
public
methods (getters and setters) to access and modify these attributes
This protects the internal state of our objects and ensures that any changes to the data happen in a controlled manner.
Inheritance
Inheritance allows us to create new classes that reuse, extend, and modify the behavior defined in other classes. Let's create an ElectricCar
class that inherits from our Car
class:
public class ElectricCar extends Car {
private int batteryCapacity;
public ElectricCar(String make, String model, int year, int batteryCapacity) {
super(make, model, year); // Call the parent class constructor
this.batteryCapacity = batteryCapacity;
}
// Override the drive method
@Override
public void drive() {
System.out.println("The " + getYear() + " " + getMake() + " " + getModel() +
" is driving silently on electricity.");
}
// Override the refuel method
@Override
public void refuel() {
System.out.println("The electric car is charging.");
}
// New method specific to ElectricCar
public void displayBatteryInfo() {
System.out.println("Battery capacity: " + batteryCapacity + " kWh");
}
}
Now we can use both types of cars:
public class Main {
public static void main(String[] args) {
Car regularCar = new Car("Toyota", "Corolla", 2020);
ElectricCar electricCar = new ElectricCar("Tesla", "Model 3", 2021, 75);
regularCar.drive(); // Uses Car's drive method
electricCar.drive(); // Uses ElectricCar's overridden drive method
regularCar.refuel(); // "The car has been refueled."
electricCar.refuel(); // "The electric car is charging."
electricCar.displayBatteryInfo(); // Method specific to ElectricCar
}
}
Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common superclass. The most common use of polymorphism in Java is when a parent class reference is used to refer to a child class object.
public class Main {
public static void main(String[] args) {
// Polymorphism in action
Car[] cars = new Car[2];
cars[0] = new Car("Toyota", "Corolla", 2020);
cars[1] = new ElectricCar("Tesla", "Model 3", 2021, 75);
// The same method call behaves differently depending on the actual object type
for (Car car : cars) {
car.drive(); // Will call the appropriate drive method based on the actual object type
}
}
}
Abstraction
Abstraction allows us to hide complex implementation details and show only the necessary features of an object. In Java, abstraction is achieved using abstract classes and interfaces.
Abstract Classes
public abstract class Vehicle {
private String make;
private String model;
public Vehicle(String make, String model) {
this.make = make;
this.model = model;
}
// Abstract method - must be implemented by subclasses
public abstract void move();
// Concrete method
public void displayInfo() {
System.out.println("Make: " + make + ", Model: " + model);
}
// Getters
public String getMake() { return make; }
public String getModel() { return model; }
}
Interfaces
public interface Chargeable {
void charge();
int getBatteryPercentage();
}
Implementing these:
public class ElectricVehicle extends Vehicle implements Chargeable {
private int batteryPercentage;
public ElectricVehicle(String make, String model) {
super(make, model);
this.batteryPercentage = 100;
}
@Override
public void move() {
System.out.println("The electric vehicle moves silently.");
batteryPercentage -= 10;
}
@Override
public void charge() {
batteryPercentage = 100;
System.out.println("Vehicle charged to 100%");
}
@Override
public int getBatteryPercentage() {
return batteryPercentage;
}
}
Conclusion
In this post, we've explored the fundamental concepts of object-oriented programming in Java. We've learned about classes and objects, and the four pillars of OOP: encapsulation, inheritance, polymorphism, and abstraction.
Understanding these concepts is crucial for becoming proficient in Java programming. They allow you to write more organized, reusable, and maintainable code.
In the next and final part of our series, we'll explore more advanced Java topics including exception handling, collections, and file I/O.
Happy coding!