我是Java编程的新手,并且有一个我一直很好奇的问题。任何帮助将不胜感激:)
私有,包私有,受保护和公共变量如何在幕后区分,以保证变量安全?
安全我的意思是,例如,它如何确保私有变量只能被类中的方法访问?
我想知道计算机/ JVM内部究竟发生了什么。 Java是否为每种类型都有不同的内存空间?如果是这样,是什么让不同的内存空间无法访问?毕竟它还在记忆中吗?
提前谢谢: - )
答案 0 :(得分:3)
可见性范围在Java中的两个位置实现:在编译器和执行字节码的JVM中。
首先,编译器限制了用public
,protected
和private
关键字标记的变量的可见性(还要注意Java有第四个范围,称为默认范围,就是你当你声明一个没有三个关键字之一的变量时获取)。差异记录在别处(参见In Java, difference between default, public, protected, and private或任何有关Java编程的好书)。
其次,我认为这更像是你要问的,是Java在运行时保护对private
,protected
和JVM中的默认范围的变量的访问。这主要适用于Java的反射API(如果您的非反射代码编译,您知道它不能访问任何不允许访问的内容,因此可能会遇到运行时可见性问题)。例如,假设你在Foo.java中有这个:
// class with a private member variable
public class Foo {
private int bar = 0;
}
然后你尝试使用Test.java中的反射从另一个类访问bar
:
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();
}
}
您将收到一个异常,表明您无权访问该变量。有一些方法可以避免这种情况,但这里的要点是,默认情况下,JVM可以保护对您通常能够看到的范围之外的变量(以及方法和类)的访问。
<强>附录强>
Java通常不会隐藏&#34;任何事情,因为它不需要。 Java语言和JVM字节码都无法访问幕后使用的指针,并且通过语言或反射API也无法访问内存中的任意位置,因此没有办法(除了一个例外 - 请参阅下文)以读取您无法访问的变量的值。使用反射时,JVM只检查一个标志(最初由编译器设置),以查看当前代码是否可以访问正在访问的变量或方法,然后允许访问或抛出异常。
我提到的异常是一个反射API调用,它可以关闭给定变量或方法的访问检查。例如,要避免前一个示例中的异常,您可以这样做:
...
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));
...
答案 1 :(得分:1)
如果您有兴趣,您应该花一点时间浏览Java虚拟机规范。特别是,section 4.5定义了如何存储和引用字段的属性,而section 5.4.3.2讨论了如何实际检索字段。
归结为解析字段(如果需要,首先解析类)然后检查字段是否可以被调用代码访问。 AFAICT没有私人领域的特殊空间。
答案 2 :(得分:1)
Java使用代码验证来强制执行它的规则。在Java字节码中,没有内存地址,但仍然有不同的专用指令用于访问实例,static
字段,局部变量或操作数堆栈的字段。由于指令显式访问字段,Verifier可以检查包含该指令的类是否有权访问该字段。
重要的是要理解,对于特定代码,这只发生一次,因为有效性不会改变,因此验证的代码将不会在后续执行时再次验证,因此验证只施加一次性开销。
允许使用不同的策略,关于何时验证方法的代码,但当然,验证必须在第一次执行代码之前发生,这是在JIT可能将字节代码编译为机器代码之前使用内存地址访问字段。
另见“The Java® Virtual Machine Specification, §4.10. Verification of class
Files”