Java is an Object-Oriented Programming Language
Object-oriented Programming is about describing real-world objects in code. Object-oriented thinking can be applied everywhere.
For example, a post on Instagram, or a product on an e-commerce website can be thought of as objects. Every item for that matter -- be it a pencil, a car, the moon, has its own specifications, state, behavior, and functionality that can be imitated in code.
Get hands-on with OOPs concepts. Learn how to apply them in real-world scenarios.
In Java, you can do this with two concepts.
- Class
- Object
A class contains attributes and methods. It is a blueprint from which objects are created. An object can be thought of as an instance of a class.
There is a feature in LinkedIn where you can create polls. Here the template to create a poll can be thought of as a class, and the poll created can be thought of as an object. Depending on the class definition, hundreds of objects can usually be created from a class.
Objects can have states (represented by attributes) and behavior (represented by methods).
For example, a student object can have attributes such as name, grade, and course.
A phone object can have behaviors such as switching off and on and making calls.
You can think of a class as a blender, and objects as different fruit juices made from it!
Here is another example of a smartphone class with its objects.
Design Principles of Object-Oriented Programming
This blog focuses on Encapsulation.
You will come across many examples of Encapsulation that will deepen your understanding of a variety of concepts.
Access Modifiers
To understand Encapsulation in-depth, one must be aware about access modifiers in Java.
Classes, attributes, methods, and objects are mainly used to write programs in Java. Usually, a class is defined within a package (which is a collection of closely related classes).
Java provides mainly four levels of visibility which can be applied to classes and all other members of a class (such as attributes, methods, etc.). These are called access modifiers.
Public: If you declare something as public, it can be accessed from any other class in the application, irrespective of the package in which it is defined.
Private: If you declare something as private it can be accessed only inside the class that defines it. Other classes in the same package and classes that inherit the class it is defined in also cannot access it. This makes private the most restrictive access modifier.
Protected: If you declare something as protected, it can be only accessed by the class that defines it, its subclasses, and classes of the same package. Even if the subclass is there in another package, it is still accessible. This makes protected access less restrictive than private.
Package: If you don’t specify a modifier, you give default access to the entity. It is also known as package access because it can only be accessed by classes within the same package. If you have a subclass defined in another package, it cannot see the default access attribute or method.
Let's understand this better with an example
Polygon.java
package polygon;
public class Polygon {
protected int edges;
private String poly_name;
void printEdges(){
System.out.print("Number of edges: " + edges);
}
public void printInfo(){
System.out.println("Number of edges: " + edges + "Name of polygon: " + poly_name );
}
}
Hexagon.java
package Hexagon;
import polygon.Polygon;
//creating a subclass Hexagon from the parent class Polygon using the extends //keyword. All the methods and variables in Polygon are inherited by Hexagon
class Hexagon extends Polygon {
public static void main(String args[]){
Hexagon h = new Hexagon();
h.edges = 6;
//compiles because edges is declared as protected
h.poly_name = "hexagon";
//compilation error as private variables cannot be accessed by subclasses
h.printEdges();
//compilation error as this method has default (package) access
h.printInfo();
//compiles as this method is public
}
}
What is Encapsulation?
Imagine the chaos that would ensue, if negative values were allowed in most of the world’s applications. Your bank savings, number of copies of a product on an e-commerce app, cell phone numbers, birthdays, velocity of a rocket, etc.
Encapsulation puts a protective shield around instance variables so that nobody can modify or set them to something inappropriate.
Encapsulation literally means the action of enclosing something. In Object-Oriented Programming encapsulation is implemented by binding methods and attributes into a single unit, called a class. Encapsulation enables data hiding.
Say you are writing a simple program for a machine that serves tea. As a programmer, you have to think about the internal logic required to create a tea serving machine. You will need to make sure that the machine serves tea only according to the teacup size, or else the tea can spill over. What else? You might have to make sure the user selects from a certain set of tea flavors.
Without Encapsulation here’s what can happen
- Another programmer could tamper with the amount of tea that the machine can pour per serving. Tea might spill because of this, or the quantity of tea poured might become too little.
- Another programmer could interchange the flavors, so you will get the wrong tea flavor when you select a tea.
With Encapsulation however
- You protect the variable that sets the amount of tea the machine can pour per serving. You restrict access to this variable.
- You restrict access to the tea flavors. You can give control safely. For example, another programmer looking to make a better tea machine, can inherit the properties and methods of your tea class, and add new flavors to the new tea machine.
- The internal logic of how the tea machine works is hidden from the user. No access to it is given to the user as well.
Learn Encapsulation by completing fun activities - Start now!
Key ideas of Encapsulation
- Wrapping attributes and methods that manipulate those attributes, in a class.
- Restricting access to certain data and methods to only within a particular class.
Say you want to create a class defined for a smartphone with - model and storage.
Without Encapsulation your class will look like this
class Smartphone {
int storage = 100;
String model;
}
If the properties are defined this way, any class can change the phone storage. So, you need a way to make sure nobody tampers with important specifications of your application that should not be modified.
There are generally three things that can be done to implement Encapsulation in Java.
- Make the instance variables private.
- Use getter methods - these are methods that retrieve data
Syntax of getter : public returnType getPropertyName()
- Use setter methods - these are methods that change data
Syntax of setter : public void setPropertyName()
Let’s write a simple program to implement Encapsulation for the smartphone class.
Smartphone.java
package smartphone;
//Define a class Smartphone within a package called smartphone. You can think of //packages as folders which contains your java files
public class Smartphone {
//Step 1 : Make instance variables private
private int storage;
private String model;
//Step 2: Write public setter methods with validation checks if necessary
public void setStorage(int phoneStorage){
if(phoneStorage < 100 || phoneStorage > 256 ){
storage = 100;
} else {
storage = phoneStorage;
}
}
public void setModel(String phoneModel){
model = phoneModel;
}
//Step 3: Write public getter methods
public int getStorage(){
return storage;
}
public String getModel(){
return model;
}
}
Once you type this, compile with the command `javac Smartphone.java` in the appropriate directory.
Then execute the command `java Smartphone.java`
Follow this for Iphone.java as well.
Iphone.java
Package iphone
//importing the Smartphone class within another package iphone
import smartphone.Smartphone;
public class Iphone {
//the main method
public static void main(String args[]){
//Instantiating an object phone belonging to the Smartphone class
Smartphone phone = new smartPhone();
//calling the method setStorage on phone
phone.setStorage(150);
//calling the method setModel on phone
phone.setModel("iphone");
//setModel will only accept strings
//java throws a compilation error if you pass an argument of a //different data type
phone.storage = 67;
// Compilation error: storage has private access in smartPhone
System.out.println(redmi.getModel() + " " + redmi.getStorage());
}
}
A public setter has the power to modify a private instance variable.
Does this invalidate the concept of Encapsulation? No.
Notice that in setters you can include validation checks. Setters work like a fuse. If the caller provides an invalid input to the setter the private variable is not affected. Setter methods must be designed keeping this in mind. Setters help dictate the changes that can be made to the application, and protect it from unwanted changes.
Encapsulation is usually implemented in this fashion. But it is more than that. How classes, variables, and methods are declared and defined depends on your application.
Also read
Test your understanding
Q1. In the code given below, length is declared as private in the class Square.mySquare, is an instance of the class Square. Does Java throw any error?
public class Square {
private double length = 4;
//Area of a square
public double getArea(){
return length*length;
}
public static void main(String[] args){
Square mySquare = new Square();
System.out.println(mySquare.length + " " + mySquare.getArea());;
}
}
Q2. Look at the code given below
public class CrioBeaver {
private String greet = "Hello there, How can I help you today?";
public static void main(String[] args) {
Greeting g = new Greeting();
g.greet = “Hi there, Have you checked out Crio’s Backend Developer program?”; // 1
System.out.println(g.greet); // 2
}
}
What is the output?
- Hello there, How can I help you today?
- Hi there, Have you checked out Crio’s Backend Developer program?
- Compilation fails at //1
- Compilation fails at //2
Q3. Study the code given below
package message;
public class Message {
private void msg(){
System.out.println("Your OTP is 5078");
}
}
package display;
import message.Message;
class Display extends Message{
public static void main(String args[]){
Message obj = new Message();
obj.msg();
}
}
What should you change in the code so that the compiler doesn’t throw an error?
Static keyword
An attribute specified using the static keyword is shared across all instances of the class.
Example: oop_principles declared without the static keyword
public class OOP{
private int oop_principles = 0;
public void printAttribute() {
System.out.println(oop_principles);
}
public static void main(String[] args){
OOP obj_1 = new OOP();
obj_1.oop_principles = 4;
obj_1.printAttribute();
OOP obj_2 = new OOP();
obj_2.printAttribute();
}
}
Output
4
0
Oop_principles declared with the static keyword
public class OOP{
private static int oop_principles = 0;
public void printAttribute() {
System.out.println(oop_principles);
}
public static void main(String[] args){
OOP obj_1 = new OOP();
obj_1.oop_principles = 4;
obj_1.printAttribute();
OOP obj_2 = new OOP();
obj_2.printAttribute();
}
}
Output :
4
4
Declaring the method printAttribute() also as static
public class OOP{
private static int oop_principles = 0;
public static void printAttribute() {
System.out.println(oop_principles);
}
public static void main(String[] args){
OOP obj_1 = new OOP();
obj_1.oop_principles = 4;
obj_1.printAttribute();
OOP.printAttribute();
}
}
Final keyword
Besides hiding data, we can also make them impossible to modify with the final keyword.
If the final keyword is applied to variables, its value cannot be changed once it has been initialized.
Note : With the static keyword, the method or attribute can be accessed even without creating an instance of a class
public class finalAttr {
private final int flavours_of_java = 3;
private final String example_flavour = "Standard_Edition";
public static void main(String[] args){
finalAttr obj = new finalAttr();
finalAttr.flavours_of_java = 4; //Compile time error
}
}
Method Overriding
If a subclass declares the same method present in the base class, it is known as method overriding. Here class override is a subclass of javaFlavours, which is specified using the extends keyword.
If a final keyword is applied to a method, that method cannot be overridden
public class javaFlavours {
public String example_flavour ;
public /*final*/ void setFlavour(String flavour){
example_flavour = flavour;
}
public void printFlavour(){
System.out.println(example_flavour);
}
public static void main(String[] args){
override obj = new override();
obj.setFlavour("Enterprise Editon");
obj.printFlavour();
}
}
class override extends javaFlavours {
//if you uncomment final keyword you get compiler error: cannot override the final method from javaFlavours
public void setFlavour(String flavour){
example_flavour = flavour + " " + "Micro Edition";
}
}
Output :
Enterprise Edition Micro Edition
Why use Encapsulation in Java?
Encapsulation is needed for several reasons
- It implements data hiding and protects data from unwanted manipulation.
- The functionality of the application is defined in one place, that is, one single class. Public access to this functionality is safely provided.
- It helps mitigate the impact of change.
- Applications are not developed by a single person. Often you work in a team. Encapsulation makes sure the code is used correctly. It helps make changes to it without breaking everyone’s code.
- Setters and getters help revise code safely.
Best practices for Encapsulation
- Everything that is visible can be used by someone (and any change you apply to it later may break other codes).
- Every variable which is visible and can be changed will be changed by someone (and you will not have any control over how it will be changed).
- Any classes that can be subclassed and methods that can be overridden will eventually be subclassed/overridden.
- Always apply the tightest possible visibility to any variable or method, ideally private, which makes them visible only to the current class.
- Mark a variable as final, if you want to make it impossible to modify.
- If a method does not need to be overridden or a class does not need to be subclassed, mark them as final.
Note
- Setter methods cannot be overridden. This is because they act on private variables. So if a subclass tries to override a setter method, using the private variable within the method, the compiler will throw an error. Therefore, the key to Encapsulation is the private keyword.
- A real-world example of a class that requires a private final keyword.
public class BankAccount {
private final int account_number = 123;
}
Advantages of Encapsulation
Control over data
You can exercise control over the data by providing validation checks in setter methods.
Reusability
Encapsulation enables the reusability of code that has already been tested.
Read-only and Write-only classes
If you provide only a getter method inside the class, you can make it a read-only class.
If you provide only a setter method inside the class, you can make it a write-only class.
Integrity and Security
Since Encapsulation restricts access to data, the integrity and safety of data is maintained.
Conclusion
Encapsulation is the process of wrapping up data and methods in a single unit, which is a class.
Encapsulation is only one of the principles of OOP. In practice, Encapsulation, inheritance, polymorphism, and abstraction work together.
It ensures robust and safe software development, since it provides access to data, without compromising its integrity.
Test your understanding
Q4. Encapsulation is supported by _______
- Methods
- Objects
- Classes
- None of the above
Q5. Encapsulation is implemented using _______ keyword on data
Do it Yourself
- Remember reading about a tea-making machine? Try to write code for it by applying Encapsulation. Use a switch case to validate available tea flavors.
- Encapsulate the Polygon class.