One of the four pillars of the OOP concept is encapsulation. Inheritance, polymorphism, and abstraction are the other three.
In Java, encapsulation combines data (variables) and code that acts on the data (methods) into a single unit. Encapsulation means that a class’s variables are concealed from other classes and can only be accessed through its current class’s methods. Consequently, it’s also known as data concealment.
The goal of encapsulation is to keep implementation details hidden from users. Other members of the same class can only access a private data member. No outside class has access to a class’s private data member (variable).
However, suppose we create public getter and setter methods to update (for example, void setEmployee(int ID)) and read (for example, int getEmployee()) the private data fields. In that case, the outside class can use those public methods to access those private data fields. Private fields and their implementation are hidden from outside classes since public methods can only access data.
Encapsulation in Java
To implement encapsulation in Java,
- Declare a class’s variables as private.
- To alter and inspect the variables’ values, provide public setter and getter methods.
Following that, we’ll look at the syntax you use when implementing encapsulation in Java.The syntax is as follows:
<Access_Modifier> class <Class_Name> { private <Data_Members>; private <Data_Methods>; }
Let’s explore the sample program below to understand the encapsulation process better.
Example: The following code illustrates how to implement Encapsulation in Java.
/* File name : CodeEncapsulation.java */ public class CodeEncapsulation { private String name; private String idNum; private int age; public int getAge() { return age; } public String getName() { return name; } public String getIdNum() { return idNum; } public void setAge( int newAge) { age = newAge; } public void setName(String newName) { name = newName; } public void setIdNum( String newId) { idNum = newId; } }
The public setXXX() and getXXX() methods provide access to the CodeEncapsulation class’s instance variables. These methods collectively are commonly referred to as getters and setters. As a result, any class that needs access to the variables should use these getters and setters. The CodeEncapsulation class variables are accessed with the following program:
/* Filename : RunCodeEncapsulation.java */ public class RunCodeEncapsulation { public static void main(String args[]) { CodeEncapsulation encapVar = new CodeEncapsulation(); encapVar.setName("Green"); encapVar.setAge(53); encapVar.setIdNum("TY905"); System.out.print("Name : " + encapVar.getName() + " Age : " + encapVar.getAge()); } }
Methods of Getter and Setter
Getter
A getter method is a method that allows you to access and retrieve an instance of a private variable.
Setter
The setter method is capable of updating or setting a private variable instance.
package Codeunderscored; public class CodeEmployee { private String c_name; public String getCodeName() { return c_name; } public void setCodeName(String c_name){ this.c_name=c_name ; } } package Codeunderscored; public class CodeTestGettersSetters { public static void main(String[] args) { CodeEmployee empOne = new CodeEmployee(); empOne.setCodeName("Green"); System.out.println(empOne.getCodeName()); } } //Output: Green
Encapsulation’s Advantages
- You can make a class read-only or write-only by giving only a setter or getter method.
- In other words, the getter and setter methods are optional.
- The most common IDEs provide the ability to generate getters and setters. Creating an enclosed class in Java is thus simple and quick. By extension, this eases the entire process of writing programs.
- Encapsulation in Java allows you to reuse your code.
- Encapsulation allows you to make modifications to existing code swiftly.
- It gives you complete control over your data. You can write the logic inside the setter method if you only want to set the value of id to be greater than 100. You can implement logic in the setter methods to prevent negative integers from being stored.
- A class can have complete control over the content of its fields.
- Because other classes will not be able to access the data through the private data members, it is a way to achieve data hiding in Java. It’s simple to test the enclosed class. As a result, it is more suitable for unit testing.
- Unit testing code written with encapsulation is simple.
Encapsulation in Java: A Simple Example
Let’s look at a simple encapsulation example with only one field and setter and getter methods.
// A Java class that is completely enclosed. It has a getter and setter method and a private data member. package com.codeunderscored; public class CodeStudent{ //the data member is private private String name; // name's getter method public String getName(){ return name; } //names' setter method public void setName(String name){ this.name=name } } //The Java class here tests the encapsulated class above package com.codeunderscored; class CodeTest{ public static void main(String[] args){ //first, create an instance of the encapsulated class CodeStudent codeStudent = new CodeStudent(); // using the setter method to assign value to the name member codeStudent.setName("Green"); // Using the name's get method to fetch the value of the name member System.out.println(codeStudent.getName()); } }
The following is a read-only class.
//Only getter methods are available in this Java class. public class CodeStudent{ //The data member is private private String code_school="Code Underscored"; // code_school's getter method public String getCodeSchool(){ return code_school; } }
You can no longer edit the value of the code_school data member “Code Underscored .”
codeStudent.setCodeSchool("w3schools");// the code renders a compile time error
The following is a write-only class:
//Only setter methods are available in this Java class. public class CodeStudent{ //data member is private private String code_school; //code_school's setter method public void setCodeSchool(String code_school){ this.code_school=code_school; } }
You can no longer access the code_school’s value; instead, you can only update the value of the code_school data component.
System.out.println(codeStudent.getCodeSchool());//There is no such procedure, resulting in a Compile Time Error. System.out.println(codeStudent.code_school);//Because the college data member is private, there is a Compile Time Error. //As a result, it is not accessible from outside the class.
Another Java example of encapsulation
Let’s look at another encapsulation example with only four fields and setter and getter functions.
// An Account class that is completely contained. // It has a getter and setter method and a private data member. class CodeAccount { //The data members are private private long code_acc_no; private String code_name,code_email; private float code_amount; //These getter and setter methods are all public public long getCodeAccNo() { return code_acc_no; } public void setCodeAccNo(long code_acc_no) { this.code_acc_no = code_acc_no; } public String getCodeName() { return code_name; } public void setCodeName(String code_name) { this.code_name = code_name; } public String getCodeEmail() { return code_email; } public void setCodeEmail(String code_email) { this.code_email = code_email; } public float getCodeAmount() { return code_amount; } public void setCodeAmount(float code_amount) { this.code_amount = code_amount; } } //A Java class for testing the encapsulated Account class. public class CodeTestEncapsulation { public static void main(String[] args) { //creation of CodeAccount class instance CodeAccount codeAccount=new CodeAccount(); //Using the setter methods to set values codeAccount.setCodeAccNo(111THY78645); codeAccount.setCodeName("Code Underscored"); codeAccount.setCodeEmail("code@codeunderscored.com"); codeAccount.setCodeAmount(250f); // using the getter methods to fetch respective values System.out.println(codeAccount.getCodeAccNo()+" "+codeAccount.getCodeName()+" "+codeAccount.getCodeEmail()+" "+codeAccount.getCodeAmount()); } }
Example: Encapsulation in Java
class CodeArea { // fields for calculating the area int c_length; int c_breadth; // constructor for values initialization CodeArea(int c_length, int c_breadth) { this.c_length = c_length; this.c_breadth = c_breadth; } // method responsible for area calculate public void getCodeArea() { int resultantArea = c_length * c_breadth; System.out.println("Resultant Area is: " + resultantArea); } } class Main { public static void main(String[] args) { // create object of Area // pass value of length and breadth resultantArea rec = new resultantArea(12, 16); rec.getCodeArea(); } }
We established a class called CodeArea in the previous example. The class’s primary goal is to compute the area. Two variables, c_length, and c_breadth, plus a method, getCodeArea, are required to determine the area. As a result, we combined these attributes and methods into one class.
The properties and methods of this class can also be accessible from other classes. As a result, this is not data concealment. It is only a summary. We’re just grouping comparable codes. While many people think of encapsulation as data hiding, this is not totally accurate. Encapsulation is the grouping together of similar fields and procedures. You can use it to conceal information. Data hiding is, therefore, not the same as encapsulation.
What is the purpose of encapsulation?
Encapsulation in Java allows us to group relevant fields and methods, making our code clearer and easier to read.
It aids in the management of our data fields’ values. For instance,
class CodeEmployee { private int age; public void setCodeAge(int c_age) { if (age >= 0) { this.c_age = c_age; } } }
The c_age variable is now private, and logic is applied inside the setCodeAge() method. c_age is no longer a negative factor. Our class fields can exist as read-only or write-only. The latter is dependent on either using the getter and setter methods. For instance,
getCodeName() // provides access for read-only setCodeName() // provides access for write-only
It aids in the decoupling of system components. We can, for example, divide code into multiple bundles. These detached components (bundle) can be built, tested, and debugged on their own time. Furthermore, any changes made to one component have no impact on the others. Encapsulation can also be used to conceal data. If we set the length and breadth variables to private in the previous example, access to these fields is restricted. They are also kept secret from the upper classes. It is referred to as data concealment.
Hiding Data
Data hiding is a method of limiting data members’ access to our data by concealing implementation details. Encapsulation also allows data to be hidden. You can hide data by using access modifiers.
The following are the four access specifiers:
Default
The first line of data concealing is the default. In case no access specifier is explicitly specified for a given Java class, the compiler will use ‘default’ as the access specifier. The default access specifier is very similar to the public access specifier in terms of functionality.
Public
The least restrictive access modifier is this. Methods and properties with the public modifier can be accessible both within and outside of your current class.
The public API of your class and any component you include includes public methods and attributes. It is usually never a good idea for any attribute. Therefore use caution when applying this modification to a method. When a method is made publicly available, it must be adequately described and capable of handling input values. Remember that this technique will be utilized by a portion of your program, making it difficult to update or eliminate.
Your public API should, in general, be as light as feasible. Only the methods used by other portions of the program or external clients are included in public APIs. The public access specifier gives class access to its access specifications for access from anywhere in the program.
Example:
package Codeunderscored; class CodeVehicle { public int c_tires; public void CodeDisplay() { System.out.println("Codeunderscored owns a vehicle."); System.out.println("The vehicle has " + c_tires + " number of wheels."); } } public class CodeTestVehicle { public static void main(String[] args) { CodeVehicle codeVehicle = new CodeVehicle(); codeVehicle.tires = 8; codeVehicle.display(); } } //Output: Codeunderscored owns a vehicle. // It has 8 number of wheels.
Private
The private modifier, the most restrictive and widely used access modifier, restricts access to an attribute or method to other members of the same class. This attribute or method is not accessible to subclasses or other classes in the same or different package.
By default, choose the private modifier for all attributes and internal methods you should not call from outside classes. You may need to make an exception to this rule when using inheritance. Also, some subclasses that require direct access to a property or internal method should be exempted. Instead of using the private modifier, you should use the protected modifier. The data members use the private access specifier for accessibility. On the other hand, the data methods are limited to the class itself.
Example:
package Codeunderscored; class CodeEmployee { private int code_rank; public int getCodeRank() { return code_rank; } public void setCodeRank(int code_rank) { this.code_rank = code_rank; } } public class CodeTestEmployee { public static void main(String[] args) { CodeEmployee codeEmployee = new CodeEmployee(); codeEmployee.setCodeRank(1); System.out.println("The Code Employee's rank is " + codeEmployee.getCodeRank()); } } // Output: // The Code Employee's rank is 1
Protected
Like the private access specifier, the protected access specifier safeguards the class methods and members. The main distinction is that instead of restricting access to a single class, the package as a whole is restricted. Internal methods that must be called or overridden by subclasses are usually given the protected modifier. You can also use the protected modifier to give subclasses direct access to a superclass’s internal attributes.
package Codeunderscored; class Computer { protected String code_stream; protected void CodeDisplay() { System.out.println("Hello, people refer to me as the " + code_stream + " Technology"); } } public class SuperComputer extends Computer { public static void main(String[] args) { SuperComputer superComputer = new SuperComputer(); superComputer.stream = " super computer because of my "; SuperComputer.CodeDisplay(); } } //Output: // Hello, people refer to me as the super computer because of my Technology
Example: Data hiding
class CodeEmployee { // private field private int c_age; // getter method public int getCodeAge() { return c_age; } // setter method public void setCodeAge(int c_age) { this.c_age = c_age; } } class Main { public static void main(String[] args) { // creating a Person's Object CodeEmployee empOne = new CodeEmployee(); // using the setter method to change the c_age empOne.setCodeAge(32); // using the getter method to access c_age System.out.println("My Current age is :" + empOne.getCodeAge()); } }
We have a private field c_age in the example above. You cannot access it from outside the class because it’s private. In this example, we utilized the public methods getCodeAge() and setCodeAge() to retrieve c_age. These are known as getter and setter methods. We were able to prohibit illegal access from outside the class by making age secret. This is data concealment. Attempts to access the c_age field from the Main class will get an error.
// error: c_age has private access in CodeEmployee empOne.c_age = 36;
Encapsulation vs. Abstraction
Abstraction and encapsulation are frequently confused. Let us investigate-
- Encapsulation is mainly concerned with the “How” of achieving functionality.
- Abstraction is primarily concerned with “What” a class can accomplish.
A mobile phone is an easy way to understand the difference where the circuit board’s sophisticated circuitry is encapsulated in a touch screen, with an interface to abstract it away.
Example: Program for demonstrating variable access using encapsulation in Java
class CodeEncapsulate { // The following private variables declared below can only be accessed by the class's public methods private String codeName; private int codeRoll; private int codeAge; // getting the method for age to access // private variable codeAge public int getCodeAge() { return codeAge; } // getting the method for name to access // private variable codeName public String getName() { return codeName; } // getting the method for roll to access // private variable codeRoll public int getCodeRoll() { return codeRoll; } // setting the method for age to access // private variable codeAge public void setCodeAge(int codeAge) { codeAge = codeAge; } // setting the method for codeName to access // private variable codeName public void setCodeName(String codeName) { codeName = codeName; } // set method for roll to access // private variable geekRoll public void setRoll(int newRoll) { geekRoll = newRoll; } } public class TestCodeEncapsulation { public static void main(String[] args) { CodeEncapsulate codeEncapsulate = new CodeEncapsulate(); // setting values of the variables codeEncapsulate.setCodeName("Green"); codeEncapsulate.setCodeAge(34); codeEncapsulate.setCodeRoll(198); // Displaying the variable values System.out.println("Code's name: " + codeEncapsulate.getCodeName()); System.out.println("Code's age: " + codeEncapsulate.getAge()); System.out.println("Code's roll: " + codeEncapsulate.getRoll()); // Direct access of CodeRoll is not possible because of encapsulation // System.out.println("Code's roll: " +// codeEncapsulate.CodeName); } }
Conclusion
In Java, encapsulation combines code and data into a single unit, such as a capsule containing several drugs. Further, in Java, we can create a fully enclosed class by keeping all of the class’s data members private. We can now set and get data using setter and getter methods. The Java Bean class represents a fully contained class. It’s frequently used to implement a data-hiding method. This strategy minimizes the accessibility of properties to the current class and controls and restricts external access to these attributes using public getter and setter methods. These methods let you specify which characteristics can be read or updated and validate the new value before making a change.
Encapsulation has the basic property of hiding data and ensuring the security of user data. Encapsulation is a good OOP practice. However, it works best when combined with a reliable APM solution like Retrace for error monitoring.