如果子类继承了父类的某些成员变量,则子类就可以访问这些变量,就像这些变量是在子类中声明的一样。然而,现在有这样一个问题:这些成员变量是从父类复制了一份,还是与父类共享同一个变量呢?
我们首先看实例变量。
例:
package deep;
public class FieldSharing {
public static void main(String[] ary) {
Water w1 = new Water();
Water w2 = new Water();
w1.go();
System.out.println(w1.des == w2.des);
w2.print("对象w2的des:");
}
}
class Liquid {
protected String des = "des";
}
class Water extends Liquid {
public void go() {
System.out.println(super.des == this.des);
print("修改之前……");
this.des = "modify this.des";
print("修改了this.des……");
super.des = "modify super.des";
print("修改了super.des……");
}
public void print(String msg) {
System.out.println(msg);
System.out.println("super.des:" + super.des);
System.out.println("this.des:" + this.des);
}
}
运行结果:
true
修改之前……
super.des:des
this.des:des
修改了this.des……
super.des:modify this.des
this.des:modify this.des
修改了super.des……
super.des:modify super.des
this.des:modify super.des
false
对象w2的des:
super.des:des
this.des:des
对于继承的实例变量,如果子类没有隐藏父类的变量,则变量在父类与子类之间是共享的,其实,对于同一个对象而言,this.des与super.des就是同一个变量,两个引用指向相同的String对象,因为String对象使用“==”判断时,只有地址相同才会返回true。因此,不管修改哪一个变量的值,另外一个也会随之改变。不过每创建一个对象,都会为实例变量分配单独的空间,不同的对象,实例变量也是不同的。因而修改对象w1的实例变量不会影响到对象w2的实例变量。
如果子类隐藏了父类中的变量,则父类的变量与子类的变量就是两个不同的变量,即this.des与super.des不再是同一个变量,其中一个变量值的改变自然不会使另外一个变量值也随之改变。例如本例,如果在子类Water中隐藏了父类变量des,声明如下:
package deep;
public class FieldSharing {
public static void main(String[] ary) {
Water w1 = new Water();
Water w2 = new Water();
w1.go();
System.out.println(w1.des == w2.des);
w2.print("对象w2的des:");
}
}
class Liquid {
protected String des = "des";
}
class Water extends Liquid {
protected String des = "Water_des";// 声明
public void go() {
System.out.println(super.des == this.des);
print("修改之前……");
this.des = "modify this.des";
print("修改了this.des……");
super.des = "modify super.des";
print("修改了super.des……");
}
public void print(String msg) {
System.out.println(msg);
System.out.println("super.des:" + super.des);
System.out.println("this.des:" + this.des);
}
}
运行结果:
false
修改之前……
super.des:des
this.des:Water_des
修改了this.des……
super.des:des
this.des:modify this.des
修改了super.des……
super.des:modify super.des
this.des:modify this.des
false
对象w2的des:
super.des:des
this.des:Water_des
一旦隐藏了父类的实例变量,this.des与super.des就不再是同一个变量了。
例:
package deep;
public class FieldSharing2 {
public static void main(String[] ary) {
System.out.println(Gas.des == Hydrogen.des);
System.out.println(Gas.des == Nitrogen.des);
System.out.println(Hydrogen.des == Nitrogen.des);
print("修改之前……");
Gas.des = "modify Gas.des";
print("修改Gas.des后……");
Hydrogen.des = "modify Hydrogen.des";
print("修改Hydrogen.des后……");
Nitrogen.des = "modify Nitrogen.des";
print("修改Nitrogen.des后……");
}
public static void print(String msg) {
System.out.println(msg);
System.out.println("Gas.des:" + Gas.des);
System.out.println("Hydrogen.des:" + Hydrogen.des);
System.out.println("Nitrogen.des:" + Nitrogen.des);
}
}
class Gas {
protected static String des = "Gas";
}
class Hydrogen extends Gas {
}
class Nitrogen extends Gas {
}
运行结果:
true
true
true
修改之前……
Gas.des:Gas
Hydrogen.des:Gas
Nitrogen.des:Gas
修改Gas.des后……
Gas.des:modify Gas.des
Hydrogen.des:modify Gas.des
Nitrogen.des:modify Gas.des
修改Hydrogen.des后……
Gas.des:modify Hydrogen.des
Hydrogen.des:modify Hydrogen.des
Nitrogen.des:modify Hydrogen.des
修改Nitrogen.des后……
Gas.des:modify Nitrogen.des
Hydrogen.des:modify Nitrogen.des
Nitrogen.des:modify Nitrogen.des
子类继承父类的静态变量,如果在子类中没有隐藏该变量,则父类与子类中的静态变量是同一变量,其中一个的改变就会导致其他变量也随之改变。也就是说,父类与所有子类共享父类中的静态变量。
如果子类隐藏了父类的静态变量,则子类与父类的静态变量就是不同的变量。
例:
package deep;
public class FieldSharing2 {
public static void main(String[] ary) {
System.out.println(Gas.des == Hydrogen.des);
System.out.println(Gas.des == Nitrogen.des);
System.out.println(Hydrogen.des == Nitrogen.des);
print("修改之前……");
Gas.des = "modify Gas.des";
print("修改Gas.des后……");
Hydrogen.des = "modify Hydrogen.des";
print("修改Hydrogen.des后……");
Nitrogen.des = "modify Nitrogen.des";
print("修改Nitrogen.des后……");
}
public static void print(String msg) {
System.out.println(msg);
System.out.println("Gas.des:" + Gas.des);
System.out.println("Hydrogen.des:" + Hydrogen.des);
System.out.println("Nitrogen.des:" + Nitrogen.des);
}
}
class Gas {
protected static String des = "Gas";
}
class Hydrogen extends Gas {
}
class Nitrogen extends Gas {
protected static String des = "Nitrogen_Gas";// 隐藏父类的静态变量
}
运行结果:
true
false
false
修改之前……
Gas.des:Gas
Hydrogen.des:Gas
Nitrogen.des:Nitrogen_Gas
修改Gas.des后……
Gas.des:modify Gas.des
Hydrogen.des:modify Gas.des
Nitrogen.des:Nitrogen_Gas
修改Hydrogen.des后……
Gas.des:modify Hydrogen.des
Hydrogen.des:modify Hydrogen.des
Nitrogen.des:Nitrogen_Gas
修改Nitrogen.des后……
Gas.des:modify Hydrogen.des
Hydrogen.des:modify Hydrogen.des
Nitrogen.des:modify Nitrogen.des
子类Hydrogen没有隐藏父类Gas的静态变量,则Hydrogen.des与Gas.des是同一个变量,其中一个值改变了,另外一个的值也会改变。子类Nitrogen隐藏了父类Gas的静态变量,所以Nitrogen.des与Gas.des不是同一个变量。其中一个的值改变了,另外一个的值不会改变。
相对于实例变量,静态变量的继承更加容易出错。因为实例变量是基于对象的,每个对象都在堆上分配单独的空间来存放实例变量,不同对象之间的实例变量互不干扰。而静态变量是基于类的。由类的所有对象所共享。如果子类继承了父类的静态变量(没有隐藏),则该静态变量还由所有子类(包括子类的对象)所共享。
假设Gas类已存在,而Hydrogen类与Nitrogen类是在以后程序升级中新加入的,并且3个类在不同的编译单元(文件)甚至在不同的包中。程序员A编写Hydrogen类,并在静态初始块中奖des重新赋值:
static {
des = "Hydrogen";
}
同时,程序员B编写Nitrogen类,同样在静态初始化块中将des重新赋值:
static {
des = "Nitrogen";
}
如此一来,在使用这两个类的时候,问题就会产生。在程序运行时,假设Hydrogen类先得到初始化,则将des修改为“Hydrogen”,后来,Nitrogen类也得到初始化,就会将des修改为“Nitrogen”,由于两个类使用的des是同一个变量,这便无意中修改了Hydrogen类的des,当再次使用Hydrogen类的des时,就会得到不正确的结果。
这个问题告诉我们,如果类的静态变量不是private类型的,通常也应该同时将其声明为final,以免发生上面所述的现象。