类的加载顺序,父类和子类初始化的顺序和重写所遇到的上塑造型

来源:互联网 发布:证券从业资格证软件 编辑:程序博客网 时间:2024/06/12 18:55
类的加载顺序 什么时候类加载 第一次需要使用类信息时加载。 类加载的原则:延迟加载,能不加载就不加载。 触发类加载的几种情况: (1)、调用静态成员时,会加载静态成员真正所在的类及其父类。 通过子类调用父类的静态成员时,只会加载父类而不会加载子类。 (2)、第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。 (3)、加载子类会先加载父类。(覆盖父类方法时所抛出的异常不能超过父类定义的范围)     注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。     例:public static final int a =123; 但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。     例:public static final int a = math.PI 如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载; 如果编译时不能确定其值的话,则运行时加载 .类加载的顺序: 1.加载静态成员/代码块:     先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。     同一个类里的静态成员/代码块,按写代码的顺序加载。     如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。     调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。 2.加载非静态成员/代码块:(实例块在创建对象时才会被加载。而静态成员在不创建对象时可以加载)     先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。     同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。     但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。     调用父类的非静态成员(private 除外),也可以像调用自己的一样。 3.调用构造方法:    先递归地调用父类的构造方法(Object的最先)也就是上溯下行;默认调用父类空参的,也可在第一行写明调用父类某个带参的。     再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。     注意:如果加载时遇到 override 的成员,可看作是所需创建的类型赋值给当前类型。     其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。     假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。     由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;     由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。
一例子:package com.base.chapter4;class Base{    public static int a = 10;    public int b = 20;    static    {        System.out.println("Static Init Base " + a);        //System.out.println("Null Init " + b);    }    public Base()    {        System.out.println("Init Base " + this.b);    }}/** *一级子类和基类包含的内容一样 **/class SuperClass extends Base{ //静态变量、静态块执行顺序,按书写先后顺序    public static int a1 = getSuperStaticNumber();    public int b1 = getSuperInstanceNumber();    public SuperClass()    {        System.out.println("Init SuperClass" + this.b1);    }    static    {        System.out.println("Static Init SuperClass" + a1);    }    public static int getSuperStaticNumber()    {        System.out.println("Static member init");        return 100;    }    public int getSuperInstanceNumber()    {        System.out.println("Instance member init");        return 200;    }}/** *二级子类为测试该代码的驱动类 */public class Sub extends SuperClass{    public static int a2 = getStaticNumber();    public int b2 = getInstanceNumber();    public Sub()    {        System.out.println("Init SubClass " + this.b2);    }    public static int getStaticNumber()    {        System.out.println("Static member init Sub");        return 1000;    }    public int getInstanceNumber()    {        System.out.println("Instance member init Sub");        return 2000;    }    static    {        System.out.println("Static Init " + a2);    }    /**     * 程序入口,main     *      * */    public static void main(String args[])    {        new Sub();    }}结果:Static Init Base 10Static member initStatic Init SuperClass100Static member init SubStatic Init 1000Init Base 20Instance member initInit SuperClass200Instance member init SubInit SubClass 2000
View Code

 

继承中注意的问题:

1  重写(也叫覆盖,也叫重构):重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型;

            重写方法不能使用比被重写方法更严格的访问权限。

重写是类与类之间的关系,两者必须是继承关系。重载是方法与方法之间的关系。

 

2  关键字super:

    在Java类中使用super来引用父类的成分

    super可用于访问父类中定义的属性

    super可用于调用父类中定义的成员方法

    super可用于在子类构造方法中调用父类的构造方法

    super的追溯不仅于直接父类,就是father的father。。。。。。

Supper关键字有两个特殊的用途:

    在子类构造函数中调用父类的构造函数

    在子类中调用父类的方法。

 

3  多态:多态就是重写和重载!而上塑造型或虚方法的调用更好的体现了多态。

             上溯造型(也叫虚方法,将子类转换成父类)中,方法是调用子类自己的,属性是调用父类的,其他就不变。(一个引用类型变量如果声明为父类的类型,

             但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法)

package cn.test;public class Father {   public String name = "Father";  public void bidBoy(){    System.out.println(" 父类的 bidBoy被调用 ");   }  public void say(Father father){    System.out.println("调用父类的方法,该对象的类型为:"+father.getClass());    father.bidBoy();  }  public void say(Son son){    System.out.println("*******************调用父类的方法,该对象的类型为:"+son.getClass());    son.bidBoy();  }}
package cn.test;public class Son extends Father{  public String name = "Son";  public void bidBoy(){ //重写    System.out.println(" 子类的 bidBoy被调用 ");    System.out.println(name);//如果子类没有name,那么这个name就输出父类的。  }  public void say(Father father){    System.out.println("调用子类的方法,该对象的类型为:"+father.getClass());    father.bidBoy();  }  public void say(Son son){    System.out.println("*******************调用子类的方法,该对象的类型为:"+son.getClass());    son.bidBoy();  }}

 

package cn.test;public class Test{public static void main(String args[]){/* * 总结:* 1、 从父类(真实类型)不能转换称子类,编译没有错,运行就会报错。例如:Father f = new Father(); Son son1 = (Son) f;* 2、 上溯造型中Father f1 = new Son(),方法是调用子类自己的,属性是调用父类的,如测试1。* 3、 对于类中的重载方法调用,如果对象是上溯造型,则会调用参数为父类的方法。如果直接创建子类对象,则调用参数为子类类型的方法。* 4、 对于类中的重载方法调用,当前参数是什么类型(包括强制转换),就进入那个重载方法。* 5、 上溯造型调用子类的方法,父类的属性;对子类和父类做为函数的重载类型时,当前对象是谁就进入那个重载方法。如测试7。*///下面对象中f1,f2是上溯造型,他们调用bidBoy方法时,调用的都是子类中的方法  System.out.println("-----------------测试1---------------------");  Father f1 = new Son(); //上溯造型,虚方法调用  f1.bidBoy(); //调用子类方法  System.out.println(f1.name); //调用父类的属性名称  System.out.println("-----------------测试2---------------------");  Father f2 = new Son();  Son son = (Son) f2;  son.say(f2); //子类say方法被调用,这个方法需要一个Father对象,然后子类调用bidBoy方法。  System.out.println("-----------------测试3---------------------");  Father father = new Father();  son.say(father); //子类say方法被调用,传入的父类对象,调用父类的bidBoy方法。  System.out.println("-----------------测试4---------------------");  f2.say(f1); //子类say方法被调用,传入的是上溯造型,然后子类调用bidBoy方法。  System.out.println("-----------------测试5---------------------");  father.say(f2); //父类say方法被调用,传入的是上溯造型,然后子类调用bidBoy方法。  System.out.println("-----------------测试6---------------------");  father.say(father); //父类say方法被调用,传入的父类对象,调用父类的bidBoy方法。  System.out.println("-----------------测试7---------------------");  Son son2 = new Son();  f2.say(son2);  //son2当前是Son,那么进入是say(Son son)方法。  f2.say(f1);      //f1上溯造型,但是当前被转换成Father对象,那么进入的是say(Father father)方法。  f2.say(father);/*-----------------测试1---------------------子类的 bidBoy被调用 SonFather-----------------测试2---------------------调用子类的方法,该对象的类型为:class cn.test.Son子类的 bidBoy被调用 Son-----------------测试3---------------------调用子类的方法,该对象的类型为:class cn.test.Father父类的 bidBoy被调用 -----------------测试4---------------------调用子类的方法,该对象的类型为:class cn.test.Son子类的 bidBoy被调用 Son-----------------测试5---------------------调用父类的方法,该对象的类型为:class cn.test.Son子类的 bidBoy被调用 Son-----------------测试6---------------------调用父类的方法,该对象的类型为:class cn.test.Father父类的 bidBoy被调用 -----------------测试7---------------------*******************调用子类的方法,该对象的类型为:class cn.test.Son子类的 bidBoy被调用 Son调用子类的方法,该对象的类型为:class cn.test.Son子类的 bidBoy被调用 Son调用子类的方法,该对象的类型为:class cn.test.Father父类的 bidBoy被调用*/}}

同类收集( homogenous collections)

 

    MyDate[] m = new MyDate[2];

    m[0] = new MyDate(22, 12, 1964);

    m[1] = new MyDate(22, 7, 1964);

 

  异类收集(heterogeneous collections)

 

    Person [] p= new Person[3];

    p[0] = new Student();//跟person有继承关系

    p[1] = new Person();

    p[2] = new Graduate();//跟person有继承关系

 

方法声明的参数类型为父类类型,可以使用子类的对象作为实参调用该方法

public class Test{

    public void method(Person e) {

               //……

               e.getInfo();

    }

    public static  void main(String args[]){

               Test t = new Test();

                Student m = new Student();

                t.method(m);

    }

}

 

4 instanceof 操作符:instanceof操作符的作用是判断一个变量是否是右操作数指出的类的一个对象,

    由于java语言的多态性使得可以用一个子类的实例赋值给一个父类的变量,而在一些情况

    下需要判断变量到底是一个什么类型的对象,这时就可以使用instanceof了。当左操作数是

    右操作数指出的类的实例或者是子类的实例时都返回真,如果是将一个子类的实例赋值给

    一个父类的变量,用instanceof判断该变量是否是子类的一个实例时也将返回真。

 

5 对象造型 :

    对Java对象的强制类型转换称为造型

    在造型前可以使用instanceof操作符测试一个对象的类型

    从子类到父类的类型转换可以自动进行

    从父类到子类的类型转换必须通过造型(强制类型转换)实现

    无继承关系的引用类型间的转换是非法的

 

public class Test{              public void method(Person e) {          System.out.println(e.getschool());   //非法,因为从子类到父类的类型转换可以自动进行,故这里已经是父类了。父类的对象只能调用父类中有的方法。          if(e intstanceof Student){                  Student me = (Student)e;                  System.out.println(me.getschool());          }              }        public static  void main(Stirng args[]){               Test t = new Test();               Student m = new Student();               t.method(m);        }}

 

 

二、父类和子类初始化的顺序:

package com.cjonline.ship.action;public class Parent {        public String a = "test";    public Parent() {        super();        System.out.println(1);    }    public Parent(String a) {        super();        System.out.println(2);        //System.out.println(pstr); //    }    {        System.out.println(" Parent hello...");    }    static {        System.out.println(" Parent static 1...");    }    static {        System.out.println(" Parent static 2...");    }        public static String pstr=init();        public static String init(){        System.out.println(" 静态属性和静态代码块之间初始化的顺序与放在类中的位置有关,靠前面的先执行");        return "Parent static properties init";    }    public  String pstr2=init2();        public  String init2(){        System.out.println(" 成员初始化优先于构造函数");        return "Parent static properties init";    }    public static void main(String[] args) {        Child child = new Child("12");        Parent test = (Parent) child;        System.out.println(test.a);        System.out.println(child.a);        System.out.println(child.a);    }    static class Child extends Parent {                public String a = "child";                public Child() {            super();            System.out.println(3);        }        public Child(String a) {//            super(a);//写这行代码调用父类带参数的Parent(String a)构造函数输出2,否则调用父类默认的构造函数Parent()输出1            System.out.println(4);            new Parent("");        }        {            System.out.println(" Child hello...");        }        static {            System.out.println(" Child static...");        }            }    }/**执行结果: Parent static 1... Parent static 2... 静态属性和静态代码块之间初始化的顺序与放在类中的位置有关,靠前面的先执行 Child static... Parent hello... 成员初始化优先于构造函数1 Child hello...4 Parent hello... 成员初始化优先于构造函数2testchildchild*/ // 最终结论    // 1.父类静态属性或静态代码块    // 2.子类静态属性或静态代码块    // 3.父类属性初始化    // 4.父类构造函数    // 5.子类属性初始化    // 6.子类类构造函数

 静态优先于非静态(属性或方法或构造函数),父类构造函数优先于子类构造函数

0 0
原创粉丝点击