继承中类以及成员变量初始化的讨论。
1.在讲之前首先应该了解static、final、static final修饰符的含义,这里简单介绍一下。
static:可以修饰变量、方法,被修饰的变量称之为静态变量,其初始化发生在其所在类第一次被装载的时;被修饰的方法同样。
final:可以修改变量、方法和类,被修饰的类或者方法是跟随该类的实力对象的,在该对象被加载到内存当中(调用该类的构造方法时)被初始化,被修饰的变量不可变,方法不可被override(等同于被private修饰的方法),被修饰的类不可被集成,不能是抽象的,并且成员变量和方法都是final(不可变)类型的。
static final :可以修饰变量或者方法。修饰变量是代表着这个变量是静态不可变的,初始化和static相同;被修饰的方法我没有过多的了解,觉得只是为了做一个不可被修改的静态类(感觉这样做没有意义)。
2.初始化顺序
在任何情况发生以前,将父类和子类(可以是多个)被加载到内存当中,会默认把分配给父类和子类的内存空间初始化成二进制的零。
之后根据继承的关系,从父类开始,依次执行初始化方法(调用构造器)进行实例化。
此时,类成员变量会“真正”的被初始化,按照你代码编写的样子。
3。实例
父类
public class Glyph { private int a = 1; Glyph() { System.out.println("父类构造器"); draw();//调用子类中重写的draw()方法 } void draw(){ System.out.println("Glyph.draw()"); } }
子类:
public class RoundGlyph extends Glyph { private int radius = 1; //定义一个成员变量并赋初值 public RoundGlyph() { System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+radius); } }
测试:
public class Test { public static void main(String[] args) { new RoundGlyph(); } }
运行结果:
父类构造器 RoundGlyph.draw()=0 //调用子类中重写的draw之后,打印子类的成员变量确实零。 子类构造器
4.结果分析
之所以运行的结果
RoundGlyph.draw()=0 而不是
RoundGlyph.draw()=1,是因为在父类构造器中调用子类中重写的方法时,此时子类并没有被“真正”实例化,现在还处于默认实例化的状态,子类的成员变量radius默认初始化为0,所以打印结果为零。
5.static、final、static final修饰的成员变量时的情况。
修改子类代码:---------------------static修饰时
public class RoundGlyph extends Glyph { private static int radius = 1; //初始化 static{ radius = 2;//初始化 } public RoundGlyph() { radius = 3;//初始化 System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+radius); } }
运行结果为:
父类构造器 RoundGlyph.draw()=2 子类构造器
正如前面讲到的,static会在类被装载时就被初始化,也就是说,在子类RoundGlyph被装载时变量radius就已经被初始化了,但为什么是2,重复的初始化不会报错吗? 答案是不会的,初始化时,只会根据你编写的初始化顺序进行,如果重复的对一个变量进行初始化了,后一个结果会覆盖前一个结果,所以是2。
为什么不是3呢? 那是因为此时还子类RoundGlyph还没有被“真正”的实例化,也就是说,在父类的构造方法中调用子类重写的方法时,子类还没有执行构造方法呢。
修改测试代码:
public class Test { public static void main(String[] args) { new RoundGlyph().draw(); } }
运行结果:
父类构造器 RoundGlyph.draw()=2 子类构造器 RoundGlyph.draw()=3
正如上面所说的,运行结果符合预期。
被final修饰时
子类代码:
public class RoundGlyph extends Glyph { private final int radius = 1; public RoundGlyph() { // radius = 3; //构造器初始化 System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+radius); } }
运行结果:
父类构造器 RoundGlyph.draw()=1 子类构造器
运行结果为1,为什么又为一了呢?不应该是零吗?
答案:确实应该是1 !因为radius此时的final类型的,如上面所说,final修饰的变量是不可变的,要么直接赋初值,要 么在构造器中初始化,无论如何必须初始化!上面的测试是直接赋初值,所以为了保证被final修饰的变量是不可 变的,而在构造器中又没有初始化的变量,只能在默认初始化时对其进行初始化。
如果将上面子类的修改成:
public class RoundGlyph extends Glyph { private final int radius; public RoundGlyph() { radius = 1; //构造器初始化 System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+radius); } }
运行结果:
父类构造器 RoundGlyph.draw()=0 子类构造器
这说明如果没对final修饰的变量赋值初始化时,就得在构造方法内对其进行初始化。运行结果和上面不同,也是好理解的了,因为是在构造器中初始化的,所以在没有“真正”初始化之前,radius的值一直等于0。
用static final修饰的变量,跟static修饰时很像,只是初始化有些区别
子类代码:
public class RoundGlyph extends Glyph { private static final int RADIUS = 1; // static{ // RADIUS = 1; // } public RoundGlyph() { System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+RADIUS); } }
子类代码2:(大同小异)
public class RoundGlyph extends Glyph { private static final int RADIUS ; static{ RADIUS = 1; } public RoundGlyph() { System.out.println("子类构造器"); } void draw(){ System.out.println("RoundGlyph.draw()="+RADIUS); } }
运行结果:
父类构造器 RoundGlyph.draw()=1 子类构造器
这个就不多赘述了,和static的一样。只不过初始化时static final修饰的变量是不能在构造器中被初始化的,因为static是隶属于类的,不隶属于某一个具体的实例对象,static变量存储在static storage(静态存储空间)中。