Java static 关键字理解

对Java static关键字的理解总结。

static member/method

static修饰的成员或方法属于某个类而不是某个实例对象,该字段被所有实例共享。举个例子,在使用Unsafe实现对某个对象sizeOf的时候也是只需要判断非静态成员变量的offset即可。

static在这种场景下通常用于定义全局变量和通用的方法,可以节省内存开销。但是各个对象必然发生关联才能发挥功效,所以需要对象级别的属性和方法。

例子:

1
2
3
4
5
6
7
8
9
10
11
public class StaticMemberMethod {
public static int MAX = 1024;
public static void foo(){
System.out.println("foo()");
}

public static void main(String[] args) {
System.out.println(StaticMemberMethod.MAX);
StaticMemberMethod.foo();
}
}

static block

静态块是静态初始化器( Static Initializers),在类加载初始化的时候执行。和Static Initializers区别的是Instance Initializers, 也就是没有static修饰的块,实例初始化执行的时机是当一个实例创建的时候。不严谨的说法就是static block在我们的构造器执行之前运行,普通的block在我们的构造器执行之后,这句话是错误的!。这里需要理解类的初始化过程

还有一个直观的理解就是,在static block里面不能访问实例变量(比如this.member),但是可以访问全局变量,传达的意思就是说现在只是类的加载过程,和实例的构造没有关系;而在non-static block里面就可以访问this.member,因为现在已经开始准备构建实例了。而且还要注意non-static block是在我们的构造器之前执行的。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class StaticBlock {
private int member;
public static String RESOURCE = "default";
public StaticBlock(int member){
this.member = member;
System.out.println("my constructor invoke");
System.out.println(StaticBlock.RESOURCE); // 此时静态初始化已经执行了
}

// static block <-> static initializer
static {
RESOURCE = "init";
System.out.println("static block");
}

// non-static block <-> instance initializer
{
this.member = 1024;
System.out.println("non-static block " + this.member);
}

public int getMember(){
return this.member;
}

public static void main(String[] args) {
StaticBlock sb = new StaticBlock(3);
System.out.println(sb.getMember());
}
}

输出:

1
2
3
4
5
static block
non-static block 1024
my constructor invoke
init
3

Spring中的一个使用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {


private static Class<?> javaxInjectProviderClass = null;

static {
ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
try {
javaxInjectProviderClass = cl.loadClass("javax.inject.Provider");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - Provider interface simply not supported then.
}
}
....
}

Unsafe的实例化也是放在static block中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}

@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
static {
...
theUnsafe = new Unsafe();
....
}

static inner class

Java的内部类(也称嵌套类 nested class)分为静态内部类和(普通)内部类,non-static inner class可以访问外围类的所有成员,而static inner class没有外围类的引用,所以其不能访问非静态变量或调用非静态的成员方法。此外还要注意的是实例化这两种类的方式是不同的。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class OuterClass {
private String name;
public static String NAME = "DefaultName";

public OuterClass(String name){
this.name = name;
}

class InnerClass{
public void show() {
System.out.println("inner class , name = " + name + ", default name=" + NAME);
}
}

static class StaticInnerClass{
public void show(){
System.out.println("static inner class , default name=" + NAME);
}
}

public static void main(String[] args) {
OuterClass.StaticInnerClass a = new OuterClass.StaticInnerClass();
OuterClass o = new OuterClass("vonzhou");
OuterClass.InnerClass b = o.new InnerClass();
a.show();
b.show();
}
}

关于内部类的使用场景也很多,如果HashMap中的Entry。还有一个常见的场景就是在initialization on demand holder单例实现中。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Stage {
private Stage() {
}

// 静态内部类
private static class StageSingletonHolder {
static Stage instance = new Stage();
}

public static Stage getInstance() {
return StageSingletonHolder.instance;
}
}

总结:

  • 动手实践
  • 多看Oracle官方的文档

参考:

Java inner class and static nested class(stackoverflow)

Nested Classes