关于static关键字及其初始化

什么是static

通常来说,创建类之后,只有执行new来创建对象时,数据存储空间才被分配,这个类的方法才能被外界调用。但是有两种特殊情况用这个方法不能解决:

  1. 只想为某特定域分配单一存储空间,而不去考虑是否创建对象或创建多少对象。
  2. 希望方法不于包含它的类的任何对象关联。即:即使没有创建对象,也可以调用这个方法

static关键字可以满足这两个需要。即使从未创建某个类的任何对象,也可以调用其static方法或访问static域。

static作用于字段

static作用于某个字段时,会改变数据创建的方式,其本质是:一个static字段对每个类来说都只有一份存储空间,而非static字段则是对每个对象都有一个存储空间。

class StaticTest {
  static int i = 47;
}

class Static {
  public static void main(String[] args) {
    StaticTest st1 = new StaticTest();
    StaticTest st2 = new StaticTest();
    System.out.println("st1:" + st1.i + " st2:" + st2.i);
    StaticTest.i++;
    System.out.println("st1Increment:" + st1.i + " st2Increment:" + st2.i);
  }
} /* output:
st1:47 st2:47
st1Increment:48 st2Increment:48
*///:~

在示例中,即使创建了两个StaticTest对象,StaticTest.i也只有一份存储空间(即:st1.i 和st2.i 指向同一个存储空间)。
这也是为什么我们可以通过类名去直接引用静态成员:

StaticTest.i++;

如上使用类名是引用static变量的首选方式(另一种方式是通过对象定位,如st1.i),因为它强调了变量的static结构,某些情况下为编译器进行优化提供了更好的机会。

static作用于方法

static作用于方法时,不像其作用于字段时有那么大的差别。static方法的重要用法是 在不创建任何对象的前提下就可以调用static方法。这一点对定义main()方法十分重要,因为它是运行一个应用时的切入点。

静态数据的初始化

在类的内部,即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。

public class ExplicitStatic {
  public static void main(String[] args) {
    System.out.println("Inside main");
    Cups.cup1.f(99); //(1)
  }
  // static Cups cups1 = new Cups(); //(2)
  // static Cups cups2 = new Cups(); //(2)
}

class Cups {
  static Cup cup1;
  static Cup cup2;
  static {
    cup1 = new Cup(1);
    cup2 = new Cup(2);
  }
  Cups() {
    System.out.println("Cups()");
  }
}

class Cup {
  Cup(int marker) {
    System.out.println("Cup(" + marker + ")");
  }
  void f(int marker) {
    System.out.println("f(" + marker + ")");
  }
}

此时(执行(1)时)的输出为:

Inside main
Cup(1)
Cup(2)
f(99)

当注释掉(1),执行(2)时,输出为:

Cup(1)
Cup(2)
Cups()
Cups()
Inside main

可见,初始化的顺序是:先静态对象,后非静态对象,且静态初始化动作只进行一次

假设有名为Dog的类,总结一下对象的创建过程:

  1. 构造器(constructor)实际上是static静态方法。首次创建类型为Dog的对象时,或者Dog类的静态方法/静态域被首次访问时,Java解释器必须查找类路径,定位Dog.class文件。
  2. 载入Dog.class并创建Class对象,此时有关静态初始化的所有动作都会执行。静态初始化只在Class对象首次加载时进行一次
  3. 用new Dog()创建对象时,首先在堆上为Dog对象分配足够的存储空间。
  4. 这块存储空间被清零,自动将Dog对象所有基本数据类型设置成默认值,引用设置成null.
  5. 执行出现于字段定义处的初始化动作。
  6. 执行构造器。

参考:《Thinking in Java 4th Edition》第二章、第五章。

猜你喜欢

转载自blog.csdn.net/zxh970209/article/details/84784964
今日推荐