The “data encapsulation” is a fundamental concept of OOPs in Java. It encapsulates the class members (variables) and member functions (methods) of the classes. This encapsulation of related elements into a single place enhances the readability of code. It also helps in limiting the accessibility of data and makes the unit testing process more easy. The “encapsulation” works with other OOP concepts like inheritance, polymorphism, and abstraction to create a well-structured application.

This guide explains the implementation of encapsulation in Java by covering the mentioned sections:

  • What is Encapsulation in Java?
  • What is Data Hiding in Java?

What is Encapsulation in Java?

In encapsulation, the complex working of methods is kept hidden from the rest of the code by placing them into a single unit. For example, the “capsule” is a combination of several medicines. It binds the class variables and methods to prevent access to their state and behavior from another class. The full encapsulation is achieved by making all the data members of the class “private”. These variables are accessed using the “public” scope setter and getter methods set and get the data member’s values.

Let’s dive into a couple of examples to understand the implementation of encapsulation in Java.

Example 1: Encapsulation Using Getter and Setter Methods

In this example, the encapsulation is established using the setter and getter methods. These methods make the class properties read-only or write-only. The “setter” method assigns the values for already declared “private” type variables. On the other hand, the “getter” method retrieves the assigned variable values. For instance, take a look at the below-mentioned code snippet:

class Player {
private String playerName;
private double playerAge;
// Setter Getter for Player Name
public void setName(String name) {
  this.playerName = name;
}
public String getName() {
  return playerName;
}
// Setter Getter for Player Age
public double getAge() {
  return playerAge;
}
public void setAge(double age) {
  if (age > 0) {
  this.playerAge = age;
  } else {
  System.out.println("You Have Inserted Invalid Age Value");
  }
}
}

public class encapsulation{
public static void main(String[] args) {
  Player football = new Player();

  football.setName("Mohammed Salah");
  football.setAge(29);

  System.out.println("The Selected Player Name is: " + football.getName());
  System.out.println("The Player has the Age of: " + football.getAge());
}
}

The explanation of the above code is stated below:

  • First, create a class “Player” that contains two “private” type variables named “playerName” and “playerAge”.
  • Next, define a setter method “setName()” that accepts a single parameter of “name”. Assign this parametric value to the “playerName” variable using a “this” keyword.
  • Then, define a getter method “getName()” that retrieves the “playerName” variable value. The value is then returned using the “return” keyword.
  • Create another getter function “getName()” to return the “playerAge” variable value.
  • Now, define a setter function “setAge()” to set a value for the “playerAge” variable. This method accepts a single parameter of “age” which is assigned to the “playerAge” if it is greater than “0”.
  • After that, the main class “encapsulation” is created and the object of a “Player” class is created named “football”. 
  • Use this object to invoke the “setName()” and “setAge()” methods, and pass them values as parameters. These methods insert the parametric values into encapsulated “playerName” and “playerAge” variables.
  • Finally, call the “getName()” and “getAge()” methods to display the values stored in the “playerName” and “playerAge” variables.

The below output shows the encapsulation of variables using setter and getter methods:

Example 2: Encapsulation Using Class Constructor

In this case, the encapsulated “private” type variables of a class are being accessed using that class constructor:

class Player {

  private String playerName;
  private double playerAge;

  public Player(String name, double age) {
  this.playerName = name;
  if (age > 0) {
  this.playerAge = age;
  } else {
  System.out.println("Invalid age input");
  }
}

public void teamInfo() {
  System.out.println("\nThe Selected Player Name is: " + playerName);
  System.out.println("The Player has the Age of: " + playerAge);
}
}
public class encapsulation {
  public static void main(String[] args) {

  Player player1 = new Player("Jane Doe", 31.5);
  player1.teamInfo();

  Player player2 = new Player("Mohammed Salah", 29.8);
  player2.teamInfo();
  }
}

In the above code block:

  • First, the “Player” class is created and two “private” type variables are created inside it.
  • Next, create a constructor for the “Player” class which accepts two parametric values. Assign these values to the created private type variables after checking their emptiness.
  • After that, define a function “teamInfo()” which displays both private type variables on the console.
  • Now, define a “main()” method and inside it create an object for the “Player” class.  Also, pass both user-defined parametric values as an argument in the class constructor. 
  • Finally, call the “teamInfo()” method using the created class object. This calling displays the encapsulated variable’s values on the console.

The generated output for the mentioned code is shown below:

Example 3: Creation of RealTime Bank Account Using Encapsulation

The encapsulation can be seen in many real-time applications like in the banking sectors, restaurants, team management, and so on. Because it wraps related data into one place and makes code a lot more readable and transparent. 

In the below code snippet, we are going to build a bank account and perform basic operations like withdrawing and depositing the amount. After that, to enhance the code readability the encapsulation concept is applied:

class BankAccount {
private double balance;
public BankAccount(double initialAmount) {
  if (initialAmount >= 0) {
  this.balance = initialAmount;
  System.out.println("\tThe Initial Balance is: £" + this.balance);
  }else {
  System.out.println("The Provided Initial Balance is Invalid!");
  }
}
public void deposit(double amount) {
  if (amount > 0) {
  this.balance += amount;
  System.out.println("You have Deposited: £" + amount);
  showAmount();
  }else {
  System.out.println("The Amount you Entered for Deposit is Invalid");
  }
}

public void withdraw(double amount) {
  if (amount > 0 && amount <= balance) {
  this.balance = this.balance - amount;
  System.out.println("You Have Withdraw: £" + amount);
  showAmount();
  }else {
  System.out.println("\nInsufficient Amount to Fullfil Your Request");
  }
}

private void showAmount() {
  System.out.println("\tYour Current Balance is: £" + balance);
}
}

public class encapsulation {
public static void main(String[] args) {
  BankAccount dummyAccount = new BankAccount(1000);
  dummyAccount.deposit(300.0);
  dummyAccount.withdraw(500.0);
  dummyAccount.withdraw(700.0);
}
}

In the above code snippet:

  • First, create a class “BankAccount” and inside it declare a “balance” named variable having the scope of “private”.
  • Now, call the class constructor that accepts a single parameter of “initialAmount”. Check if this parameter contains a valid value or not. If the value is valid, display it and store it in the “balance” type variable. Otherwise, print an error message.
  • Then, define a “deposit()” method accepting the single double type parameter “amount”.
  • Next, use the “if” statement to check whether “amount” contains a valid value. If the value is valid then, add it to the “balance” variable.
  • After adding the value, print the “amount” that has been added and invoke the custom “showAmount()” method.
  • Now, define a “withdraw()” method which also accepts a single double type parameter of “amount”. 
  • This parameter is first checked for validity and then if it is less than the “balance” variable or not. Because the withdrawal amount must be less than the amount residing inside the bank account.
  • If the condition returns “true” then subtract the parametric “amount” variable from the “balance” variable. The “showAmount()” method is then invoked to display the remaining balance.
  • Now, define a “showAmount()” method to print the “balance” variable on the console.
  • After that, define a “main()” method and create an object for the “BankAccount” class. Also, pass the initial amount as a parameter in the class constructor.
  • Finally, invoke the encapsulated “deposit()” and “withdraw()” methods to perform the desired operation.

The output shows that the basic bank account operations are performed using the “encapsulation” concept:

What is Data Hiding in Java?

The “Data Hiding” concept is like the OOP “encapsulation” concept as both hide the information from specific parts of the programs. The hidden parts are visible only to the specified part like nested classes, within or outside the package, and so on. This data-hiding process is performed using the access modifiers which are named as:

  • Public
  • Protected
  • Default
  • Private

Take a look at the below table to get a quick view of the availability or scope of each access modifier:

ScopePublicProtectedDefaultPrivate
Within ClassYesYesYesYes
Within PackageYesYesYesNo
Same Package by SubclassesYesYesYesNo
Outside Packages by SubclassesYesYesNoNo
GloballyYesNoNoNo

Let’s have a look at these access modifiers and their availability scope.

Public Access Modifier

The “public” access modifier provides maximum availability of its variables and methods. The class properties having the “public” modifier are accessible from the “Same package”, “Outside package”, “Nested Cass”, and “Globally”. The practical example is shown below:

class modifier{
public String name; //Public field

modifier(String name) {
  this.name = name;
}
public void publicMethod() //Public Method
{
  System.out.println("This is a public Type method.");
}
}
public class encapsulation {
public static void main(String[] args) {
  modifier person = new modifier("Maxwell");
  System.out.println("The public type Variable is: " + person.name);
  person.publicMethod();
}
}

The working of above code is as follows:

  • First, the class “modifier” is created. Inside it, the variable “name” is created having the scope of “public”. The value of this variable is assigned via the class constructor from the “main()” method.
  • Then, a “public” scoped method “publicMethod()” is created that displays random text on the console.
  • After that, create another class “encapsulation” which contains a “main()” method. Inside this method create an object for the “modifier” class and pass the random parametric value.
  • In the end, use this object to print the “name” variable and “publicMethod” created inside the “modifier” class.

The output shows that the variable and method are called because of their “public” scope:

Protected Access Modifier

The “protected” access modifier provides maximum availability after the “public” methods. Although its availability is not global, the user can access its elements from another class, package, or nested class. The user can declare the methods or variables with a protected modifier using the below syntax:

//Protected Scope Class variables
protected String name;

//Protected Scope member methods
protected void protectedMethod() {
  System.out.println("Hello! This is a protected method.");
}

Default Access Modifier

The “default” access modifier offers less availability of class properties than the “protected”. It is accessible inside the single package and inherited classes. However, it is unavailable in packages opposite to “protected” or “public” modifiers. Moreover, its scope is not global; it has a local scope that resides inside a single package. The declaration of class properties having the default access modifier:

//Default Scope Class variables
String name;

//Default Scope member methods
void defaultMethod() {
  System.out.println("Hello! This is a default method.");
}

Private Access Modifier

The private access modifier offers maximum abstraction or data-hiding features by providing minimum scope for class properties. The methods or variables having the scope of “private” are accessible only within the class where they are defined. They are inaccessible in any other positions like a nested child or outside the package and generate an “Unresolved compilation problems” error. 

Let’s define class properties with the access modifier of “private” and access them outside of their scope to visit any generated error:

class modifier{
private String name;

modifier(String name) {
  this.name = name;
}
private void privateMethod() {
  System.out.println("\nThis is a private Type method.");
}
}

public class encapsulation {
  public static void main(String[] args) {
  modifier scope = new modifier("Maxwell");
  System.out.println("The private type Variable is: " + scope.name);
  scope.privateMethod();
  }
}

In the above code snippet:

  • First, create a class “modifier” and inside it declare a “private” scoped “name” variable. This variable is initialized by the class constructor. 
  • Next, define a method “privateMethod()” having the access modifier of “private”.
  • Then, create another class “encapsulation” and define a “main()” method in it. Also, create an object for the “modifier” class named “scope” and pass a random value as an argument for the “modifier” class constructor. 
  • In the end, using this object access the private scoped variable and method.

The generated output shows the generation of error due to the usage of private type class properties in another class:

This guide has explained the OOP encapsulation concept in Java.

Conclusion

An encapsulation is a technique to hide the class properties(methods and variables) from the rest of the code by placing them in one place. The placement is done by wrapping the line of code that leads to the creation of the same output. This wrapping is done by placing code in one class that has the access modifiers of “private” or else. To select these wrapped elements the setter and getter methods with “public” scope are used. These encapsulated elements can also be accessed using the class “constructor”. That’s all about the encapsulation in Java.