OOPS... Not Just an Exclamation, But a Fundamental Java Concept
The concept of Object-Oriented Programming (OOP) in Java is as fundamental as the word "oops" is when we make a mistake. OOPs refers to Object Oriented Programming System . This concept tells us to write and use our code effectively .Object-Oriented Programming (OOP) in Java is a powerful way to structure your code by modeling real-world entities as objects. Think of it as a way to organize your code into reusable and manageable pieces. OOP revolves around four main principles: encapsulation, inheritance, polymorphism, and abstraction.
Encapsulation :
Encapsulation is one of the core principles of object-oriented programming (OOP) in Java. At its heart, encapsulation is about hiding the internal details of an object and only exposing what’s necessary. This not only keeps the data safe but also makes the code more organized and easier to Let's understand it with an example.
A story of Encapsulation :
In a quiet village, a mysterious gardener named Elara tended to a hidden garden filled with vibrant flowers and rare plants. This garden was enchanting, but its magic lay in Elara’s secretive care—she kept her methods hidden from everyone.
One day, a curious neighbor, Leo, asked Elara how she made her garden thrive. Instead of revealing her secrets, she handed him a small watering can and some seeds. “Use these and follow my simple instructions,” she said. Leo could enjoy the beauty of the garden without knowing the complex techniques Elara employed.
As Leo watered his plants each morning, they flourished, thanks to Elara's guidance. He learned to garden without disrupting the delicate balance of her hidden paradise.
This story illustrates encapsulation: Elara’s hidden methods represent the complex inner workings of a system, while the watering can and instructions serve as the public interface. By sharing only what was necessary, Elara protected her garden’s integrity, allowing others to benefit without exposing its secrets. Encapsulation simplifies interaction while safeguarding complexity, making it essential in programming.
Let’s translate this scenario into Java code to understand encapsulation better:
SecretGarden.java
// Class representing the secret garden
class SecretGarden {
// Private variables to encapsulate the garden's secrets
private String specialTechnique;
private String flowerType;
// Constructor to initialize the garden
public SecretGarden(String specialTechnique, String flowerType) {
this.specialTechnique = specialTechnique;
this.flowerType = flowerType;
}
// Public method to water plants
public void waterPlants() {
System.out.println("Watering the " + flowerType + " using my special technique.");
}
// Method to provide instructions to the neighbor
public void giveInstructions() {
System.out.println("Follow these simple steps to grow your own " + flowerType + "!");
}
// Private method that contains the secret technique (not accessible outside)
private void secretTechnique() {
System.out.println("Using the secret technique: " + specialTechnique);
}
}
Neighbour.java
// Class representing the neighbor
class Neighbor {
private SecretGarden garden;
// Constructor to create a Neighbor with access to the garden
public Neighbor(SecretGarden garden) {
this.garden = garden;
}
// Method to enjoy the garden's beauty
public void enjoyGarden() {
garden.waterPlants();
garden.giveInstructions();
// Cannot access secretTechnique() directly
}
}
GardenDemo.java
// Main class to demonstrate encapsulation
public class GardenDemo {
public static void main(String[] args) {
// Create a secret garden with hidden techniques
SecretGarden elaraGarden = new SecretGarden("Mystical Watering", "Rose");
// Create a neighbor who will benefit from the garden
Neighbor leo = new Neighbor(elaraGarden);
// Leo enjoys the garden without knowing the secrets
leo.enjoyGarden();
}
}
In the above code :
SecretGarden Class:
Contains private variables (
specialTechnique
andflowerType
) that encapsulate the garden's secrets.Public methods (
waterPlants
andgiveInstructions
) allow interaction while keeping the complex inner workings hidden.The private method
secretTechnique
cannot be accessed from outside the class, preserving the garden's secrets.
Neighbor Class:
- Represents Leo, who interacts with the
SecretGarden
class without knowing its secrets.
- Represents Leo, who interacts with the
GardenDemo Class:
- The main class demonstrates how Leo enjoys the benefits of the garden while Elara’s secrets remain protected.
This code effectively illustrates encapsulation, where sensitive data and methods are hidden, allowing for safe and simple interaction.
Why Encapsulation ?
Encapsulation brings several benefits that enhance the security, flexibility, and maintainability of code. Here are some important reasons to use encapsulation:
Data Protection: By hiding the internal state of an object, encapsulation prevents unauthorized access and modification, safeguarding sensitive information.
Controlled Access: It allows controlled access to the object's data through public methods, enabling validation and maintaining data integrity.
Code Maintenance: Changes to the internal implementation can be made without affecting external code that interacts with the object, simplifying maintenance.
Improved Modularity: Encapsulation promotes modular design, making it easier to manage complex systems by grouping related data and behaviors into cohesive units.
Enhanced Readability: It clarifies the interface between different parts of a program, making the code easier to understand and use
Abstraction:
Abstraction is another fundamental concept of object-oriented programming that simplifies complex systems by focusing on the essential details while hiding the rest. It allows you to interact with objects without needing to understand the inner workings behind them. Abstraction emphasizes what an object does, not how it does it.
What is Abstraction?
Abstraction is all about exposing only the necessary functionalities of an object while keeping the internal implementation hidden. It simplifies how we interact with complex systems by providing us with a clean interface to use, while the underlying logic remains hidden. This makes systems easier to work with and reduces complexity for the user. Let’s understand it with an example :
The Story of Abstraction: Wakanda and the Black Panther :
In the hidden kingdom of Wakanda, a land known only to a select few, lies the most advanced technology on Earth, fueled by the rare metal vibranium. But Wakanda is not just about technology; it’s a sanctuary of culture, tradition, and extraordinary power. At the center of this legacy is T’Challa, the Black Panther.
Wakanda’s technology is a marvel, from its unbreakable shields to its advanced medical facilities. However, these complexities are hidden from the outside world. To outsiders, Wakanda appears to be a simple, tribal nation, but beneath the surface lies a sophisticated society that utilizes vibranium in ways unimaginable.
When T’Challa steps into his role as king, he doesn’t just inherit the throne; he embodies the essence of Wakanda’s strength, wisdom, and secrecy. He uses the advanced technology to protect his people and aid the Avengers, all while keeping Wakanda’s true nature concealed from those who might misuse it.
How Abstraction takes part in this example :
Think of T’Challa as an abstraction layer. When he interacts with the Avengers, he doesn’t reveal every intricate detail of Wakandan technology. Instead, he provides them with what they need: vibranium-infused weapons and tools that enhance their abilities. The complexity of how vibranium is processed and the secrets of Wakandan technology remain hidden.
This is related to how abstraction works in programming:
Essential Features: T’Challa shares only the essential functionalities with the Avengers powerful weapons and support while the underlying complexities of Wakanda remain undisclosed.
Simplified Interaction: The Avengers can focus on using the technology effectively without needing to understand every detail of its operation.
Encapsulation of Details: Just as T’Challa protects Wakanda’s secrets, abstraction allows programmers to hide the inner workings of their code, exposing only the necessary methods and functionalities to users.
The Impact of Abstraction
By employing abstraction, T’Challa ensures that Wakanda’s technology is used for good without exposing its vulnerabilities. Similarly, abstraction in programming allows developers to build systems that are easier to use and maintain, keeping intricate details private while providing a simple interface for interaction.
Here’s how we could implement this concept of abstraction in Java:
Wakanda.java
import java.util.List;
import java.util.ArrayList;
// Class representing Wakanda
class Wakanda {
// Private attributes to encapsulate Wakanda's secrets
private String vibraniumTechnology;
private String culturalLegacy;
// Constructor to initialize Wakanda
public Wakanda(String vibraniumTechnology, String culturalLegacy) {
this.vibraniumTechnology = vibraniumTechnology;
this.culturalLegacy = culturalLegacy;
}
// Public method to provide weapons to the Avengers
public void provideWeapons() {
System.out.println("Providing vibranium-infused weapons to the Avengers.");
}
// Public method to offer support to the Avengers
public void offerSupport() {
System.out.println("Offering support to the Avengers.");
}
// Private method to process vibranium (hidden complexity)
private void processVibranium() {
System.out.println("Processing vibranium using advanced techniques.");
}
// Private method to maintain secrecy (hidden complexity)
private void maintainSecrecy() {
System.out.println("Maintaining the secrecy of Wakanda.");
}
}
BlackPanther.java
// Class representing the Black Panther
class BlackPanther {
private String name;
private String powers;
private Wakanda kingdom;
// Constructor to initialize the Black Panther
public BlackPanther(String name, String powers, Wakanda kingdom) {
this.name = name;
this.powers = powers;
this.kingdom = kingdom;
}
// Method for the Black Panther to interact with the Avengers
public void interactWithAvengers() {
System.out.println(name + " is interacting with the Avengers.");
kingdom.provideWeapons();
kingdom.offerSupport();
}
// Method to protect Wakanda
public void protectWakanda() {
System.out.println(name + " is protecting Wakanda.");
}
}
Avengers.java
// Class representing the Avengers
class Avengers {
private List<String> members;
// Constructor to initialize the Avengers
public Avengers(List<String> members) {
this.members = members;
}
// Method for the Avengers to request support from Wakanda
public void requestSupport(BlackPanther blackPanther) {
System.out.println("Avengers are requesting support from " + blackPanther.name + ".");
blackPanther.interactWithAvengers();
}
// Method for the Avengers to use Wakandan technology
public void useWakandanTechnology() {
System.out.println("Avengers are using Wakandan technology.");
}
}
WakandaDemo.java
// Main class to demonstrate abstraction
public class WakandaDemo {
public static void main(String[] args) {
// Create Wakanda with hidden attributes
Wakanda wakanda = new Wakanda("Advanced Vibranium Tech", "Rich Cultural Heritage");
// Create Black Panther (T'Challa)
BlackPanther blackPanther = new BlackPanther("T'Challa", "Superhuman abilities", wakanda);
// Create Avengers
List<String> avengerMembers = new ArrayList<>();
avengerMembers.add("Iron Man");
avengerMembers.add("Captain America");
avengerMembers.add("Thor");
Avengers avengers = new Avengers(avengerMembers);
// Avengers request support
avengers.requestSupport(blackPanther);
// Avengers use Wakandan technology
avengers.useWakandanTechnology();
}
}
In the above code:
Wakanda Class:
Contains private attributes and methods that encapsulate the complexity of Wakanda's technology and culture.
Public methods allow the Black Panther to provide weapons and support.
BlackPanther Class:
Represents T'Challa with attributes for his name and powers.
Contains methods for interacting with the Avengers and protecting Wakanda.
Avengers Class:
- Holds a list of Avenger members and methods for requesting support and using Wakandan technology.
WakandaDemo Class:
- The
main
method demonstrates how the classes interact, showcasing abstraction by allowing the Avengers to access Wakandan support without knowing its complexities.
- The
Why Abstraction?
Simplifies Complexity: Hides intricate details, allowing users to interact with systems without being overwhelmed.
Enhances Code Reusability: Promotes the use of common interfaces, enabling different implementations to be easily interchangeable.
Improves Maintainability: Allows changes in the implementation without affecting the classes that depend on the abstract class.
Encourages Modular Design: Breaks programs into smaller, manageable components, making development easier.
Decouples Implementation from Interface: Separates how something works from how it is used, facilitating easier updates and changes.
Supports Polymorphism: Enables different classes to be treated as instances of the same class through a common interface.
Promotes Clearer Code: Improves readability and understanding by providing a clear structure of how different parts of a system interact.
Inheritance :
Imagine a world where you could effortlessly build on existing foundations, creating something new without starting from scratch. Sounds like a dream, right? Inheritance in Java makes this dream a reality! Just as we inherit traits from our families like our knack for music or our love for adventure inheritance in programming allows one class to inherit properties and methods from another. This powerful concept is key to writing clean, efficient, and organized code.
What is Inheritance ?
Inheritance is the concept of inheriting the property of a class to the other class .The class whose properties are being inherited is known as the parent class, while the class that inherits these properties is known as the child class. Inheritance allows the child class to reuse, extend, and modify the behavior defined in the parent class, promoting code reusability and a hierarchical class structure. Let’s understand it with an example :
The Story of Inheritance: Thor, Odin, and Loki
In the majestic realm of Asgard, there lived a powerful king named Odin, the All-Father. He ruled with wisdom and strength, protecting his kingdom and its people. As the years passed, it became clear that Odin would eventually need to pass on his throne to one of his sons.
Odin had two sons: Thor, the god of thunder, and Loki, the trickster god. Both were unique in their own right, but they inherited different aspects of their father's legacy.
Thor, the Heir to the Throne:
Thor was strong, brave, and noble, destined to be the next king of Asgard. He inherited not just the title of prince but also the values of leadership and courage from Odin. With his mighty hammer, Mjolnir, Thor was prepared to defend Asgard against any threat. When Odin decided to pass down his throne, it was to Thor that he entrusted the responsibility of ruling Asgard.
As the new king, Thor inherited not only the throne but also the power to uphold the laws and protect the realm. He could choose to change any rules or traditions that he felt were outdated, shaping Asgard’s future with his own vision.
Loki, the Master of Deception :
Loki, on the other hand, inherited something different. Although he was also a son of Odin, he was known for his cunning and magic rather than traditional heroism. Loki had a unique set of skills—his ability to manipulate reality and create illusions. While he didn’t inherit the throne, he still carried the legacy of Odin's lineage.
Loki often found himself in the shadow of Thor, yet his cleverness and unpredictability made him a vital part of Asgard’s story. Even without the crown, he had the power to influence events in significant ways, proving that inheritance isn’t just about power and titles; it’s also about the gifts and challenges passed down through generations.
The Legacy of Inheritance :
In this tale, we see how Odin's inheritance shaped both of his sons. Thor inherited the responsibility of kingship, with the power to lead and protect, while Loki inherited cunning and magic, which would play a crucial role in their adventures.
This story illustrates the concept of inheritance in a relatable way. Just as in life, where children often inherit traits, responsibilities, and even challenges from their parents, so too do characters like Thor and Loki embody the principles of inheritance each shaping their own destinies while carrying the legacy of their father.
Let’s code it to understand how Inheritance works in code:
Odin.java
//Odin class the parent class
class Odin {
//name is public because it will not visible outside the class so make it public
//if we are making it private then we can create getter and setter method for the field
// to achieve encapsulation and can access anywhere using getter and setter method
public String name;
private String title;
private String wisdom;
private int strength;
// Constructor to initialize Odin's attributes
public Odin(String name, String title, String wisdom, int strength) {
this.name = name;
this.title = title;
this.wisdom = wisdom;
this.strength = strength;
}
// Method to pass the throne to a successor
public void passThrone() {
System.out.println(name + " has passed the throne to his son.");
}
}
Thor.java
// Subclass representing Thor, inheriting from Odin
class Thor extends Odin {
private int bravery; // Bravery attribute specific to Thor
// Constructor to initialize Thor's attributes
public Thor(String name, String title, int strength, int bravery) {
super(name, title, "Wisdom of the Ages", strength); // Calling the constructor of Odin
this.bravery = bravery; // Unique to Thor
}
// Method for Thor to wield Mjolnir
public void wieldMjolnir() {
System.out.println(super.name + " wields Mjolnir with strength!");
}
// Method for Thor to protect the realm
public void protectRealm() {
System.out.println(super.name + " is protecting Asgard!");
}
}
Loki.java
// Class representing Loki, inheriting from Odin
class Loki extends Odin {
private int cunning; // Cunning attribute specific to Loki
private int magic; // Magic attribute specific to Loki
// Constructor to initialize Loki's attributes
public Loki(String name, String title, int cunning, int magic) {
super(name, title, "Master of Deception", 10); // Calling the constructor of Odin
this.cunning = cunning; // Unique to Loki
this.magic = magic; // Unique to Loki
}
// Method for Loki to create illusions
public void createIllusions() {
System.out.println(super.name + " creates an illusion!");
}
// Method for Loki to manipulate reality
public void manipulateReality() {
System.out.println(super.name + " manipulates reality!");
}
}
Asgard.java
// Main class to demonstrate the inheritance
public class Asgard{
public static void main(String[] args) {
// Creating an instance of Odin
Odin odin = new Odin("Odin", "All-Father", "Wisdom of the Ages", 100);
// Creating an instance of Thor
Thor thor = new Thor("Thor", "God of Thunder", 90, 95);
// Creating an instance of Loki
Loki loki = new Loki("Loki", "God of Mischief", 80, 85);
// Odin passing the throne
odin.passThrone();
// Thor's actions
thor.wieldMjolnir();
thor.protectRealm();
// Loki's actions
loki.createIllusions();
loki.manipulateReality();
}
}
In the above code :
Inheritance in Classes:
class Thor extends Odin
: This line signifies thatThor
inherits from theOdin
class, meaning it gains all the attributes and methods ofOdin
.class Loki extends Odin
: Similarly,Loki
also inherits fromOdin
, enabling it to use the same attributes and methods.
Using
super
:In both
Thor
andLoki
, thesuper
keyword is used to call the constructor of the parent class (Odin
). This allows the subclasses to initialize inherited attributes directly.Discuss more about
this
andsuper
keyword in coming blogs.
Unique Attributes and Methods:
- While both
Thor
andLoki
inherit fromOdin
, they each have unique attributes (e.g.,bravery
for Thor andcunning
andmagic
for Loki) and methods that define their specific behaviors. This showcases polymorphism, where subclasses can have their own implementations while sharing a common interface.
- While both
Asgard Class: The main class demonstrates how to create instances of Thor and Loki, showcasing their unique methods along with inherited ones.
Why Inheritance ?
Code Reusability: Allows new classes to reuse existing code, reducing duplication.
Logical Organization: Creates a clear hierarchy that helps organize classes and their relationships.
Simplified Maintenance: Changes in a parent class automatically propagate to child classes, making updates easier.
Polymorphism Support: Enables methods to be overridden, allowing for flexible behaviors while maintaining a consistent interface.
Extensibility: Facilitates adding new features by building on existing classes without starting from scratch.
Encapsulation of Common Behavior: Groups shared functionality in parent classes, keeping child classes focused on specific features.
Improved Readability: Makes code easier to understand by clarifying relationships between classes.
Polymorphism :
Imagine a world where the same action can take many forms, adapting to the situation at hand. This is the essence of polymorphism in programming, particularly in Java. Just as characters in stories can exhibit different behaviors depending on their circumstances, polymorphism allows methods to perform various functions based on the object that invokes them.
What is Polymorphism?
Polymorphism is a core concept in object-oriented programming that enables a single interface to represent different underlying forms (data types). In simpler terms, it allows objects of different classes to be treated as objects of a common superclass, enabling methods to be called in a flexible manner.
The Story of Polymorphism: Thor and Loki in Asgard
In the majestic realm of Asgard, both Thor and Loki share a common duty to protect their kingdom, but they go about it in vastly different ways. This dynamic showcases the power of polymorphism.
Thor, the God of Thunder: Thor is strong, brave, and noble. When facing a threat, he charges in with his mighty hammer, Mjolnir. His method of attack is straightforward—he embodies valor, wielding thunder to vanquish foes. Thor's combat style reflects his straightforward approach to problems: direct and forceful.
Loki, the Master of Deception: Loki, on the other hand, is a master of cunning and illusion. Known for his ability to shapeshift, he can transform into anyone or anything as the situation demands. Need a fierce beast to scare off enemies? Loki can become one. Want to sneak past guards? He can disguise himself as a trusted ally.
When a challenge arises, Loki doesn't rely on brute strength. Instead, he uses his magical abilities to confuse and outsmart his opponents. He creates illusions, making enemies question what is real and what is not. His quick wit and adaptability allow him to turn the tides of battle in unexpected ways, proving that power isn't just about strength it's also about strategy.
Let’s code it to understand how polymorphism works in java :
In programming, we can define a common method in a parent class that is then overridden by child classes. Here’s how it looks in our Asgard story:
Odin.java
// Parent class representing Odin, the All-Father
class Odin {
public String name; // Name of Odin
private String title; // Title of Odin
private String wisdom; // Wisdom attribute
private int strength; // Strength attribute
// Constructor to initialize Odin's attributes
public Odin(String name, String title, String wisdom, int strength) {
this.name = name;
this.title = title;
this.wisdom = wisdom;
this.strength = strength;
}
// Method to pass the throne to a successor
public void passThrone() {
System.out.println(name + " has passed the throne to his son.");
}
// Common method for protection
public void protect() {
System.out.println(name + " is protecting Asgard!");
}
}
Thor.java
// Class representing Thor, inheriting from Odin
class Thor extends Odin {
private int bravery; // Bravery attribute specific to Thor
private String hammer; // Thor's weapon
// Constructor to initialize Thor's attributes
public Thor(String name, String title, int strength, int bravery) {
super(name, title, "Wisdom of the Ages", strength); // Calling the constructor of Odin
this.bravery = bravery;
this.hammer = "Mjolnir"; // Thor's hammer
}
// Overridden method for Thor's protection approach
@Override
public void protect() {
System.out.println(super.name + " wields " + hammer + " and charges into battle with valor!");
}
// Method for Thor to wield Mjolnir
public void wieldMjolnir() {
System.out.println(super.name + " wields Mjolnir with strength!");
}
}
Loki.java
// Class representing Loki, inheriting from Odin
class Loki extends Odin {
private int cunning; // Cunning attribute specific to Loki
private int magic; // Magic attribute specific to Loki
// Constructor to initialize Loki's attributes
public Loki(String name, String title, int cunning, int magic) {
super(name, title, "Master of Deception", 10); // Calling the constructor of Odin
this.cunning = cunning;
this.magic = magic;
}
// Overridden method for Loki's protection approach
@Override
public void protect() {
System.out.println(super.name + " uses cunning and illusions to confuse his enemies!");
}
// Method for Loki to create illusions
public void createIllusions() {
System.out.println(super.name + " creates an illusion!");
}
}
Main.java
// Main class to execute the program
public class Main {
public static void main(String[] args) {
// Creating an instance of Odin
Odin odin = new Odin("Odin", "All-Father", "Wisdom of the Ages", 100);
// Creating instances of Thor and Loki
Thor thor = new Thor("Thor", "God of Thunder", 90, 95);
Loki loki = new Loki("Loki", "God of Mischief", 80, 85);
// Odin passing the throne
odin.passThrone();
// Using polymorphism to call the protect method
odin.protect(); // Calls Odin's protect method
thor.protect(); // Calls Thor's overridden protect method
loki.protect(); // Calls Loki's overridden protect method
// Thor's unique action
thor.wieldMjolnir();
// Loki's unique action
loki.createIllusions();
}
}
In the above code:
Odin Class: The base class that includes a
protect()
method, which will be overridden by the subclasses.Thor Class: Inherits from
Odin
and overrides theprotect()
method to provide Thor's specific approach to protection. It also includes a method to wield Mjolnir.Loki Class: Inherits from
Odin
and overrides theprotect()
method to provide Loki's unique, cunning approach to protection. It also includes a method for creating illusions.Main Class: The
main
method creates instances ofOdin
,Thor
, andLoki
. It demonstrates polymorphism by calling theprotect()
method on each instance, showing how the same method can have different implementations based on the object that calls it.
Why Polymorphism?
Flexibility: Code can be more flexible and extendable, allowing new classes to be added with minimal changes to existing code.
Reusability: Common interfaces mean you can reuse code across different classes without needing to know their specific implementations.
Maintainability: If the implementation needs to change, only the child class needs to be updated, not the code that uses it.
Cleaner Code: It encourages cleaner and more organized code structures, making it easier to manage and understand.
Just like Thor and Loki embody different approaches to their responsibilities as protectors of Asgard, polymorphism allows methods to adapt to the object invoking them. Loki's ability to shapeshift and approach challenges creatively illustrates how polymorphism enhances flexibility and creativity in programming. This dynamic adaptability makes the design of your code robust and versatile much like the heroes of Asgard themselves!
Conclusion
Object-Oriented Programming (OOP) is a powerful paradigm that enhances code organization and reuse through concepts like inheritance, encapsulation, polymorphism, and abstraction. By exploring characters like Thor and Loki, we see how polymorphism allows for varied behaviors under a common interface, simplifying code management and extending functionality.
I encourage you to dive deeper into OOP concepts and experiment with creating your own classes and inheritance structures. The more you practice, the more intuitive these ideas will become.
Feedback
What did you find most interesting? Are there specific OOP areas you'd like to explore further? Your feedback is important, and I’d love to hear your thoughts!
Happy coding, and keep exploring the fascinating world of Object-Oriented Programming!
Subscribe to my newsletter
Read articles from Akash Das directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Akash Das
Akash Das
Java Developer | Tech Blogger | Spring Boot Enthusiast | DSA Advocate*I specialize in Java, Spring Boot, and Data Structures & Algorithms (DSA). Follow my blog for in-depth tutorials, best practices, and insights on Java development, Spring Boot, and DSA. Join me as I explore the latest trends and share valuable knowledge to help developers grow.