对于static关键字的用法,主要分为三个部分,分别是静态变量、静态方法和静态类。
1.静态变量
静态变量(静态域)不是类的某个具体对象所有,而是类的所有对象共有的,静态变量既能够被对象调用,也可以用类来调用。
package StaticTest_4_3; public class StaticTest { public static int count = 3; public static void main(String[] args) { StaticTest test1 = new StaticTest(); System.out.println(test1.count); StaticTest test2 = new StaticTest(); test2.count++; System.out.println(test1.count + " " + test2.count + " " + StaticTest.count); } }
输出结果:
3 4 4 4
每个类对象都可以对类的静态变量进行修改,这些修改都会被保存,这一点和final关键字修饰的变量不一样。
注意:静态变量不能引用非静态方法,原因是,加载静态变量的时候,非静态变量、方法都还不存在,所以不能引用。但是非静态方法或类可以正常引用静态变量或方法,因为非静态的总是出现在静态的之后。
2.静态方法
静态方法和静态变量一样,属于类所有,在加载类的时候进行加载,不属于具体的类的对象,所有对象均可以进行引用。
对于静态类,有几点注意事项:
①仅能够调用其他的static方法;
②只能访问static数据;
③不能以任何方式引用this或super。
package StaticTest_4_3; public class StaticTest1 { private static String str1 = "static"; private String str2 = "pro"; public StaticTest1(){} public void print1() { System.out.println(str1); System.out.println(str2); print2(); } public static void print2() { System.out.println(str1); System.out.println(str2); print1(); } }
由于print2是独立于对象存在的,可以直接使用类名调用。
假如说可以在静态方法中访问非静态方法/变量的话,那么如果在main方法中有下面一条语句:
staticTest1.print2();
此时对象都没有,str2根本就不存在,所以就会产生矛盾了。同样对于方法也是一样,由于你无法预知在print1方法中是否访问了非静态成员变量,所以
禁止在静态成员方法中访问非静态成员方法。
非静态成员方法,它访问静态成员方法/变量显然是毫无限制的。
因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
另外记住,即使没有显示地声明为static,类的构造器实际上也是静态方法。
3.静态类
一个普通的类不允许被声明为static,但是在内部类中可以直接将其声明为static,这个时候,外部类可以直接调用内部类,因为static的内部类是在加载外部类的同时加载的,所以并不需要实例化外部类就能直接调用内部类。
BaseStatic.java
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"); } } }
StaticLoadOrderTest.java
public class StaticLoadOrderTest{ public static void main(String[] args) { // TODO Auto-generated method stub new BaseStatic.BaseInnerClass(); } }
因为BaseInnerClass是静态的,所以这里并不需要加载外部类和实例化外部类,可以直接加载BaseInnerClass并实例化。
输出结果:
Base inner class static BaseInnerClass constructor
再看一个例子
BaseStatic.java
package StaticTest_4_3; 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"); } } }
StaticLoadOrderTest1.java
package StaticTest_4_3; public class StaticLoadOrderTest1 { static { System.out.println("Load test"); } public StaticLoadOrderTest1() { System.out.println("Test Constructor"); } public static void main(String[] args) { new BaseStatic(); new StaticLoadOrderTest1(); new BaseStatic.BaseInnerClass(); } }
输出结果:
Load test Load base static BaseStatic constructor Test Constructor Base inner class static BaseInnerClass constructor
在进入main方法之前,需要加载StaticLoadOrderTest类,这时候发现有static代码块,先加载静态代码块,然后进入main方法内部,new BaseStatic(),这时候需要加载BaseStatic类,同时也要先加载静态代码块,然后调用构造器。注意:这里并没有加载BaseInnerClass,因为它是内部类只有在真正用到的时候才会进行加载,相信聪明的读者看到这个是不是想到了又一种单例设计模式的实现方式?自己研究吧。回到main方法中,接下来该执行new StaticLoadOrderTest()了,因为StaticLoadOrderTest类之前已经被加载过一次了,并且类只加载一次,所以这里就直接构造了;然后是最后一句new BaseStatic.BaseInnerClass()了。