zoukankan      html  css  js  c++  java
  • Java基础-学习笔记(十一)——类的继承性

    1、为什么会有继承性

     1 class Person
     2 {
     3     String name;
     4     int age;
     5     String getInfo(){...}
     6 }
     7 class Student
     8 {
     9     String name;
    10     int age;
    11     String school;
    12     String getInfo(){...}
    13     String study(){...}
    14 }

    在编写代码过程中,可能会出现如上所示的情况,Student类中包含了Person类中的方法和属性。我们针对这种情况,就引入了继承这个概念,只要表明Student类继承了Person类中的所有属性和方法,就不用再在Student中重写Person类中的属性和方法,也就是简化了类的定义,如下所示:

    class Person
    {
        String name;
        int age;
        void getInfo()
        {
            System.out.println("name="+name+" "+"age="+age);
        }
    }
    class Student extends Person
    {
        String school;
        void study()
        {
            System.out.println("school="+school);
        }
        public static void main(String [] args)
        {
            s1.name="Jane";
            s1.age=23;
            s1.school="清华";
            s1.getInfo();
            s1.study();
        }
    }
    /*
    F:java_examplelesson5>java Student
    name=Jane age=20
    school=清华*/

    2、继承性的特点

    1)可以简化类的定义;

    2)java只支持单继承,不允许多重继承。也就是说,一个类不能同时继承多个类;

    3)java支持多层继承。即,A类可以继承B类,B类可以继承C类,...,重复再多层也支持。

    4)子类只继承父类(或称为基类、超类)所有的成员和方法,不能继承父类的构造函数,需要通过子类构造方法中使用语句super(参数列表)调用父类的构造函数;

     1 class Person
     2 {
     3     String name;
     4     int age;
     5     Person(String name,int age)
     6     {
     7         this.name=name;
     8         this.age=age;
     9     }
    10     void getInfo()
    11     {
    12         System.out.println("name="+name+" "+"age="+age);
    13     }
    14 }
    15 class Student extends Person
    16 {
    17     String school;
    18     Student(String name,int age,String school)
    19     {
    20         super(name,age);
    21         this.school=school;
    22     }
    23     void study()
    24     {
    25         System.out.println("school="+school);
    26     }
    27     public static void main(String [] args)
    28     {
    29         Student s1=new Student("Jane",20,"清华");
    30         s1.getInfo();
    31         s1.study();
    32     }
    33 }
    34 /*
    35 F:java_examplelesson5>java Student
    36 name=Jane age=20
    37 school=清华*/

    5)子类对象实例化过程

    注意事项: 子类对象创建时需要调用父类的构造函数

    如下代码在编译过程中出错

     1 class Person
     2 {
     3     String name;
     4     int age;
     5     Person(String name,int age)
     6     {
     7         this.name=name;
     8         this.age=age;
     9     }
    10         //Person(){}
    11     void getInfo()
    12     {
    13         System.out.println("name="+name+" "+"age="+age);
    14     }
    15 }
    16 class Student extends Person
    17 {
    18     String school;
    19     void study()
    20     {
    21         System.out.println("school="+school);
    22     }
    23     public static void main(String [] args)
    24     {
    25         Student s1=new Student();
    26         s1.name="Jane";
    27         s1.age=23;
    28         s1.school="清华";
    29         s1.getInfo();
    30         s1.study();
    31     }
    32 }
    View Code

    报错如下:

    F:java_examplelesson5>javac lesson5.java
    lesson5.java:19: 错误: 无法将类 Person中的构造器 Person应用到给定类型;
    class Student extends Person
    ^
    需要: String,int
    找到: 没有参数
    原因: 实际参数列表和形式参数列表长度不同
    1 个错误

    解决方法:将无参数的Person构造函数注释去掉。以后只要是定义了类有参数的构造函数,需要把其无参数的构造函数同样写上,避免类似错误。

    现象说明:在构造Student类的s1对象的过程中,需要调用父类Person的无参数构造函数,但是由于,父类已存在的构造函数是有参数的,那么编译器不会再给Person自动生成无参数的构造函数,所以s1对象就无法创建成功

     1 class Person
     2 {
     3     String name="unknown";
     4     int age=-1;
     5     Person(String name,int age)
     6     {
     7         this.name=name;
     8         this.age=age;
     9     }
    10     Person()
    11     {
    12         System.out.println("Is calling");
    13     }
    14     void getInfo()
    15     {
    16         System.out.println("name="+name+" "+"age="+age);
    17     }
    18 }
    19 class Student extends Person
    20 {
    21     String school="unknown";
    22     Student(String name,int age,String school)
    23     {
    24         super(name,age);
    25         this.school=school;
    26     }
    27     /*Student(String name,int age)
    28     {
    29         super(name,age);
    30     }
    31     Student(String name,int age,String school)
    32     {
    33         this(name,age);
    34         this.school=school;
    35     }
    36     */
    37     void study()
    38     {
    39         System.out.println("school="+school);
    40     }
    41     public static void main(String [] args)
    42     {
    43         Student s1=new Student("Jane",20,"清华");
    44         s1.getInfo();
    45         s1.study();
    46     }
    47 }
    View Code

    这段代码说明了,子类对象创建时会按需选择父类中对应的构造函数。

    具体实例化步骤:

    第一步 分配成员变量的内存空间并进行默认的初始化,就是在new对象的过程中,会按照系统的默认值给各个成员赋初值,例如,String类型赋值为“null”,int类型为0等等;

    第二步 绑定构造方法参数,就是将new Student("Jane",20,"清华")中的值传给对应的构造函数中的形参

    第三步 不会立马将构造函数形参的值赋值给实参,而是,检查有没有this()方法调用。如果有,则调用相应的重载构造函数(被调用的重载构造函数,又从第二步开始执行),该过程结束后,回到当前的构造方法,直接转到第六步;如果没有,则执行下一步骤;

    将代码中Student的重载构造函数换成如下

    Student(String name,int age)
    {
      super(name,age);
    }
    Student(String name,int age,String school)
    {
      this(name,age);
      this.school=school;
    }

    第四步 显示(就是执行super语句)或者隐式追溯调用父类的构造方法(根据继承性,一直追溯到最上层的父类)。在这个过程中,也是从第二步开始,全部结束后,继续下一步骤;

    第五步 进行实例变量的显式初始化操作,也就是执行在定义成员变量时对它赋初值的语句。即,将unknown赋给name。没有则跳过该步骤

    第六步 执行当前构造方法中的程序代码,即执行this.school=school。super或this已经在之前的步骤中执行过了,注意区别,上文提到的this()和this.school=school语句,前者调用的是构造方法,后者只是一个普通的语句

    super()能和this()放同一个函数中么?我们通过this()间接来调用父类的构造方法,作用是和super()一致的,所以没意义,程序也不允许

    super()、this()可以放在方法体中任意位置么?NO,只能放在对应构造方法体中的第一句,要不然就和上面的流程冲突了

    3、覆盖父类的方法

    在子类中可以根据需要更改从父类继承过来的方法---方法的覆盖或重写

    覆盖的方法和被覆盖的方法两者具有相同的名称、参数列表和返回值类型

     1 class Person
     2 {
     3     String name="unknown";
     4     int age=-1;
     5     public Person(String name,int age)
     6     {
     7         this.name=name;
     8         this.age=age;
     9     }
    10     public Person()
    11     {
    12         System.out.println("Is calling");
    13     }
    14     public void getInfo()
    15     {
    16         System.out.println("name="+name+" "+"age="+age);
    17     }
    18 }
    19 class Student extends Person
    20 {
    21     String school="unknown";
    22     public Student(String name,int age,String school)
    23     {
    24         super(name,age);
    25         this.school=school;
    26     }
    27     public void getInfo()//覆盖父类中的getInfo()
    28     {
    29         System.out.println("name="+name+" "+"age="+age+" "+"school="+school);
    30         //super.getInfo();可以直接调用父类的getInfo()
    31     }
    32     public void study()
    33     {
    34         System.out.println("I'm studing");
    35     }
    36     public static void main(String [] args)
    37     {
    38         Student s1=new Student("Jane",20,"清华");
    39         s1.getInfo();
    40         s1.study();
    41     }
    42 }
    43 /*
    44 F:java_examplelesson5>java Student
    45 name=Jane age=20 school=清华
    46 I'm studing
    View Code

    如果将子类的访问修饰符换成protected,那么编译时会报如下错

    F:java_examplelesson5>javac lesson5.java
    lesson5.java:27: 错误: Student中的getInfo()无法覆盖Person中的getInfo()
    protected void getInfo()//覆盖父类中的getInfo()
    ^
    正在尝试分配更低的访问权限; 以前为public
    1 个错误

    这说明了,如果要覆盖父类的方法,则子类的该方法的权限修饰符等级要比父类的高或者是同级;但两者均不能用private来进行修饰,super需要调用父类的getInfo()

    4、final关键字

    a 在java中声明类、属性、方法时,可以用关键字final来修饰

    b final标记的类不能被继承

    c final标记的方法不能被子类重写

    d final标记的变量(成员变量或局部变量)则为常量,只能赋值一次,并且,只能在声明或者该类的所有构造方法中显式赋值再来使用,还有,赋值后的变量只能在该类中直接使用,在类的外部不能直接使用

    java中定义常量,常用public static fianl的组合方式进行标识

    e 方法中定义的内部类只能访问该方法的final类型的局部变量,用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期

     1 class Person
     2 {
     3     public final String;// name=“Jane”;
     4     //要么在声明时初始化,要么在所有显式的构造函数中都进行初始化
     5     int age=-1;
     6     public Person(String name,int age)
     7     {
     8         this.name="Jane";
     9         this.age=age;
    10     }
    11     public Person()
    12     {
    13         this.name="Jane";//如果不写这句,会报错“可能尚未初始化变量name”
    14         System.out.println("Is calling");
    15     }
    16     public void getInfo()
    17     {
    18         System.out.println("name="+name+" "+"age="+age);
    19     }
    20 }
    21 class Student extends Person
    22 {
    23     String school="unknown";
    24     public Student(String name,int age,String school)
    25     {
    26         super(name,age);
    27         this.school=school;
    28     }
    29     public void getInfo()//覆盖父类中的getInfo()
    30     {
    31         System.out.println("name="+name+" "+"age="+age+" "+"school="+school);
    32         super.getInfo();
    33     }
    34     public void study()
    35     {
    36         System.out.println("I'm studing");
    37     }
    38     public static void main(String [] args)
    39     {
    40         Student s1=new Student("Chen",20,"清华");//name已经是常量,且值为Jane
    41         s1.getInfo();
    42         s1.study();
    43     }
    44 }
    45 /*
    46 F:java_examplelesson5>java Student
    47 name=Jane age=20 school=清华
    48 I'm studing

    如果将public final String name;写成public static final String name;只是替换这一句,程序会报错,因为用static修饰的变量,可以直接用类名来引用。那么就存在了一种隐患,我如果是通过类名来调用name,那我就不用new一个Person对象,我既然不用创建对象,那也不会调用其构造函数,从而,我的name也就没有进行初始化。所以,在通过publicstatic final修饰的变量,只能在声明时赋值

  • 相关阅读:
    C++操作Kafka使用Protobuf进行跨语言数据交互
    聊聊Disruptor 和 Aeron 这两个开源库
    DTrace arg0-kernel mode and arg1-user mode
    top
    how to write your first linux device driver
    how to compile and replace ubuntu kernel
    linux du
    c++ rvo vs std::move
    【Android】wifi开发
    无线局域网络 WIFI/WAPI/WLAN区别浅析
  • 原文地址:https://www.cnblogs.com/tiantianxiangshang33/p/4948626.html
Copyright © 2011-2022 走看看