ITEEDU

入门准备

接触对象

对象导向

资源管理

对象容器(Container)

输入输出(I/O)

执行绪(Thread)

反射(Reflection)

metadata

数据库(JDBC)

舍遗补缺

Java Gossip: 关于 static 成员

对于每一个基于相同类别所产生的对象而言,其拥有各自的数据成员,然而在某些时候,您会想要这些对象拥有相同的数据成员,其数据是共享的。

举个例子来说,在Ball类别中,您会使用到圆周率的这个数据,对于任一个球而言,圆周率都是一样的,您并不需要让不同的球对象拥有各自的数据成员来记录圆周率,而这个记录的值却是相同,这只会增加内存的消耗而已。

您可以将数据成员宣告为"static",被宣告为"static"的数据成员,它是属于类别所拥有,而不是个别的对象,您可以将"static"视为个别对象所拥有、共享的数据成员。

要宣告static变量,您只要在宣告数据成员时加上"static"关键词就可以了,例如:

public class Ball {
	// ....
	public static double PI = 3.14159; // 宣告static数据
	public Ball() {
		// ..
	}
	public Ball(double radius, String name) {
		//...
	}
	public double getVolumn() {
		// ......
	}
}

由于static成员属于类别所拥有,所以在不使用对象名称的情况下,您也可以使用类别名称加上 . 运算子来存取static数据成员,例如:

System.out.println("PI = " + Ball.PI); 

static变量同样遵守public、protected与 private的存取限制,所以若您要在类别之外直接存取static变量,必须注意它的权限(例如必须设定为public成员)。

虽然您也可以在宣告对象之后,使用 . 运算子来存取static数据成员,但是这并不被鼓励,通常建议使用类别名称加上 . 运算子来存取,一方面也可以避免与非static成员混淆。

与静态数据成员类似的,您也可以宣告方法成员为static方法,又称静态方法,被宣告为静态的方法通常是为了提供工具,例如在Ball类别上增加一个角度转径度的方法toRadius():

public class Ball {
	...
	public static double toRadius(double angle) {
		return 3.14159 / 180 * angle;
	}
}


与静态数据成员一样的,您可以透过类别名称使用'.'运算子来存取static方法(当然要注意权限设定,例如设定为public),例如:

System.out.println("角度90等于径度" + Ball.toRadius(90));

静态数据与静态方法的作用通常是为了提供共享的数据或工具方法,例如将数学常用常数或计算公式,以static宣告并撰写,之后您可以把这个类别当作工具,透过类别名称来管理与取用这些静态数据或方法,例如像J2SE 所提供的Math类别上,就有Math.PI这个静态常数,以及Math.Exp()、Math.Log()、Math.Sin()等静态方法可以直接使用,另外还有像Integer.parseInt()、Integer. MAX_VALUE等也都是静态方法与静态数据成员的实际例子。

由于static成员是属于类别而不是对象,所以当您呼叫static方法时,并不会传入对象的位置参考,所以static方法中不会有 this参考,由于没有this参考,所以在Java的static方法成员中不允许使用非static成员,因为程序没有this来参考至对象地址,也 就无法辨别要存取哪一个对象的成员,事实上,如果您在static方法中使用非static数据成员,在编译时就会出现以下的错误讯息:

non-static variable test cannot be referenced from a static context

或者是在static函式中呼叫非static函式,在编译时就会出现以下的错误讯息:

non-static method showMe() cannot be referenced from a static context

Java在使用到类别时才会加以加载程序中,如果您要使用一个static数据或方法,而在加载一个类别时,您希望先进行一些初始化动作,您可以使用static定义一个区块,并在当中撰写初始化资源的动作,例如:

public class Ball {
	public static int[] arr = new int[10];
	static {
		// 一些初始化程序代码
	}
	....
}

像上面这样一个类别,在第一次呼叫而被载入时,static区块中的程序代码就会被执行,且只会执行一次,要注意的是,static属性成员必须撰写在 static区块之前,而如果在static区块中发生了例外,则最后会被包装为 java.lang.ExceptionInInitializerError。