http://blog.csdn.net/zhouwubin123/article/details/6623308
Java开发人员一定遇到过,在eclipse开发环境中,在包括main方法在内的static方法中调用某非静态变量,会编译出错。
public class StaticDemo {
int x;
void fun() {
System.out.println("this is fun()");
}
public static void main(String[] args) {
x = 5; // 报错,不能被访问
fun(); // 报错,不能被访问
}
}
在static方法中调用非静态变量x和非静态方法fun()的出错信息如下图所示:
究其原因,我们来做如下分析:
任何程序最终都是在内存中执行的,变量只有在内存中占有一席之地时才能被访问,不在内存中的变量就不能被访问。类的静态成员(变量和方法)都属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
public class StaticDemo {
static int x;
static void fun() {
System.out.println("this is fun()");
}
public static void main(String[] args) {
StaticDemo.x = 5; // 可以用类名直接访问静态成员
fun(); // 在类体内也可以直接访问
}
}
由于静态成员在类加载的时候就会被分配内存,而非静态成员则不会。因此,在一个类的静态成员中去访问其非静态成员会出错,是因为在类的非静态成员不存在的时候,类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。
而类又是在什么时候加载的呢?核心类(比如String类)在 JVM 启动时(main 方法开始执行前)就会被加载,其它类在使用前(实例化对象、调用其静态方法、访问静态域等前)会被动态加载。
需注意:子类被加载前,它的所有超类要根据由父到子的顺序被逐一加载。
如下例子:
class A1 {
public static int a = 5;
}
class B1 extends A1 {
public static int a = 8;
void print() {
System.out.println(super.a);
System.out.println(a);
}
}
使用一个测试类:
public class TestStatic {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("b.a="+B1.a);
System.out.println("b.a="+A1.a);
new B1().print();
}
}
最终的运行结果如下:
b.a=8
b.a=5
5
8
代码解析:
当你在dos环境下输入:java TestStatic时,
(1)JVM会首先加载TestStatic类,此时JVM会先看看TestStatic类是否存在static字段,没有,直接执行main方法;
(2)main方法第一句是打印B1.a, JVM便会去找类B1,找到B1时,发现B1的父类是A1,于是父亲A1被优先加载;
(3)在加载A1时,会扫描A1中是否存在static的成员(静态变量、静态方法),发现存在静态变量a(static int a = 5) ,先加载,当把所有static的字段加载完,一个类就加载完成了,以后就无需再加载了;
(4)父亲加载完成了,才轮到子类B1的加载,同样先扫描B1中是否存在static的字段,发现存在静态变量a(static int a = 8),加载a,此时B1也加载完毕了;
第一条print语句到此时也执行完毕了。
轮到下一条print语句了(new B1().print()),当执行这句时,会发生动态绑定,此时会有一个代表B1对象的this对象传递给print()方法,所以print()方法中的 System.out.println(a); 其实是System.out.println(this.a),会打印出一个8。至于super.a就是打印父类中的a,结果是5。到此,main()方法执行完,整个程序退出。