Lesson6 多态

1 多态

  • 多态:是面向对象程序设计的另一个重要特征,其基本含义是“拥有多种形态”,具体指在程序中用相同的名称来表示不同的含义。例如:用同一方法名来表示不同的操作

  • 静多态/静态联编/静绑定:在编译时决定调用哪个方法

    • :包括隐藏、方法的重载,一般指方法重载、方法隐藏
    • 只要构成了方法重载、方法隐藏,就可以认为形成了静态多态的条件;因此,静态多态与是否发生继承没有必然联系
  • 方法重载(Overloading)

    • 方法名相同,参数个数参数类型参数顺序至少有一个不同
    • 返回值类型与访问权限修饰符可以相同也可以不同,上述两项不能当做判断是否重载的条件。
  • 动多态/动态联编/动绑定:在运行时才能确定调用哪个方法

    • 3个条件
      • 继承
      • 覆盖:继承中必须要有方法覆盖
      • 向上转型:必须由父类的引用指向派生类的实例,并且通过父类的引用调用被覆盖的方法
  • 方法覆盖(Override)

    • 方法名、参数个数、参数类型及参数顺序必须一致
    • 若父类方法定义时有异常抛出,则子类覆盖父类该方法时时,该方法也不能有更多的异常抛出,否则编译时会产生错误
    • 子类方法不能缩小父类方法的访问权限
    • 私有方法、静态方法不能被覆盖,如果在子类出现了同签名的方法,那是方法隐藏;
  • 抽象类和抽象方法

  • 抽象类

    • 如果一个类继承自某个抽象父类,而没有具体实现抽象父类中的抽象方法,则必须定义为抽象类
    • 抽象类引用:虽然不能实例化抽象类,但可以创建它的引用。因为Java支持多态性,允许通过父类引用来引用子类的对象。
    • 如果一个类里有抽象的方法,则这个类就必须声明成抽象的。但一个抽象类中却可以没有抽象方法
    • 抽象方法不能被private、final或static修饰。

2 接口

2.1 定义

  • 接口:不相关类的功能继承。
    • 特殊的抽象类
    • 只包含常量(所有变量默认public static final)和方法(默认 public abstract)的定义,没有方法的实现。(但一般不包含变量)
    • 没有构造方法
    • 一个类可以实现多个接口;如果类没有实现接口的全部方法。需要被定义成 abstract
    • 接口的方法体还可以由其他语言写,此时接口方法需要用 native 修饰
  • 关键词 interface implements
[public] [interface] 接口名称 [extends 父接口名列表]{
	// 静态常量
	[public][static][final]数据类型 变量名=常量名;
	//抽象方法
	[public][abstract][native]返回值类型 方法名(参数列表);
}

[修饰符] class类名 [extends父类名] [implements接口A,接口B,]{
	类的成员变量和成员方法;
	为接口A中的所有方法编写方法体,实现接口A;
	为接口B中的所有方法编写方法体,实现接口B;
}
public interface Flyer{
	public void takeOff();
	public void land();
	public void fly();
}

public class Bird extends Animal implements Flyer {
	public void takeOff() { /* take- off implementation */ }
	public void land() { /* landing implementation */ }
	public void fly() { /* fly implementation */ }
	public void buildNest() { /* nest building behavior */ }
	public void layEggs() { /* egg laying behavior */ }
	public void eat() { /* override eating behavior */ }
}

2.2 使用接口

  • 接口用作类型
    • 声明格式:接口 变量名 (又称为引用)
    • 接口做参数:如果一个方法的参数是接口类型,就可以将任何实现该接口的类的实例的引用传递给接口参数,那么接口参数就可以回调类实现的接口方法。
  • 接口回调
    • 把实现某一接口的类创建的对象引用赋给该接口声明的接口变量
    • 该接口变量就可以调用被类实现的接口中的方法。
    • 即:
      接口变量 = 实现该接口的类所创建的对象;
      接口变量.接口方法([参数列表])
interface Runner {
	//接口1
	public void run();
}

interface Swimmer {
	//接口2
	public void swim();
}

abstract class Animal {
	//抽象类,去掉关键字abstract是否可行?
	public abstract void eat();
}

class Person extends Animal implements Runner,Swimmer { //继承类,实现接口
	public void run() {
		System.out.println("我是飞毛腿,跑步速度极快!");
	}
	public void swim(){
		System.out.println("我游泳技术很好,会蛙泳、自由泳、仰泳、蝶泳...");
	}
	public void eat(){
		System.out.println("我牙好胃好,吃啥都香!");
	}
}

public class InterfaceTest{
	public void m1(Runner r) { r.run(); } //接口作参数
	public void m2(Swimmer s) {s.swim();} //接口作参数
	public void m3(Animal a) {a.eat();} //抽象类引用
	public static void main(String args[]){
		InterfaceTest t = new InterfaceTest();
		Person p = new Person();
		t.m1(p); //接口回调
		t.m2(p); //接口回调
		t.m3(p); //接口回调
	}
}

2.3 接口的继承

  • 接口可以继承,而且可以多重继承

2.4 抽象类与接口

  • 共同点
    • 在语义上,都位于系统的抽象层,需要其他类来进一步提供实现细节。
    • 抽象类与接口都是为了继承与多态,它们都需要子类来继承或实现才有意义,最终目的是为了多态;子类重写了父类的方法,再通过向上转型,由父类对象引用指向子类对象,达到运行时动态调用子类方法的目的。
  • 区别
    • 接口中的成员变量和方法只能是 public 类型的,而抽象类中的成员变量和方法可以处于各种访问级别。
    • 接口中的成员变量只能是 publicstaticfinal 类型的,而在抽象类中可以定义各种类型的实例变量和静态变量。
    • 接口中没有构造方法,抽象类中有构造方法。接口中所有方法都是抽象方法,抽象类中可以有,也可以没有抽象方法。抽象类比接口包含了更多的实现细节。
    • 抽象类是某一类事物的一种抽象,而接口不是类,它只定义了某些行为
      例如,“生物”类虽然抽象,但有“狗”类的雏形,接口中的run方法可以由狗类实现,也可以由汽车实现。
    • 在语义上,接口表示更高层次的抽象,声明系统对外提供的服务。而抽象类则是各种具体类型的抽象。

2.5 Native关键字

  • Native用来声明一个方法是由机器相关的语言(如C/C++语言)实现的。通常,native方法用于一些比较消耗资源的方法,该方法用c或其他语言编写,可以提高速度。
  • native 定义符说明该方法是一个使用本地其他语言编写的非java类库的方法,它是调用的本地(也就是当前操作系统的方法或动态连接库)。最常见的就是c/c++封装的DLL里面的方法,这是java的 JNI技术。它在类中的声明和抽象方法一样没有方法体。

3 upcasting 和 downcasting

3.1 向上转型 upcasting

  • 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。

  • 使用格式:
    父类类型 变量名 = new 子类类型();
    如:Person p = new Student();

  • 上转型对象的使用

    • 上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法。
    • 如果子类重写了父类的某个实例方法后,当用上转型对象调用这个实例方法时一定是调用了子类重写的实例方法。
    • 上转型对象不能操作子类新增的成员变量;不能调用子类新增的方法。

3.2 向下转型 downcasting

  • 向下转型(映射):一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。
  • 使用格式:
    子类类型 变量名 = (子类类型) 父类类型的变量;
    如:Person p = new Student();
    Student stu = (Student) p
  • 如果是直接创建父类对象,是无法向下转型的 ,能过编译,但运行时会产生异常
    如:Person p = new Peron();
    Student stu = (Student) p

instanceof 操作符

  • instanceof 操作符用于判断一个引用类型所引用的对象是否是一个类的实例instanceof 运算符是Java独有的双目运算符
  • instanceof 操作符左边的操作元是一个引用类型的对象(可以是null),右边的操作元是一个类名或接口名。
  • 形式如下:obj instanceof ClassName 或者 obj instanceof InterfaceName
  • a instanceof X,当 X 是 A类/A类的直接或间接父类/A类实现的接口时,表达式的值为true
Built with MDFriday ❤️