java继承&子父类成员域加载及执行过程

前段时间偶然看到一篇文章,关于java继承的,讲其加载顺序,然后我就看蒙了,虽然接触过不少时间java,用起来因为编译器的存在没什么语法问题,但真正用notepad++时,发现细节真的很重要,下面看测试时用的例子。

package one;
import java.util.*;

public class Test extends Parent {
    static int c=getInt(1);//2初始化子类静态成员域 
    int d=getInt(2);//6初始化子类非静态成员域
    private String name;
    private int age;
    public Test() {
        name="Tom";
        age=20;//此时可以给成员域赋值,这是在成员域已经进行了默认的赋值之后才进行的操作,也就是说不赋值也不会有编译错误。
        System.out.println("7 子类构造函数执行。注意,父类子类成员初始化构造函数执行生成实例后,new操作终于算完成。");
    }
    public static void main(String[] args) {
         System.out.println("3 执行与对象实例化无关的操作。");//3执行与对象实例化无关的操作。
        Test t = new Test();
        // 7父类子类成员初始化构造函数执行生成实例后,new操作终于算完成。
        System.out.println("8 执行剩余无需生成实例的代码。此时注意,t是子类实例,该类中没有num成员,而此时编译无错误,说明子类t同时是父类的实例  " + t.age+"  "+t.num);
        // 8执行剩余无需生成实例的代码。此时注意,t是子类实例,该类中没有num成员,而此时编译无错误,说明子类t同时是父类的实例
        t.c();
        // 9子类对象执行c方法,虽然父类中同样有c方法,但不会被执行。 

        System.out.print("\n\n\n下来来看另一个实例:在子类中new父类实例,看看执行的步骤:\n");
        Parent p=new Parent();
        p.c();
        //用父类实例来调用c,看执行的到底是子类还是父类中的方法。

            //System.out.println(" "+p.getInt(1));
            //尝试用p来调用test类中的方法getInt,编译器会报错:找不到符号:类型为Parent的变量P,一个错误。也就是说p为Parent的实例,但不是子类的实例。

        System.out.print("\n\n\n下来来看另一个实例:在子类中new子类实例,然后赋值给父类对象,也就是向上转型,看看执行的步骤:\n");
        Parent pa=new Test();
        //此时编译正常通过,向上转型没有丝毫问题,不需要强制。
    }

    @Override//注:@Override只是提醒自己这是个与父类同名同参的函数,并无实际作用,切不要被误导。
    public void c(){
        System.out.println("9 子类对象执行c方法,虽然父类中同样有c方法,但不会被执行。");
    }

    public static int getInt(int i){
        System.out.println((i==1)?"2 初始化子类静态成员域":"6 初始化子类非静态成员域");
        return i;
    }
}
class Parent {
    static int K=1;
    int sum=test(3);
    int num = test(2);//4需要生成子类实例时,先初始化父类非静态成员域,即便没有使用到的成员也会被初始化,这可以看出,是按照顺序初始化的,
    static int sam=test(K);//1从子类main方法入口进入后,最先初始化父类静态成员域。“在类被加载的同时会将静态成员进行加载。而且看我这里调用的是K的值,如果只是加载了静态成员而没有被赋值的话,那K应该为0,然而此时K为1”
    public Parent() {
        System.out.println("5 执行父类构造函数,生成父类实例对象。");
        //5执行父类构造函数,生成父类实例对象。
    }
    public static int test(int i) {
        switch(i){
            case 1:{
                System.out.println("1 从子类main方法入口进入后,最先初始化父类静态成员域sam");
                break;
            }
            case 2:{
                System.out.println("4 先初始化父类非静态成员域num");
                break;
            }
            case 3:{
                System.out.println("4 先初始化父类非静态成员域sum");
                break;
            }
        }
        return i;
    }
    public void c(){
        System.out.println("被子类覆盖了的父类方法,子类对象不能直接访问 ");
    }
}

我知道大家最不喜欢的就是看代码,因为它远不如文字来的形象直观,并且不能跳跃,其实我也不喜欢,所以,可以对照着输出来看源码:
这里写图片描述
为了我以后方便查看,我把print的内容都换了,这样就直观多了,注释上有序号,对照着输出看,如果真是懒得看,那直接看从中得出的结论。(注:源码可执行,有不明的地方可以将其他部分注释掉单独看结果。)

如果子类main方法中不执行new操作,那么执行结果为:
这里写图片描述
可以看到只有父类和子类的静态成员域得到初始化,非静态成员域没有得到任何初始化,并且可以试着使创建父类对象,可以得出:
1、要创建子类对象时,会自动先创建父类的实例对象。
2、在类被加载的同时会将静态成员进行加载并得到初始化且能赋值。
3、在类的实例被创建时,非静态成员才会得到初始化,然后调用类的构造函数生成实例。
4、子类实例同时是父类实例,可以调用父类方法,成员也是可以调用的(如例子中子类无num,但可以使用),如果父类子类有相同的方法或属性,优先调用子类中的(使用super可以调用父类中方法及属性)。
5、父类的实例不是子类的实例。不能调用子类方法。

后面两个模块之所以没有第一到第三步,是因为我没有注释完全,不影响查看。代码中注字部分是要点。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章