Master Encapsulation in Java with Simple Examples

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.

Crio Bytes
Get real software development experience. Work on challenging projects. Learn in-demand tech skills to excel in your career

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

  1. Encapsulation
  2. Abstraction
  3. Inheritance
  4. Polymorphism

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.

Learn Encapsulation in C++ with Simple Example Program
How is Encapsulation implemented? Why do we need encapsulation? Types of Encapsulation in C++. Real-world implementation example.

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

10-Minute Guide To Abstraction In Java
Learn Abstraction in Java with simple programs, real world examples, and test your understanding with fun quizzes.

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?

  1. Hello there, How can I help you today?
  2. Hi there, Have you checked out Crio’s Backend Developer program?
  3. Compilation fails at //1
  4. 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 _______

  1. Methods
  2. Objects
  3. Classes
  4. None of the above

Q5. Encapsulation is implemented using _______ keyword on data

Do it Yourself

  1. 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.
  2. Encapsulate the Polygon class.

Further Reading

10-Minute Guide To Abstraction In Java
Learn Abstraction in Java with simple programs, real world examples, and test your understanding with fun quizzes.
Understand Polymorphism in C++ with Simple Examples
Polymorphism is the ability of a message or object to be shown in multiple forms. Learn this important OOPs concept with useful resources and easy examples.
Inheritance in C++ Simplified for Programmers
Inheritance is a feature that lets one class inherit traits and characteristics from another. Read more to learn in detail.
What’s Special About Data Abstraction in C++?
Abstraction separates interface and actual implementation. So, how to implement abstraction in C++? What is an Abstract Class?