Introduction
Modifiers are keywords that let us fine-tune access to our class and its members, their scope, and behavior in certain situations. For example, we can control which classes/objects can access certain members of our class, whether a class can be inherited or not, whether we can override a method later, whether we should override a method later, etc.
Modifier keywords are written before the variable/method/class (return) type and name, e.g. private int myVar
or public String toString()
.
Modifiers in Java fall into one of two groups - access and non-access:
- Access:
public
,private
,protected
- Non-access:
static
,final
,abstract
,synchronized
,volatile
,transient
, andnative
Want to learn more about non-access modifiers? Check out our article Non-Access Modifiers in Java.
Access Modifiers
Access modifiers deal with the visibility of class members. They control whether other classes can see or change certain variables/methods of our class.
These types of modifiers are closely related to an important part of Object Oriented Programming called encapsulation. As a reminder, encapsulation is an idea that links data with the code that manipulates it. By controlling access, you can prevent misuse.
For example, by making sure that certain variables can only be accessed through well-defined methods (the typical get/set combination of methods) we make sure that we won't encounter any unexpected values or deny outside access to certain variables/methods altogether.
As previously mentioned, there are three access modifiers: public
, private
, and protected
. Java also provides default access control (when no modifier is specified), which behaves similarly to protected
.
public
- the member can be accessed from anywhereprotected
- the member is only inaccessible from non-subclasses in a different package- default (package-private) - also known as
package
access, the member can be accessed by any class within the same package private
- the member can only be accessed by other members within the same class
This table shows all possible access scenarios for class members:
Private | Default | Protected | Public | ||
---|---|---|---|---|---|
Same class | Yes | Yes | Yes | Yes | |
Subclass (same package) | No | Yes | Yes | Yes | |
Non-subclass (same package) | No | Yes | Yes | Yes | |
Subclass (different package) | No | No | Yes | Yes | |
Non-subclass (different package) | No | No | No | Yes |
This table applies only to class members, not classes in general. A non-nested class can only be public
or without a modifier. The behavior is logical, when a class is declared without a modifier it can only be accessed by code within the same package, and when it is declared public
it can be used in a different package as well.
Note: A public
class must be the only (non-nested) class in the file, and the file must have the same name as the class.
For example, let's say we have two packages, creatively named packageOne
and packageTwo
.
package packageOne;
public class MyPublicClass {
String noModifierText = "No Modifier";
private String privateText = "Private Text";
protected String protectedText = "Protected Text";
public String publicText = "Public Text";
public MyPublicClass() {
// We can access all members of a class from within that class
System.out.println("MyPublicClass constructor:")
System.out.println(noModifierText);
System.out.println(privateText);
System.out.println(protectedText);
System.out.println(publicText);
}
}
Note that the above code is in a file called "MyPublicClass.java". The name must match the class since we'll make the class public so we can access it from a different package. The same applies to the other classes below.
package packageOne;
class SamePackageExtends extends MyPublicClass {
public SamePackageExtends() {
System.out.println("SamePackageExtends constructor:")
System.out.println(noModifierText);
// Trying to access the private member privateText will fail, since private members
// can only be accessed by members of the same class, even though this class extends it.
// System.out.println(privateText);
System.out.println(protectedText);
System.out.println(publicText);
}
}
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
package packageOne;
class SamePackageDoesntExtend {
// Has the same access as SamePackageExtends
public SamePackageDoesntExtend() {
MyPublicClass myPublicClass = new MyPublicClass();
System.out.println("SamePackageDoesntExtend constructor:")
System.out.println(myPublicClass.noModifierText);
// System.out.println(myPublicClass.privateText);
System.out.println(myPublicClass.protectedText);
System.out.println(myPublicClass.publicText);
}
}
package packageTwo;
class DifferentPackageExtends extends packageOne.MyPublicClass {
public DifferentPackageExtends() {
System.out.println("DifferentPackageExtends constructor:")
// System.out.println(noModifierText); // Same class or same package only
// System.out.println(privateText); // Same class only
System.out.println(protectedText);
System.out.println(publicText);
}
}
package packageTwo;
class DifferentPackageDoesntExtend {
public DifferentPackageDoesntExtend() {
packageOne.MyPublicClass myPublicClass = new packageOne.MyPublicClass();
System.out.println("DifferentPackageDoesntExtend constructor:")
// System.out.println(myPublicClass.noModifierText);
// System.out.println(myPublicClass.privateText);
// System.out.println(myPublicClass.protectedText); // Same package only
System.out.println(myPublicClass.publicText);
}
}
Tip: It's common practice to encapsulate a class. This means that we declare member variables as private
and declare public
methods that manipulate them. For example, we want to let someone change int ID
field but we also want to make sure that int ID
is strictly a positive integer. Though the public method, we can first run a check and manipulate the field if the given value passes our check. This is a construct called a set()
method, and it's usually accompanied by a get()
method (since we can't read private members outside of our class) or when we want to control how and when the value of a variable can be read.
class GetSetExample {
...
private int ID = 0; // Default value
public setID(int n) {
if (n > 0) {
ID = n;
}
else ID = 0;
}
public int getID() {
// Potential read conditions that need to be met
return ID;
}
...
}
One other thing to note is that protected
is the least used of all of the access modifiers. It can easily be bypassed if we want to. Even in a different package we can simply inherit the class whose protected
members we want to access, and then access them via that inherited class.
With that in mind, protected
is most often used as a guideline that says "This member is not meant to be accessed by non-subclasses in a different package", so even though we can easily bypass protected
access control, it's not advisable, since it was most likely put there for a reason.
Conclusion
Modifiers are keywords that let us fine-tune access to our class and its members, their scope and behavior in certain situations. They provide fundamental traits for our classes and their members. Every developer should be thoroughly be acquainted with them to make the best use of them.