陷阱5 无风起浪——初始化静态成员的陷阱
1.陷阱产生的场景
静态成员在应用程序开发中经常使用,在成员变量的类型或者方法返回值类型的前面加上关键字static,就能将该成员定义为静态成员,静态成员属于类而不属于实例。静态成员的应用范围很广泛,比如,可在应用程序的全局内获取或设置公共的静态成员的值,这非常类似于C++的全局变量,但如果应用不当,会给应用程序带来很多麻烦。
例3.5 初始化静态成员时产生的陷阱。(光盘位置:光盘\MR\Instance\3\05\InitStaticMember)
在本实例中定义一个名为Test 的类,该类要实现的功能是这样的,当使用无参的构造器实例化该类时,调用该类的getName ()方法获取类内私有字段的默认值,当使用有参的构造器实例化该类时,调用该类的getName ()方法获取构造器传入的参数值,具体代码如下:
private static String strStaticName = "明日"; //无参构造器 public Test () { } //有参构造器 public Test (String strName) { strStaticName = strName; } //自定义方法,用于返回字段strStaticName的值 public String getName () { return strStaticName; }
注意
上面代码中的strStaticName字段是静态的。
本实例创建的是一个控制台应用程序,在main ()方法中创建了两个Test 类的实例,一个使用无参构造器实例化,另一个使用有参构造器实例化,具体代码如下:
public static void main (String[] args) { //创建实例t1 System.out. println ("创建t1实例时,名称设置为东方"); Test t1 = new Test ("东方"); System.out. println ("所以使用GetName方法获取的名称为:"+t1. getName ()); System.out. println (""); //创建实例t2 System.out. println ("创建t2实例时,名称未设置"); Test t2 = new Test (); String strName = t2. getName (); System.out. println ("但通过GetName方法获取的名称却为:"+strName); }
按照最初的设计思路,程序的运行结果应该是这样,t1实例调用getName ()方法的返回值应该为“东方”, t2实例调用getName ()方法的返回值应该为默认的值“明日”,但结果却完全出乎意料,运行结果如图3.3所示。
图3.3 运行结果
说明
由于静态成员变量strStaticName 在类的两次实例化过程中,只被初始化一次,也就是说,第二次实例化Test类时,静态成员变量strStaticName仍然保留第一次实例化Test类时的值,这就是问题的所在,所以在设计程序时,不要乱用静态成员,否则会适得其反。
注意
在一个应用程序中,无论创建了一个类的多少个实例,该类的静态字段都只被初始化一次。
2.陷阱的解决方法
本实例产生的陷阱,并不是什么系统错误或程序异常,而是对静态概念认识不够所造成的,相信很多有经验的程序员也犯过类似的错误.。本实例的解决办法很多,这里列出以下三种比较简单的解决方法。
· 取消静态关键字static。
· 当创建Test类对象时,都使用有参的构造器。
· 在无参构造器内,给静态字段strStaticName赋值,可以使用如下代码:
public Test () { strStaticName = "明日"; }
技巧
在程序中,由于静态成员被类的所有实例所共享,因此可以将静态成员变量作为全局变量使用。