对于每一个基于相同类所产生的对象而言,它们会拥有各自的域成员,然而在某些时候,你会想要这些对象拥有共享的域成员。举个例子来说,在 Ball 类中,打算使用到圆周率 PI 这个数据,因为对于任何一个 Ball 的实例而言,圆周率都是相同的,你不需要让不同的 Ball 实例拥有各自的圆周率域成员。
1.静态成员变量
可以将 PI 域成员声明为 static,被声明为 static 的域成员又称静态域成员。静态域成员是属于类所有,而不是个别的对线。可以将静态成员视为每个对象实例所共享的域成员。要声明静态域成员,只要在声明域成员时加上 static 关键字即可。例如:
public final class Ball { public static double PI = 3.14159; }
静态成员属于类所有,可以在不使用名称参考下,直接使用类名加上运算符来存取静态域成员,不过静态域成员同样遵守 public、protected 与 private 的存取限定。所以若要直接存取静态域成员,必须注意它的权限。例如必须设定为 public 成员,就可以通过以下语句存取:
System.out.println("PI = " + Ball.PI);
虽然也可以在声明对象之后,通过对象名称加上.操作符来存取静态域成员,但是这种方式并不被鼓励。通常建议使用类名加上.操作符来存取,这也可以避免于非静态域成员混淆。例如下面的方式是不被鼓励的:
Ball ball = new Ball(); System.out.println("PI = " + ball.PI);
2.静态成员方法
与静态域成员类似,也可以声明方法成员为 static 方法,又称静态方法。被声明为静态的方法通常是作为工具方法。例如在 Ball 类上增加一个角度转弧度的方法 toRadian():
public final class Ball { public static double PI = 3.14159; public static double toRadian(double angle) { return PI / 180 * angle; } }
与静态域成员一样,可以通过类名称使用.操作符来存取 static 方法,当然要注意权限设定。例如若设定为 public 可以通过以下语句存取:
System.out.println("角度 90 等于弧度" + Ball.toRadian(90));
静态数据与静态方法的作用通常是为了提供共享的数据或工具方法,例如将数学常用数或计算公式,以 static 声明并编写,之后可以把这个类当作工具类。通过类名称来管理与取用这些静态数据或方法,例如 J2SE 所提供的 Math 类上,就有 Math.PI 这个静态常数,以及 Math.Exp()、Math.Log()、Math.Sin() 等静态方法可以直接使用,另外 Integer.parseInt()、Integer.MAX_VALUE 等也都是静态方法与静态域成员的实际例子。
由于静态成员是属于类而不是对象,所以当调用静态方法时,并不会传入对象的参考,即静态方法中不会有 this 参考名称。由于没有 this 名称,所以在 Java 的静态方法中不允许使用非静态成员。因为没有 this 来引用至对象,也就无法辨别要存取的是哪一个对象的成员,事实上,如果在静态方法中使用非静态域成员,在编译时就会出现以下的错误信息:
non-static variable test cannot be referenced from a static context
或者是在静态方法中调用非静态方法,在编译时就会出现以下的错误信息:
non-static method showHello() cannot be referenced from a static context
在 Java 中程序入口点(Entry Point)的 main() 方法就是静态方法,如果要直接在 main() 中调用其他的方法,则该方法就必须是静态方法。例如:
public class Hello { public static void sayHello() { System.out.println("Hello World!"); } public static void main(String[] args) { sayHello(); } }
可以试着将 sayHello() 前的 static 去掉,编译时就会发生上述的第二个错误信息。
3.静态代码块
Java 在使用到类时才会加载类至程序中。如果在加载类时,希望先进行一些类的初始化动作,可以使用 static 定义一个静态区块,并在当中编写类载入时的初始化动作。例如:
public class Ball { static { // 执行初始化程序代码 } // Methods }
在类被加载时,预设会先执行静态区块中的程序代码,且只会执行一次。实际使用范例来说明一下,首先编写范例 SomeClass 类。
public class SomeClass { static { System.out.println("类被载入"); } }
这个类只定义了静态区块,主要是为了测试类被加载时是否执行该区块,接着编写测试程序 StaticBlockDemo 类。
public class StaticBlockDemo { public static void main(String[] args) { SomeClass someClass = new SomeClass(); } }
在使用 new 来建立 SomeClass 的实例时,SomeClass 类会被加载,加载之后预设会执行静态区块的内容,所以程序的执行结果如下:
类被载入