In this article, we’ll talk about one of the most important Object Oriented Programming concepts, Encapsulation in Java.
1. What is Encapsulation in Java
Encapsulation in Java is a way to wrap members and methods of a class, into a single unit. Additionally, the goal of encapsulation is to hide all the data that should not be accessed by other classes.
This means that:
- For members of a class, we just declare them as private and create public setters and getters
- For methods of a class, we restrict them to the most private access modifier possible.
2. How to Achieve Encapsulation in Java
There are three types of encapsulation: read-only, write-only, and read-write.
2.1 Read-Only Encapsulation
Let’s say we have a class Car
:
public class Car { private String brand; private String model; private int horsePower; private int engineVolumeInCC; public Car(String brand, String model, int horsePower, int engineVolumeInCC) { this.brand = brand; this.model = model; this.horsePower = horsePower; this.engineVolumeInCC = engineVolumeInCC; } }
If we create a main method inside Car.java, we can access and modify all of the members.
However, we CANNOT access or modify any member of the Car class from any other class.
To make our class read-only, we need to create getter methods – public methods whose purpose is to return the value of a member.
As a result, Car.java
will look like this:
public class Car { private String brand; private String model; private int horsePower; private int engineVolumeInCC; public String getBrand() { return brand; } public String getModel() { return model; } public int getHorsePower() { return horsePower; } public int getEngineVolumeInCC() { return engineVolumeInCC; } public Car(String brand, String model, int horsePower, int engineVolumeInCC) { this.brand = brand; this.model = model; this.horsePower = horsePower; this.engineVolumeInCC = engineVolumeInCC; } }
Then, we can create a driver class, Main.java
, and access members of a Car
object:
Car car = new Car("Bugatti", "Veyron", 1000, 4000); System.out.println(car.getBrand()); System.out.println(car.getModel()); System.out.println(car.getHorsePower()); System.out.println(car.getEngineVolumeInCC());
This will print:
Bugatti Veyron 1000 4000
Of course, we will never be able to modify any of the members of Car
class.
2.2 Write-only Encapsulation
We can even make the Car class as write-only, by removing the public getter methods that we created and by adding setter methods – public methods whose purpose is to assign a new value to a member.
Car car = new Car("Bugatti", "Veyron", 1000, 4000); car.setBrand("Opel"); car.setModel("Astra"); car.setHorsePower(105); car.setEngineVolumeInCC(1600);
The only way to verify that the members of this car object have changed is through reflection or a debugger:
2.3 Read-Write Encapsulation
To achieve read-write encapsulation, you just have to include both setters and getters, then both reading and writing will be possible:
public class Car { private String brand; private String model; private int horsePower; private int engineVolumeInCC; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public int getHorsePower() { return horsePower; } public void setHorsePower(int horsePower) { this.horsePower = horsePower; } public int getEngineVolumeInCC() { return engineVolumeInCC; } public void setEngineVolumeInCC(int engineVolumeInCC) { this.engineVolumeInCC = engineVolumeInCC; } public Car(String brand, String model, int horsePower, int engineVolumeInCC) { this.brand = brand; this.model = model; this.horsePower = horsePower; this.engineVolumeInCC = engineVolumeInCC; } }
2.4 Encapsulation of Methods and Constructors
We can encapsulate the methods and constructors of the class according to the following rules:
- If a method/constructor is meant to be used only in this class, it should always be declared private.
- Should a method/constructor is meant to be used by classes in the same package only, it must not have any access modifier(default).
- If a method/constructor is meant to be used by a child class outside of the package to which this class belongs, it should have the protected access modifier.
- Finally, if a method/constructor does not fall into any of the categories above, it should be declared as public.
Of course, these rules apply to classes and not abstract classes or interfaces, as they have different rules.
You can read more about access modifiers here, abstract classes here, and interfaces here.
3. Why Do We Need Encapsulation?
- A class can be made as read-only if we want other classes to be able to access its data
- A class can be made as write-only if for whatever reason we do not want any other class to use the data of this class
- A class can be made both accessible and modifiable if the application requires it
As a result, we can choose exactly what permissions other classes will have with regard to a specific class.
4. Read-only Encapsulation with Java 14 Records
Since Records were introduced in Java 14, we can create read-only Java classes with just a single line.
Nevertheless, we cannot create write-only or read-write classes as all fields of a record are final.
Below you can find the record version of Car.java:
public record Car(String brand, String model, int horsePower, int engineVolumeInCC) { }
As you would expect, we can create a new car and access its members, but we cannot modify them:
Car car = new Car("Bugatti", "Veyron", 1000, 4000); System.out.println(car.brand()); System.out.println(car.model()); System.out.println(car.horsePower()); System.out.println(car.engineVolumeInCC());
Records do not use the
getXXX()
naming but just thefieldName()
naming.
This will print the same as before:
Bugatti Veyron 1000 4000
5. Conclusion
By now you should know what encapsulation in Java is and how you can achieve it both for members and methods.