• Java编程思想学习笔记_2(继承和多态)


    • 静态初始化:

       静态初始化只在必要的时刻进行.(即当程序需要加载类进入内存的时候,执行静态初始化.静态变量和静态代码块的初始化顺序,按照在代码中声明的顺序老执行.例如:如果要执行某个public类,那么首先需要加载进内存,这时候就开始静态初始化.随后将主函数加载进栈.静态初始化只在Class对象首次加载的时候执行一次.(对象不创建,非静态变量不初始化.类不加载,静态变量不初始化.下面是代码示例:

    public class Test3 {
        public static void main(String[] args) {
            /*主函数中不声明任何语句静态变量也会初始化,因为主函数进栈就需要把类加载进内存.*/
               //ADog dog;            //声明引用,非静态变量也不会初始化
          //ADog dog3=new ADog();   //创建对象.执行非静态变量和构造函数的初始化    
        }
        static ADog dog1=new ADog();
        static ADog dog2=new ADog();
        static {
            System.out.println("汪汪!");
        }    /*静态代码块和静态变量初始化顺序看语句中的顺序.*/
    }
    class ADog {
        public ADog() {
            System.out.println("小狗狗在此!");
        }
    }
    • 可变参数列表:

      参数列表中,采用参数类型加...代表数组的称为可变参数.例如:int... 可变参数可以接受任意大小的元素进入数组,编译器会为你去填充数组.例如:

      

    f(int ... arr)        //f(1)OK  f(2,3,4)OK

      实际上arr也是数组,可以接受任意类型的数组.这里有几个特殊情况说明:

    1. 如果向可变参数列表中传入了一个数组,而不是一系列数组的元素,编译器会发现它已经是个数组了,所以不会在其上做任何转换.
      public class Test4 {
          public static void main(String[] args)  {
      //        
              Integer[] ins={1,2,3,4};
              f(ins);
          }
          static void f(Integer...arr) {
              for(int x:arr) {
                  System.out.println(x);
              }
          }

      输出1,2,3,4

    2. 如果向一个Object参数的可变参数列表传入一个基本数据类型的数组,则会将整个数组引用当成一个Object,因此会输出数组的引用的toString.
      public class Test4 {
          public static void main(String[] args) {
              f((new int[]{1,2,3,4}));
          }
          static void f(Object...arr) {
              for(Object x:arr) {
                  System.out.println(x);    //输出[I@15db9742
              }
          }

       3.可变参数列表也是存在自动装箱的,因此如果往Integer...可变参数列表中加入一系列int元素是可以的.但是如果在其中传入int[]元素,编译器在检查的时候会发现int[]元素无法装入Integer[]中,因此报错.

    public class Test4 {
        public static void main(String[] args)  {
            f(1,2,3,4);        //Ok
            f(new int[]{1,2,3,4});    //Error
        }
        static void f(Integer...arr) {
            for(Object x:arr) {
                System.out.println(x);
            }
        }

      4.应该总是在重载方法的一个版本上使用可变参数,或者根本不使用它.因为可变参数有可能有的时候,表面看上去方法不同,但是在传入参数的时候,两个方法都可以调用.

    public class Test4 {
        public static void main(String[] args) {
            f('a','b');        //编译错误.
        }
        static void f(float ... args) /*float也接受char类型.*/{
            System.out.println("first");
        }
        static void f(Character... args) {
            System.out.println("second");
        }
    }

      这个特性显著不同于在方法中传递参数,传入的参数将优先寻找类型匹配的参数,但是在可变参数中,只要可接受,编译器就默认可以装入数组.容易造成方法调用的不确定性.

    public class Test4 {
        public static void main(String[] args) {
            f('a');
        }
        static void f(float args)/*可接受char.但是优先匹配Character和char,然后是int,然后才是float*/{
            System.out.println("first");
        }
        static void f(Character args) {
            System.out.println("second");
        }
    }
    • 在继承的时候确保正确清理

      在清理方法中,还必须注意对基类清理方法和成员对象清理方法的调用顺序,以防止某个子对象依赖于另一个子对象的情形的发生.一般而言,首先执行类的所有特定清理动作,其顺序同生成顺序相反(通常这要求基类元素仍然存活.然后再调用基类的清理方法.

    • final参数

      Java允许在参数列表中以声明的方式将参数声明为final.这意味着无法在方法中更改参数的值.(引用无法更改参数指向的对象).

    • final和private关键字

      类中所有的private方法都隐式的被指定为final的,由于无法取用private方法,所以也就无法覆盖它.可以给private方法添加final修饰词,但这并不能给方法增加额外的意义.覆盖只有在能将一个对象向上转型并调用它的方法的时候才会出现,如果方法是private,则它不是基类接口的一部分,仅仅是隐藏与类中的程序代码,当声明一个public protected和包访问权限的方法的话,该方法不会产生在基类中出现的仅仅具有相同名称的情况.此时并没有覆盖方法,仅仅是生成了一个新的方法.下面的代码说明了这个问题:

    public class Dad {
        private void say() {
            System.out.println("I am Dad");
        }
        public static void main(String[] args) {
            Dad d=new Son();    
            //子类的Public访问权限的say相当于定义在子类的全新的方法,不符合动态绑定的情况,仍然使用父类的say方法
            d.say();//输出I am Dad
        }
    }
    class Son extends Dad{
        public void say() {
            //在子类中无法访问父类的private方法,private包装在子类对象内部的父类对象内,无法访问.
            //super.say();错误.无法访问,类的访问权限.
            System.out.println("I am son.");
        }
    }
    • 初始化以及类的加载

      下面的代码提供了初始化的示例:

    import static net.mindview.util.Print.*;
    class Insect {
        private int i=9;
        protected int j;
        Insect() {
            print("i="+i+"j="+j);
            j=39;
        }
        private static int x1=printInit("static Insect.x1 initialized.");
        static int printInit(String s) {
            print(s);
            return 47;
        }
    }
    public class Beetle extends Insect{
        private int k=printInit("Bettle.k initialized");
        public Beetle() {
            print("k="+k);
            print("j="+j);
        }
        private static int x2=printInit("static Bettle.x2 initialized");
        public static void main(String[] args) {
            print("Beetle constructor:");
            Beetle b=new Beetle();
        }
    }

      首先在Beetle上运行Java的时候,视图访问Beetle.main,于是加载器开始启动并找出Beetle类的编译代码,在对它进行加载的过程中,编译器注意到它有一个基类,于是它继续进行加载,不管是否打算产生一个基类的对象,这都会发生.

      接下来,根基类的static元素会被初始化,然后是下一个子类,以此类推.这种方式很重要,因为子类的static初始化可能会依赖于基类成员是否被正确初始化.至此,必要的类加载完毕,对象就可以创建了.首先对象中所有的基本类型都会设为默认值,对象的引用被设为null-然后基类的构造器会被调用,基类构造器和子类的一样,以相同的顺序经历相同的过程,在基类构造器完成后,实例变量按次序被初始化.最后,构造器的其余部分被执行.

    • 多态发生时的域

      只有普通的方法调用才可以是多态的.如果直接访问某个域,这个访问就在编译器开始进行解析:

    class Super {
        public int field=0;
        public int getField() {
            return field;
        }
    }
    class Sub extends Super {
        public int field=1;
        public int getField() {
            return field;     //相当于return this.field.
        }
    }
    public class Test3 {
        public static void main(String[] args) {
            Super sup=new Sub();
            System.out.println("sup.field="+sup.field+",sup.getField="+sup.getField());  //sup.field=0,sup.getField=1
        }
    }

      当Sub对象转型为Super引用的时候,任何域访问操作都将有编译器解析,因此不是多态的.Sub实际上包含两个称为field的域,它自己的和从Super处得到的.而在引用Sub中的field域时候所产生的默认域并非Super版本的field域.

  • 相关阅读:
    vue3.0中如何使用ueditor
    如何在vue+element中实现选择框和穿梭框的根据拼音以及拼音首字母以及汉字的模糊搜索
    select 使其默认选中文本不为空
    java环境配置
    amaze ui 滚动监听
    vue项目中如何使用less
    强大的css3库
    input type file兼容性
    select中想要加a链接 并且新窗口打开
    echarts绘制k线图为什么写candlestick类型就报错
  • 原文地址:https://www.cnblogs.com/hlhdidi/p/5652422.html
Copyright © 2020-2023  润新知