0%

面向对象编程(中级部分)-4

多态

★★★多态

  • 方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。
方法的多态
  • 方法的重载和重写就体现出多态(虽然方法名相同,但根据具体的情况却可以对应名称相同但具体内容不同的方法,体现出多态)。
★对象的多态
  • 一个对象的编译类型和运行类型可以不一致;

    编译类型在定义对象时,就确定了,不能改变;而运行类型是可以变化的;

    编译类型看定义时 “=” 的左边,运行类型看定义时 “=” 的右边。

    1
    2
    //Animal 是 Dog 的父类,Animal 是编译类型,Dog 是运行类型
    Animal animal = new Dog();
  • 多态的前提:两个类存在继承关系

★向上转型
  • 本质:父类的引用指向了子类的对象

    语法父类类型 引用名 = new 子类类型();

    特点:编译类型看左边,运行类型看右边;可以调用父类中的所有成员(须遵守访问权限),但不能调用子类中的特有成员,因为在编译阶段,能调用哪些属性和方法是由编译类型来决定(例如子类 Dog 有一个方法 public void bark() 而父类 Animal 没有,则 Animal animal = new Dog();animal 是不能调用 bark() 的);当调用方法时,是看运行类型的,即会先从子类开始查找,遵循 “继承本质详解” 中提到的查找方法的规律(例如 animal.cry() 时,会先从 Dog 类中调用 cry() 函数,若没有,再从 Animal 类中调用 cry() 函数)。

  • 注意:属性没有重写之说,属性的值看编译类型,这和方法调用时看运行类型是有区别的。

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-14.jpg)

    只需记住只有属性特别一点,是看编译类型的,其他的,因为对象本身就是由运行类型来创建的,所以当然先看运行类型(比如 instanceof 运算符)。

向下转型
  • 语法子类类型 引用名 = (子类类型)父类引用;

    1
    2
    3
    //要求此时 animal 是指向的 Dog 类的对象
    Dog dog = (Dog)animal;
    //此时就可以使用 dog.bark() 了。也可以 ((Dog)animal).bark()

    特点:只能强转父类的引用,不能强转父类的对象;要求父类的引用必须指向的是当前目标类型的对象;当向下转型后,就可以调用子类类型中的所有成员(须遵循访问权限)。

★★★★★动态绑定机制
  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
    package com.f;

    public class DynamicBinding {
    public static void main(String[] args) {
    Base base = new Sub();
    System.out.println(base.sum()); //21
    System.out.println(base.sum1()); //2
    }
    }

    class Base {
    public int i = 1;

    public int sum() {
    //由于动态绑定机制,所以调用的是 Sub 类的 getI 函数,应该为 20 + 1 = 21
    return getI() + 1;
    //return this.getI() + 10; 和上面是一样的
    }

    public int sum1() {
    //属性是没有动态绑定机制的,所以应该为 1 + 1 = 2
    return i + 1;
    }

    public int getI() {
    return i;
    }
    }

    class Sub extends Base {
    public int i = 20;

    //public int sum() {
    // return i + 20;
    //}

    //public int sum1() {
    // return i + 10;
    //}

    public int getI() {
    return i;
    }
    }
多态数组
  • 定义:数组定义类型为父类类型,里面保存的实际元素类型为子类类型

    1
    2
    3
    4
    5
    6
    7
    //Animal 是父类,Dog 和 Cat 是子类
    Animal[] animals = new Animal[5];
    animals[0] = new Animal();
    animals[1] = new Dog();
    animals[2] = new Dog();
    animals[3] = new Cat();
    animals[4] = new Cat();
  • 如果想让 animals[1]animals[2] 调用其指向的对象的运行类型(Dog)的成员方法 bark() ,可以用如下方法:

    1
    2
    3
    4
    5
    for (int i = 0; i < animals.length; i++) {
    if(animals[i] instanceof Dog){ //类型判断
    ((Dog)animals[i]).bark(); //向下转型然后调用
    }
    }
多态参数
  • 定义:方法定义的形参类型为父类类型实参类型允许为子类类型

Object类详解

  • 下面是 Object 类的方法:

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-15.jpg)

★==和equals

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-16.jpg)

  • == 和 equals 的对比:

    • == 是一个比较运算符,既可以判断基本类型,又可以判断引用类型;

      如果判断基本类型,则判断的是值是否相等10 == 10.0 结果为 true);

      如果判断引用类型,则判断的是地址是否相等,即判断是不是指向同一个对象

    • equalsObject 类中的方法,只能判断引用类型;默认判断的是地址是否相等子类中往往重写该方法(比如 StringInteger),用于判断内容是否相等。

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-21.jpg)

hashCode

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-17.jpg)

  1. 可以提高具有哈希结构的容器的效率;
  2. 对于两个引用,如果指向的是同一个对象,则其哈希值肯定是一样的;反之其哈希值肯定是不一样的;
  3. 哈希值主要是根据地址号来的,但不能完全将哈希值等价于地址
★toString

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-18.jpg)

1
2
3
4
// Object 类的 toString 方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 默认返回:全类名(包名加类名) + @ + 哈希值的十六进制

    子类往往重写 toString 方法,用于返回对象的属性信息(可以在 IDEA 中通过 Alt+insert 快捷键快速重写 toString 方法)。

  • 注意:当直接输出一个对象时,toString 方法会被默认调用

finalize

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-19.jpg)

  1. 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作
  2. 什么时候被回收:当某个对象没有任何引用时,jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法
  3. 垃圾回收机制的调用,是由系统来决定的(jvm 的 GC 算法),也可以通过 System.gc() 主动触发垃圾回收机制。

★断点调试(debug)

  • 断点调试是指在程序的某一行设置一个断点(breakpoint),调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误并停下,进而程序员可以找到这个 Bug。

    断点调试也能帮助我们查看 java 底层源代码的执行过程,从而提高程序员的 java 水平。

  • 注意:在断点调试的过程中,是运行状态,是以对象的运行类型来执行的。

![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-notebook/img/Java/C8-20.jpg)

断点调试快捷键
  1. F7:跳入。跳入方法内;
  2. F8:跳过。不跳入方法内,逐行执行代码
  3. shift+F8:跳出。跳出方法;
  4. F9:resume,执行到下一个断点处。
---------------The End---------------