1. Introduction
In Java, as in life, not everything should be accessible to everyone all the time. Imagine an apartment: you don’t want your neighbours walking through your bedroom, right? Likewise, in programs we sometimes want to “close the door” to certain variables or methods so they cannot be accessed from the outside.
For this, Java has access modifiers — special words that indicate where a particular variable or method can be used: for example, public or private.
Main access modifiers
| Modifier | Where visible? |
|---|---|
|
Everywhere the class is visible (in other packages and in other files) |
|
Only within the same class |
|
Only within the same package (i.e., within classes in the same directory) |
There are additional modifiers, but we’ll talk about them later when we get to inheritance.
Practical example
public class User
{
public String name; // visible to everyone
private int age; // visible only inside the User class
public void sayHello()
{
System.out.println("Hi, my name is " + name);
}
private void secretMethod()
{
System.out.println("This is a secret method!");
}
}
Explanation:
- name — visible everywhere the User class is accessible.
- age — visible only inside the User class itself.
- sayHello() — a public method; it can be invoked from any other class.
- secretMethod() — a private method; it cannot be called from outside.
How does this work in real-world tasks?
Suppose we have a class describing a bank account. We definitely don’t want just anyone to change the balance directly! Therefore, the balance variable will be private, and there will be special methods to work with it.
Why shouldn’t you make everything public?
You might be tempted: “What if we just make everything public so we don’t have to worry about it?” But that’s a path to chaos. Imagine anyone being able to change your balance, username, or even call a method that was meant for internal use only. In large programs this leads to errors and mysterious bugs.
The golden rule for a Java developer:
Make everything private first, then expose only what is truly necessary.
2. Variable scope
Scope is the region of code where a variable “exists” and can be used. Step outside this zone and the variable “disappears” — as if it never existed.
In Java, there are several kinds of variables by scope:
- Local variables — declared inside a method or a code block { ... }.
- Method parameters — declared in a method’s parentheses.
- Class fields — declared inside a class but outside methods.
If a variable is declared inside { block },
it is visible only within that block
and is not accessible outside it.
Local variables
A local variable lives only inside the method or block in which it is declared.
void printSum(int a, int b)
{
int sum = a + b; // local variable
System.out.println(sum);
}
// sum no longer exists here!
Attempting to access sum outside the method will cause a compilation error: “Cannot find variable sum.”
Method parameters
Parameters are also variables, but they live only inside the method.
void greet(String name)
{
System.out.println("Hi, " + name);
}
// name no longer exists here!
Class fields (member variables)
Class fields are declared inside a class but outside methods. They are visible in all methods of that class.
public class Counter
{
private int count = 0; // class field
public void increment()
{
count++; // we can use the field
}
public int getCount()
{
return count; // we can also use the field
}
}
3. Variable shadowing
Shadowing is a situation where a variable (or parameter) is declared in one scope with the same name as in an outer scope. Inside that block, the “new” name shadows the old one, and you can no longer refer to the outer value directly.
Shadowing example:
class ShadowDemo
{
int value = 10; // class field
void printValue()
{
System.out.println(value); // 10 — prints the class field
int value = 5; // the local variable shadows the class field
System.out.println(value); // prints 5, not 10
}
}
In this example, when we write int value = 5;, we declare a new local variable that takes precedence over the class field with the same name. When accessing value inside the printValue() method, the local variable is used rather than the class field.
If you still need to access a static class field, use the class name as a prefix:
class ShadowDemo
{
static int value = 10; // static class field
void printValue()
{
System.out.println(value); // 10 — class field
int value = 5;
System.out.println(value); // 5 — local variable
System.out.println(ShadowDemo.value); // 10 — static class field, access via 'ShadowDemo'
}
}
If you need to access a non-static class field, use the keyword this. It refers to the current object instance.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — class field
int value = 5;
System.out.println(value); // 5 — local variable
System.out.println(this.value); // 10 — class field, access via 'this'
}
}
4. Practice: access modifiers and scope
Let’s continue developing our training app: a simple student registry. We’ll make fields and methods with different access modifiers.
Example: Student class
public class Student
{
public String name; // student name (visible to everyone)
private int age; // age (visible only inside the class)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Hi, my name is " + name);
}
private void printSecret()
{
System.out.println("My age is: " + age);
}
public void revealSecret()
{
printSecret(); // a private method can be called inside the class
}
}
Using the Student class
public class Main
{
public static void main(String[] args)
{
Student s = new Student("John", 20);
s.sayHello(); // OK: public method
s.revealSecret(); // OK: public method that calls a private one inside
s.age = 30; // Error! Field age is private
s.printSecret(); // Error! Method printSecret is private
}
}
5. Common mistakes when working with access modifiers and scope
Error #1: Making everything public — hello, chaos!
If you make all fields and methods public, you risk losing control over who changes your data and how. Make fields private and expose only what’s necessary.
Error #2: Trying to use a variable outside its scope.
For example, you declared a variable inside a method and then try to access it from outside — you’ll get a compilation error. Local variables live only within their own block { ... }.
Error #3: Name conflicts (variable shadowing).
If you declare a variable in a method with the same name as a class field, it’s easy to get confused about which variable you’re using. Use this to explicitly refer to the class field: this.value.
Error #4: Trying to access a private method or field from another class.
If a method or field is marked private, it is accessible only within the same class. Attempting to access it from outside will cause a compilation error.
Error #5: Forgetting about scope in a loop or block.
A variable declared inside a loop or any block { } lives only inside that block. Outside the block it does not exist.
GO TO FULL VERSION