What are Modifiers?
Modifiers are keywords in Java that you add to declarations of classes, methods, and variables to change their meaning. They are divided into two main categories:
- Access Modifiers: Control the visibility or scope (who can access the code). Also known as Access Control Modifiers.
- Non-Access Modifiers: Control the behavior or functionality, but not the scope.
Using modifiers correctly is a key part of Object-Oriented Programming, as it helps you create secure, reusable, and well-structured code.
Controlling Visibility
Access modifiers define who can access a class, method, or variable. There are four access levels in Java.
| Modifier | Description |
|---|---|
public |
Accessible from any other class. No restrictions. |
protected |
Accessible within the same package, and by subclasses (even if they are in a different package). |
| (no modifier) | Accessible only within the same package. This is the default access level. |
private |
Accessible only within the same class. The most restrictive level. |
Code Example Demonstrating All Levels
// File: Parent.java
package com.example;
public class Parent {
public String publicVar = "public";
protected String protectedVar = "protected";
String defaultVar = "default"; // no modifier
private String privateVar = "private";
// All methods can access all variables within the same class
public void printVariablesInParent() {
System.out.println(publicVar);
System.out.println(protectedVar);
System.out.println(defaultVar);
System.out.println(privateVar);
}
}
// File: ChildInSamePackage.java
package com.example;
public class ChildInSamePackage extends Parent {
public void printVariablesInChild() {
// System.out.println(privateVar); // ERROR: private is not accessible
System.out.println(publicVar); // OK: public is accessible
System.out.println(protectedVar); // OK: protected is accessible to subclass
System.out.println(defaultVar); // OK: default is accessible in same package
}
}
Changing Behavior
Non-access modifiers do not change visibility. Instead, they provide special functionalities to classes, methods, and variables. The most common ones are static, final, and abstract. We will cover them in detail next.
Other non-access modifiers include synchronized and volatile (used for multithreading), transient (used for serialization), and native (for calling code from other languages).
Belonging to the Class
The static keyword means that a member (variable or method) belongs to the class itself, not to a specific instance of the class.
Key Points:
- There is only one copy of a static member, shared across all objects.
- You can access static members without creating an object, using the class name:
ClassName.staticMember.
Static Variable (Class Variable)
A static variable is shared by all instances. It's often used for constants or counters.
public class Employee {
private String name;
// A static variable to count all employees
private static int employeeCount = 0;
public Employee(String name) {
this.name = name;
employeeCount++; // Increment the shared counter
}
public static int getEmployeeCount() {
return employeeCount;
}
}
Static Method
A static method belongs to the class. It can only access other static members directly.
public class MathUtils {
// A static method to calculate the square of a number
public static int square(int number) {
return number * number;
}
}
// How to use it:
public class Main {
public static void main(String[] args) {
// Call the static method directly on the class
int result = MathUtils.square(5);
System.out.println("Square of 5 is: " + result); // 25
}
}
The "Once and For All" Modifier
The final keyword means that once something is assigned, it cannot be changed.
Final Variable (Constant)
A final variable is a constant. Its value cannot be changed after it's assigned. By convention, constant names are in UPPERCASE.
public class Circle {
// PI is a constant. Any attempt to change it will cause an error.
public static final double PI = 3.14159;
public double calculateCircumference(double radius) {
// PI = 3.14; // ERROR: Cannot assign a value to final variable PI
return 2 * PI * radius;
}
}
Final Method
A final method cannot be overridden by a subclass. This is used to prevent a critical method's implementation from being changed.
class Parent {
// This method is final and cannot be overridden
public final void showMessage() {
System.out.println("Message from Parent");
}
}
class Child extends Parent {
// @Override
// public void showMessage() { // ERROR: Cannot override the final method from Parent
// System.out.println("Message from Child");
// }
}
Final Class
A final class cannot be extended (inherited). This is done for security or to create an immutable class like String.
// This class is final and cannot be a parent to any other class
public final class ImmutableClass {
// ... class members
}
// class ChildClass extends ImmutableClass { } // ERROR: Cannot inherit from final class
The Incomplete Blueprint
The abstract keyword is used to create incomplete classes and methods that are meant to be completed by subclasses.
Abstract Class
An abstract class cannot be instantiated (you cannot create an object of it). It is designed to be a base class that other classes can extend. It can have both abstract and concrete methods.
// An abstract class representing a generic shape
abstract class Shape {
String color;
// A concrete method
public void setColor(String color) {
this.color = color;
}
// An abstract method (no body)
public abstract double area();
}
Abstract Method
An abstract method is declared without an implementation (no body, just a semicolon). Any concrete (non-abstract) subclass must provide an implementation for all abstract methods it inherits.
// A concrete subclass of Shape
class Rectangle extends Shape {
double length;
double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
// Must provide an implementation for the abstract area() method
@Override
public double area() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
// Shape myShape = new Shape(); // ERROR: Cannot instantiate an abstract class
Rectangle myRect = new Rectangle(10, 5);
System.out.println("Area of rectangle: " + myRect.area()); // 50.0
}
}