最近重新复习了Java中静态数据的初始化问题,包括何时初始化以及初始化的顺序问题。

首先,我们要明确的是静态数据 的含义。静态数据的关键字是static,当声明一个一个事物是static时,就意味着这个域或者方法不会与包含他那个类的任何对象实例关联在一起。也就是意味着,你可以直接通过类名来直接访问,而不用创建一个对象,再来访问。

这里,我们有需要注意的地方:

  1. 如果static和private连用,在类里我们仍然是可以直接通过类名加.来调用的,包括public类的main方法,但是在类外就无法直接通过类名调用。
class Demo1{
        private static int m=9;
    }
    public class Demo {
        private static int i=5;
        public static void main(String[] args) {
            int j=Demo.i;
            int k=Demo1.m;
            System.out.println(j);
            System.out.println(k);
        }
    }
在上述代码中,J可以成功打印,但是K则无法通过编译。

还有,static方法是能够被继承和重写的,但是重写的方法仍然应该是static方法;

static方法中是不能使用this,super的。

public class Demo {
        private static int i=5;
        public static void f(int i){
            this.i=i;
        }
        public static void main(String[] args) {
            int j=Demo.i;
            System.out.println(j);
        }
    }
上面的代码片里f方法的中具体实现使用了this,无法通过程序编译。

关于static我们就了解这,现在我们主要需要探讨的是其初始化的时间和顺序。静态数据只占用一份存储区域,而且,static关键字不能应用于局部变量,因此它只能作用于域。

对于静态的基本类型域,如果没有显示的对其初始化,则其会获得基本类型的标准初值;

对于一个对象引用,那么它的默认初始化值就是null。

现在,我们来通过写一个简单的Demo来研究静态数据的初始时间和顺序。

class Apple{
        Apple(int i){
            System.out.println("Apple:"+i);
        }
        void f1(int i){
            System.out.println("f1:"+i);
        }
    }

    class Apples{
        static Apple a1=new Apple(1);
        static Apple a2=new Apple(2);
        Apples(){
            System.out.println("Apples()");
            a2.f1(1);
        }
        void f2(int i){
            System.out.println("f2:"+i);
        }
    }

    class Appless{
        Apple a3=new Apple(3);
        static Apple a4=new Apple(4);
        static Apple a5=new Apple(5);
        Appless(){
            System.out.println("Appless()");
            a4.f1(2);
        }
        void f3(int i){
            System.out.println("f3:"+i);
        }
    }
    public class Fruit {
        static Apples apples=new Apples();
        static Appless appless=new Appless();
        public static void main(String[] args) {
            System.out.println("分隔符——————————");
            new Appless();
            System.out.println("分隔符——————————");
            new Appless();
            apples.f2(1);
            appless.f3(1);
        }
    }

在eclipse上运行输出的结果为:

Apple:1
    Apple:2
    Apples()
    f1:1
    Apple:4
    Apple:5
    Apple:3
    Appless()
    f1:2
    分隔符——————————
    Apple:3
    Appless()
    f1:2
    分隔符——————————
    Apple:3
    Appless()
    f1:2
    f2:1
    f3:1

我们来分析一下上面的代码:一共是四个类:Apple、Apples、Appless、Fruit。我是用了组合思想,通过最基础的类Apple来,使其成为其他类的数据成员。

从public类的程序的运行顺序来看静态数据的初始化时间和顺序:

  1. 要执行main方法,必须加载Fruit类,而Fruit类还有两个静态数据成员:apples和appless。所以,这两个数据成员的生成调用main方法前。
  2. 先看第一行的apples,其是new Apples()的引用,这就要加载Apples类,而Apples类中也有静态数据成员a1,a2,这在构造函数前运行,所以,要先加载Apple类的构造函数。
  3. 同理,appless也是一样,在加载Appless类的时候往回追溯到底类Apple,而且Appless里面还有非静态数据成员,其应排在静态数据成员和构造方法之间,从打印结果可以看出的确如此。
  4. 在public类的数据成员加载结束后,才是main方法,而且,在打印结果可以看到,静态数据成员只会初始化一次。

简单的总结化就是:Public类的数据成员初始化,不断的往回追溯类加载,然后再是main方法,其中要牢记,静态数据成员只初始化一次。静态数据成员初始化在静态数据成员前。