Java学习Day03

第5章 继承

5.1 类、超类和子类

  1. 关键字extends表明正在构造的新类派生于一个已存在的类。

    已存在的类称为超类(superclass)、基类(base class)或父类(parent class)。

    派生出来的新类称为子类(subclass)、派生类(derived class)或孩子类(child class)。

  2. 覆盖,overriding,也称重写。

    发生在继承中,子类重写超类的方法,方法名、参数列表、返回值类型全部要求相同。被重写的方法,不能比超类的访问权限更严格。

    注意:要与重载区分开来

  3. 子类构造器

    • 使用super调用构造器的语句必须是子类构造器的第一条语句。
    • 如果子类构造器没有显式调用超类的构造器,则将自动调用超类默认(没有参数)的构造器。如果超类没有无参的构造器,子类也没有调用超类的其它构造器,则Java编译器会报错。
    • 一个对象变量可以指示多种实际类型的现象被称为多态。在运行时能够自动选择调用哪个方法的现象称为动态绑定
  4. 继承层次

    由一个公共超类派生出来的所有类的集合被称为继承层次。在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链。

    继承层次.png

  5. 总结

    1. 继承的基本概念

      ​ 扩展父类的功能

    2. Java中使用extends关键字完成继承

      格式:

      ​ class 子类 extends 父类 { }

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      class Person{
      private String name;
      private int age;
      public String getName() {
      return name;
      }
      public void setName(String name) {
      this.name = name;
      }
      public int getAge() {
      return age;
      }
      public void setAge(int age) {
      this.age = age;
      }
      public void say(){
      System.out.println("姓名:"+this.getName()+" 年龄:"+this.getAge());
      }

      }

      class Student extends Person{
      private int score;

      public int getScore() {
      return score;
      }

      public void setScore(int score) {
      this.score = score;
      }
      public void show (){
      System.out.println("成绩:"+this.getScore());
      }
      }

      public class ExtendsDemo01 {

      public static void main(String[] args) {
      Student s = new Student();
      s.setName("张三");
      s.setAge(20);
      s.setScore(99);
      s.say();
      s.show();

      }

      }

    3. 继承的限制

      • 在Java中只允许单继承
      • 子类不能直接访问父类的私有成员变量
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      class People {
      private int age;

      public int getAge() {
      return age;
      }

      public void setAge(int age) {
      this.age = age;
      }
      private void say() {
      System.out.println("I'am a father");
      }

      }

      class Worker extends People {
      //虽不可对父类的私有成员直接访问,但可以通过其他方式进行访问,如set()和get()方法
      //子类实质上是继承了父类的所有成员变量(包括私有成员变量),但私有方法并没有继承
      public void tell() {
      System.out.println("I'am a worker,I'am "+getAge());

      }
      }

      class PetWorker extends Worker { //只允许单继承,不允许多继承,但可以多层继承
      public void tell() {
      System.out.println("I'am a petworker,I'am "+getAge());

      }
      }
      public class ExtendsDemo02 {

      public static void main(String[] args) {
      Worker wor = new Worker();
      wor.setAge(30);
      wor.tell();
      // wor.say(); //私有方法没有继承,无法使用,报错
      PetWorker petWor = new PetWorker();
      petWor.setAge(45);
      petWor.tell();

      }

      }
      //运行结果:
      //I'am a worker,I'am 30
      //I'am a petworker,I'am 45
    4. 子类对象实例化过程

      • 在子类对象实例化时,必须先调用父类中的构造方法,之后调用子类构造方法。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        class Father {
        public Father(){
        System.out.println("调用父类构造方法");
        }
        }

        class Son extends Father{
        public Son() {
        System.out.println("调用子类构造方法");
        }
        }
        public class ExtendsDemo03 {

        public static void main(String[] args) {
        Son son = new Son();

        }

        }

        //运行结果:
        //调用父类构造方法
        //调用子类构造方法
    5. 方法重写与super关键字

      1. 重写

        • 在继承中,也存在着“重写”的概念,其实就是子类定义了和父类同名的方法。

        • 定义:

          ​ 方法名称相同,返回值类型相同,参数也相同。

        • 限制:被子类重写的方法不能拥有比父类方法更加严格的访问权限

        • 访问权限:private<default<public

          四种修饰符的访问权限范围:

        权限 类内 同包 不同包子类 不同包非子类
        private × × ×
        default × ×
        protected ×
        public
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        class A {
        public void tell() {
        System.out.println("我是tell方法");
        }
        }

        class B extends A {
        public void tell() { //方法名称相同,返回值类型相同,参数也相同。
        super.tell(); //强行调用父类的方法执行
        System.out.println("我重写了tell方法");
        }
        }
        public class ExtendsDemo04 {

        public static void main(String[] args) {
        B b = new B();
        b.tell();
        }

        }
        //运行结果:
        //我是tell方法
        //我重写了tell方法
      2. super关键字

        • 强行调用父类的方法执行
        • super也可以表示那些方法是从父类中继承而来的
  6. 多态性

    1. Java面型对象多态性

      1. 多态性的体现:

        • 方法的重载和重写
        • 对象的多态性
      2. 对象的多态性:

        • 向上转型:程序自动完成

          父类 父类对象 = 子类实例

        • 向下转型:强制类型转换

          子类 子类对象 = (子类)父类实例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      class A {
      public void tell1() {
      System.out.println("A--tell1");
      }
      public void tell2() {
      System.out.println("A--tell2");
      }
      }

      class B extends A {
      public void tell1() {
      System.out.println("B--tell1");
      }
      public void tell3() {
      System.out.println("B--tell3");
      }
      }
      public class PolDemo01 {

      public static void main(String[] args) {
      // 向上转型
      // B b = new B();
      // A a = b;
      // a.tell1(); //B重写了A的tell1方法,打印结果为 B--tell1
      // a.tell2(); //打印结果为 A--tell2


      // 向下转型

      // A a = new A(); //a 和 b 没有明确关系,无法转型
      // B b = (B)a; //运行出错

      // 向下转型前,一定会有向上转型
      A a = new B();
      B b = (B)a;
      b.tell1();
      b.tell2();
      b.tell3();

      }

      }
      //运行结果:
      //B--tell1
      //A--tell2
      //B--tell3

    2. 多态性的应用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      class A1 {
      public void tell1() {
      System.out.println("tell1");
      }
      }
      class B1 extends A1 {
      public void tell2() {
      System.out.println("tell2");
      }
      }
      class C1 extends A1 {
      public void tell3() {
      System.out.println("tell3");
      }
      }

      public class PolDemo02 {

      public static void main(String[] args) {
      say(new B1());
      say(new C1());

      }
      public static void say(A1 a) {
      a.tell1();
      }

      }

      //运行结果:
      //tell1
      //tell1
  7. 抽象类与接口

    1. final关键字的使用

      1. final关键字在Java中被称为完结器,表示最终的意思。
      2. final能声明类、方法、属性
        • 使用final声明的类不能被继承
        • 使用final声明的方法不能被重写
        • 使用final声明的变量变成常量,常量是不可以被修改的。被修饰的变量名称应全部大写。
    2. 抽象类

      1. 抽象类概念:包含一个抽象方法的类就是抽象类。

      2. 抽象方法:声明而未被实现的方法,抽象方法必须使用abstract关键字声明。

      3. 抽象类被子类继承,子类(如果不是抽象类)必须重写抽象类中的所有抽象方法。

      4. 定义格式:

        1
        2
        3
        4
        5
        6
        7
        8
        //抽象类
        abstract class className {
        属性
        方法
        //抽象方法,用abstract修饰,在“()”后直接加上“;”
        //eg:
        //public abstract void say();
        }
      5. 抽象类不能直接实例化,要通过其子类进行实例化。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        //抽象类
        abstract class Abs {
        private int age;
        public void say() {

        }
        //抽象方法
        public abstract void print();
        public abstract void tell();
        }

        class AbsSon extends Abs {
        //重写抽象类中的所有抽象方法
        public void print() {

        }
        public void tell() {

        }
        }
        public class AbsDemo {

        public static void main(String[] args) {
        // 抽象类不能直接被实例化,要通过其子类进行实例化
        // Abs abs = new Abs();
        AbsSon abs = new AbsSon();
        abs.say();
        abs.print();
        abs.tell();

        }

        }

    3. 接口的实现

      1. 接口是Java中最重要的概念,可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。

      2. 接口的格式:

        1
        2
        3
        4
        interface interfaceName {
        全局常量
        公共抽象方法
        }
      3. 接口的实现也必须通过子类,使用关键字implements,而且接口是可以多实现的。

      4. 接口被子类继承,子类(如果不是抽象类)必须重写接口中的所有抽象方法。

      5. 一个子类可以同时继承抽象类和实现接口

      6. 一个接口不能继承一个抽象类,但是却可以通过extends关键字同时继承多个接口,实现接口的多继承。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      //接口
      interface Inter1 {
      public static final int AGE = 20;
      public abstract void tell() ;
      }
      interface Inter2 {
      public abstract void say();
      }
      abstract class AbsDemo01 {
      public abstract void print();
      }
      class A extends Abs implements Inter1,Inter2 {
      //重写所继承的所有接口中的所有抽象方法
      public void tell() {

      }
      public void say() {

      }
      //重写所继承的所有抽象类中的所有抽象方法
      public void print() {

      }
      }
      //通过extends关键字同时继承多个接口
      interface Inter3 extends Inter1,Inter2{

      }
      public class InterDemo01 {

      public static void main(String[] args) {
      A a = new A();
      a.tell();
      a.say();
      System.out.println(A.AGE);
      a.print();
      }

      }
    4. 抽象类和接口的对比

      参数 抽象类 接口
      默认的方法实现 它可以有默认的方法实现 接口完全是抽象的。它根本不存在方法的实现
      实现 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
      构造器 抽象类可以有构造器 接口不能有构造器
      与正常Java类的区别 除了不能实例化抽象类之外,它和普通Java类没有任何区别 接口是完全不同的类型
      访问修饰符 抽象方法可以有publicprotecteddefault这些修饰符 接口方法默认修饰符是public。你不可以使用其它修饰符。
      main方法 抽象方法可以有main方法并且我们可以运行它 接口没有main方法,因此我们不能运行它。
      多继承 抽象方法可以继承一个类和实现多个接口 接口只可以继承一个或多个其它接口
      速度 它比接口速度要快 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
      添加新方法 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 如果你往接口中添加方法,那么你必须改变实现该接口的类。
    5. 什么时候使用抽象类和接口:

    • 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
    • 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
    • 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

5.2 Object所有类的超类

  1. Object类是Java中所有类的始祖,在Java中每个类都是由它扩展而来。

    • Object类型的变量可以引用任何类型的对象,但若要对其具体内容进行具体操作,还是需要知道对象的原始类型,并进行相应的转换。
    • 在Java中,只有基本类型不是对象,如数值、字符、布尔值等。所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类。
  2. equals方法

    Object类中的equals方法用于检测一个对象是否等于另一个对象,本质上是比较两个对象是否具有相同的引用。

  3. hashCode方法

    散列码(hash code)是由对象导出的一个整型值。散列码是没有规律的。不同的对象,散列码基本不同。

    1
    2
    3
    4
    String s = "OK";
    System.out.println(s.hashCode());
    //运行结果:
    //java.io.PrintStream@15db9742
  4. toString方法

    • toString方法用于返回表示对象值的字符串。(个人感觉还不如用空字符串连接其它数据,如int a = 12; String s = ""+a;

5.3 泛型数组列表

  1. ArrayList是一个采用类型参数的泛型类。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面,

    例如ArrayList<Father> test = new ArrayList<Father>();

    注意:从Java SE7开始,可以省去右边的参数类型,即

    ArrayList<Father> test = new ArrayList<>();

  2. ArrayList类用起来与数组类似,但在添加或删除元素时,具有自动调节数组容量的功能。也可设置初始容量。

    • add(),添加元素到数组列表
    • get(),从数组列表取元素
    • set(),替换数组列表相应位置已有的元素
    • remove(),删除元素
    • size(),返回当前数组列表中包含的实际元素数目。
    • trimToSize(),将存储区域的大小调整为当前元素数量所需存储空间数目,多余的内存将会被gc回收。
  3. 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public class ArrList {

    public static void main(String[] args) {
    // 创建ArrayList实例,并设置初始容量100
    ArrayList<Person> human = new ArrayList<>(100);
    human.add((new Person("张三",34)));
    Person p = human.get(0);
    System.out.println("姓名:"+p.name);
    System.out.println("年龄:"+p.age);
    System.out.println("此时的ArrayList的实际元素数目为"+human.size());
    human.remove(human.size()-1);
    System.out.println("删除成功!");
    System.out.println("此时的ArrayList的实际元素数目为"+human.size());

    }

    }
    class Person {
    String name;
    int age;
    public Person(String name,int age) {
    this.name = name;
    this.age = age;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public int getAge() {
    return age;
    }
    public void setAge(int age) {
    this.age = age;
    }

    }

5.4 对象包装器与自动装箱

  1. 所有的基本类型都有一个与之对应的类。如,Integer类对应基本类型int。Integer、Long、Float、Double、Short、Byte、Character、Boolean、Void均称之为对象包装器类。

    ArrayList<Integer> list = new ArrayList<>();

  2. 程序调用list.add(3);将会自动变成list.add(Integer.valueOf(3));,这种变换称为“自动装箱”。

  3. 编译器将list.add(Integer.valueOf(3));翻译成list.add(3);,这种变换称为“自动拆箱”。

  4. 简单一句话:装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

5.5 参数数量可变的方法

  1. printf方法就是参数可变的方法。
  2. 个人觉得目前用不上,很鸡肋。

5.6 枚举类

枚举类是类!

1
public enum Size {SMALL,MEDIUM,LARGE,EXTRA_LARGE};

5.7 反射

  1. 反射库提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。
  2. 能够分析类能力的程序称为反射。
  3. 反射机制极其强大:
    • 在运行时分析类的能力。
    • 在运行时查看对象。
    • 实现通用的数组操纵代码。
    • 利用Method对象。

5.8 继承的设计技巧

  1. 将公共操作和域放在超类
  2. 不要使用受保护的域
  3. 使用继承实现“is-a”关系
  4. 除非所有继承的方法都有意义,否则不要使用继承
  5. 在覆盖方法时,不要改变预期的行为
  6. 使用多态,而非类型信息
  7. 不要过多使用反射