Java static关键字详解
1. static修饰的变量或方法或类的加载时机
在加载类的同时加在static修饰的部分。(注意:这个时候,还不存在具体对象,并且这个过程只进行一次)
2. 通过代码示例来分别看看静态变量、静态方法、静态类的效果
2.1 静态变量
public static int count =0; @SuppressWarnings("static-access") public static void main(String[] args) { // TODO Auto-generated method stub StaticTest test1 = new StaticTest(); System.out.println(test1.count); StaticTest test2 = new StaticTest(); test2.count++; System.out.println(test1.count+" "+test2.count+" "+StaticTest.count); } }
输出结果:
0 1 1 1
可见,static变量并不是所在类的某个具体对象所有,而是该类的所有对象所共有的,静态变量既能被对象调用,也能直接拿类来调用。
除此之外,静态变量不能引用非静态方法,原因正如前面描述静态加载时机中说的那样,加载静态的时候,非静态的变量、方法等还不存在,当然就无法引用了。但是,非静态方法或类却能正常引用静态变量或方法。因为非静态总是在静态之后出现的。
2.2 静态方法
静态方法和静态变量一样,属于类所有,在类加载的同时执行,不属于某个具体的对象,所有对象均能调用。对于静态方法需要注意以下几点:
- 它们仅能调用其他的static 方法。
- 它们只能访问static数据。
- 它们不能以任何方式引用this 或super。
class Simple { static void go() { System.out.println("Welcome"); } } public class Cal { public static void main(String[] args) { Simple.go(); } }
静态方法一般用于工具类中,可以直接拿类名调用工具方法进行使用。
2.3 静态类
一般来说,一个普通类是不允许被声明为static的,但是,在内部类中可以将其声明为static的,这个时候,外部类可以直接调用内部类,因为static的内部类是在加载外部类的同时加载的,所以也就是说,并不要实例化外部类就能直接调用静态内部类。看例子:
public class BaseStatic { static { System.out.println("Load base static"); } public BaseStatic(){ System.out.println("BaseStatic constructor"); } static class BaseInnerClass{ static{ System.out.println("Base inner class static"); } public BaseInnerClass(){ System.out.println("BaseInnerClass constructor"); } } } public class StaticLoadOrderTest{ public static void main(String[] args) { // TODO Auto-generated method stub new BaseStatic.BaseInnerClass(); } }
在看答案之前,自己想想这个输出结果是什么?
先不急着看答案,我们先来看看这个执行过程:首先,在进入StaticLoadOrderTest的main方法之前,加载StaticLoadOrderTest类,然后执行new BaseStatic.BaseInnerClass();这里需要注意:因为BaseInnerClass是静态的,所以这里并不需要加载外部类和实例化外部类,可以直接加载BaseInnerClass并实例化。所以输出:
Base inner class static BaseInnerClass constructor
这里留个坑:当直接使用外部类类名.静态内部类进行实例化的时候,如果外部类没有加载的话(实际上也是没有加载),那么这个statement: BaseStatic.BaseInnerClass中的BaseStatic是个什么存在????难道只是与静态内部类发生了简单的名称关联吗?若是这样还设计静态内部类干嘛呢?我觉得java设计者们不至于犯这种错误吧?也可能因为自己对于JVM并不熟悉,对于底层不太了解,若是路过的大神能帮忙解决一下,感激不尽!!!!
2.4 关于静态加载顺序的示例
下面这段代码的输出是什么?
public class BaseStatic { static { System.out.println("Load base static"); } public BaseStatic(){ System.out.println("BaseStatic constructor"); } static class BaseInnerClass{ static{ System.out.println("Base inner class static"); } public BaseInnerClass(){ System.out.println("BaseInnerClass constructor"); } } } public class StaticLoadOrderTest { static { System.out.println("Load test"); } public StaticLoadOrderTest(){ System.out.println("Test constructor"); } public static void main(String[] args) { // TODO Auto-generated method stub new BaseStatic(); new StaticLoadOrderTest(); new BaseStatic.BaseInnerClass(); } }
和上面一样,分析一下过程:在进入main方法之前,需要加载StaticLoadOrderTest类,这时候发现有static代码块,先加载静态代码块,然后进入main方法内部,new BaseStatic(),这时候需要加载BaseStatic类,同时也要先加载静态代码块,然后调用构造器。注意:这里并没有加载BaseInnerClass,因为它是内部类只有在真正用到的时候才会进行加载,相信聪明的读者看到这个是不是想到了又一种单例设计模式的实现方式?自己研究吧。回到main方法中,接下来该执行new StaticLoadOrderTest()了,因为StaticLoadOrderTest类之前已经被加载过一次了,并且类只加载一次,所以这里就直接构造了;然后是最后一句new BaseStatic.BaseInnerClass()了,和上面例子一样,这里就不再细讲。所以输出结果为:
Load test Load base static BaseStatic constructor Test constructor Base inner class static BaseInnerClass constructor
再考虑一下,如果我把上面的例子改成下面这样,输出结果又会是什么呢?
public class BaseStatic { static { System.out.println("Load base static"); } public BaseStatic(){ System.out.println("BaseStatic constructor"); } static class BaseInnerClass{ static{ System.out.println("Base inner class static"); } public BaseInnerClass(){ System.out.println("BaseInnerClass constructor"); } } } public class StaticLoadOrderTest extends BaseStatic{ static { System.out.println("Load test"); } public StaticLoadOrderTest(){ System.out.println("Test constructor"); } public static void main(String[] args) { // TODO Auto-generated method stub new BaseStatic.BaseInnerClass();new StaticLoadOrderTest(); new BaseStatic(); } }