Visibility scoping is implemented in two places in Java: in the compiler and in the JVM that executes the bytecode.
First, the compiler limits visibility of variables flagged with public
, protected
and private
keywords (also note that Java has a fourth scope, called the default scope, that was what you get when you declare a variable without one of the three keywords). The differences are documented elsewhere (see In Java, difference between default, public, protected, and private or any good books on Java programming).
Second, and I think this is more what you are asking about, is that Java protects access to variables with private
, protected
and the default scope within the JVM at runtime. This primarily applies to Java's reflection API (if your non-reflection code compiles you know it isn't accessing anything it isn't allowed to access and thus can't run into runtime visibility problems). For example, say you've got this in Foo.java:
// class with a private member variable
public class Foo {
private int bar = 0;
}
And then you try to access bar
from another class using reflection in Test.java:
import java.lang.reflect.*;
// This class won't have access to Foo's member 'bar'
public class Test {
void doStuff() {
// create a new instance of Foo
Foo foo = new Foo();
try {
// use reflection to get the variable named 'bar'
Field barField = foo.getClass().getDeclaredField("bar");
// attempt to access the value of 'bar' which will throw an exception
System.out.println(barField.get(foo));
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new Test().doStuff();
}
}
You would get an exception indicating you have no access to that variable. There are ways to circumvent this, but the point here is that by default the JVM protects access to variables (and methods and classes) outside the scope you'd normally be able to see.
Addendum
Java doesn't typically "hide" anything since it doesn't need to. Neither the Java language nor the JVM bytecode have access to the pointers used behind the scenes and there's also no other way through the language or the reflection API to access arbitrary locations in memory and thus no way (with one exception -- see below) to read the value of a variable to which you don't have access. When using reflection the JVM simply checks a flag (initially set by the compiler) to see if the current code has access to the variable or method being accessed and then either allows the access or throws an exception.
The exception I mentioned is a reflection API call that can turn off the access checking for a given variable or method. For example, to avoid the exception from my previous example you could do this:
...
Field barField = foo.getClass().getDeclaredField("bar");
// mark the variable 'bar' as accessible
barField.setAccessible(true);
// attempt to access the value of 'bar' which will throw an exception
System.out.println(barField.get(foo));
...