多态
多态的概念
定义
多态 polymorphism :对象根据所接收的消息而做出动作,同样的消息为不同的对象接收时可导致完全不同的行动,该现象 称为多态性
静态绑定
编译时多态 指在编译程序时就根据调用方法提供的信息,把它所对应的具体方法确定下来。即 在编译时就把调用方法名与具体方法绑定在一起方法重载
动态绑定
运行时多态 指在编译程序时还不能确定方法调用所对应的具体方法,只有在程序运行过程中才能确定具体的方法。 即 在程序运行时才把调用方法名与具体方法绑定在一起继承多态、接口多态
为何需要多态?
- 在设计一个程序系统时,可以通过在abstract类中声明若干
abstract方法,表明这些方法在整个系统设计中的重要性,方法体的内容细节由它的非abstract子类去完成 - 使用多态进行程序设计的核心技术之一是使用向上转型对象,即 将abstract类声明对象作为其子类的向上转型对象,那么这个向上转型对象就可以调用子类重写的方法
- 当将absctract类声明对象按需实际引用不同的子类对象时,就能达到以一变应万变的目的
- 所谓面向抽象编程,是指当设计某种重要的类时,不让该类面向
具体的类,而是面向抽象类,即所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象
继承与多态
继承基本概念
继承: 以存在的类为基础定义新的类,新类即拥有基类的数据成员和成员方法。
继承的目的: 代码重用(减少代码重复编写 code reuse)
继承的本质: 描述自然界中广泛存在的一种关系——类属关系(is-a)
实现过程:(略)
使用 extends 关键字
方法重写 Override
注意
- 子类重写的方法的权限不能低于父类方法的访问权限
- 子类重写的方法不能抛出比父类方法抛出的异常多
- 被重写的父子类方法须同时为static的,或同时为非static的静态方法按类绑定,实例方法按实际所引用的对象实例绑定
- 父类的private方法不能被重写,本质上它不是为了继承而生
- 重写和重载区别:重载(overload)是形式参数列表丌一样.重写 则是方法头部( 包括形式参数列表)要一致;重写只能出现在继承过程中.
- 对象调用被重写的非静态方法时,使用对象引用实际引用的实 例所对应的版本(即运行时绑定)
继承的构造
父类对象引用子类实例(重点)
Java运行时某个类型的引用变量引用其子类的实例,而且可以对这个引用变量进行类型转换
简单的记: b = d;
继承的多态所需的三个条件:
- 继承 子类继承基类
- 重写 子类重写基类同名同参方法
- 父类对象引用子类实例 b = d
实例一:
// 基础版多态
class Person {
public String toString() {
return "Person";// 重写Object的toString方法
}
}
class Man extends Person {
public String toString() {
return "Man";// 重写父类的toString方法
}
public void show() {
System.out.println("这是子类独有的方法");// 子类新加的方法
}
}
public class Test_extends_poly1 {
public static void main(String[] args) {
/*---编译时多态--- */
Person q = new Person();// b = b
Man m = new Man(); // d = d
System.out.println(q.toString());// 执行自己的方法 Person
System.out.println(m.toString());// 执行自己的方法 Man
/*---运行时多态--- */
Person p = new Man(); // b = d 父类对象引用子类实例
System.out.println(p.toString());// 运行时多态
/*
Java从实例所属的类开始寻找匹配的方法执行
如果当前类没有重写匹配的方法,则沿着继承关系逐级向上寻找
直到Object类为止
*/
p.show(); // 编译Error:**The method show() is undefined for the type Person**
// 编译阶段,p的类型是Person,其中没有show方法,编译不通过
}
}
/*
输出的结果为:
Person
Man
Man
*/
实例二:
// 进阶版多态
class A {
public String show(A obj) {
return ("AA") ;
}
public String show(D obj) {
return ("AD") ;
}
}
class B extends A {
public String show(A obj) {
return ("BA");
}
public String show(B obj) {
return ("BB");
}
}
class C extends B {
}
class D extends B {
}
public class Test_extends_poly2 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b));// 向上转型
System.out.println(a1.show(c));// 向上转型
System.out.println(a1.show(d));// 向上转型
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));
}
}
结果:
AA
AA
AD
BA
BA
AD
BB
BB
AD
规则:
- 运行时绑定:实例方法与引用变量实际引用的对象的方法
- 编译时绑定:静态方法与引用变量所声明的类型的方法
- 编译时绑定:成员变量(包括静态变量和实例变量)与引用变量所声明的类型成员变量
(俗称:编译看左边,运行看右边)
接口与多态
接口基本概念
接口是一种特殊的引用类型,没有构造方法,也不能被实例化
传统接口的接口体仅能包含两样:
- 公开的静态常量: 隐式 public static final 修饰组合,且必须显式初始化
- 公开的抽象方法: 隐式 public abstract 修饰组合
实现过程:(略)
使用 implements 关键字
接口和继承的区别
区别 | |
---|---|
抽象类 | 1.对行为和属性的抽象,是多个类(对象)共同的抽象 2.可视作"“类模板”:使用-个抽象类派生的各子类,是有共性的 3.抽象类的各子类,都是抽象类的对象,继承抽象类的子类和抽象 4.类是一种is-A关系 |
接口 | 1.对行为(动作/方法)的抽象,不是对一类事物的抽象,属协议规范 2.实现接口的类可以是不相关的类,只是它们都具有某种表现不同的行为而已 3.接口是让类拥有或者具备某种功能 |
接口实现多态(重点)
接口和实现类→接口变量引用实例类对象实例 i=o → 调用实现方法
实例:
// 先创建好一个接口
public interface Fly {
public void fly() ;
}
// 创建接口的实现类
class Plane implements Fly {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("飞机用机翼飞行");
}
}
class Bird implements Fly {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("小鸟用翅膀飞行");
}
}
// 进行调试
public class Test_interface_poly1 {
public static void main(String[] args) {
Fly Cfly;
Bird bird = new Bird();
Plane plane = new Plane();
Cfly = bird;// 接口引用实现类对象实例
Cfly.fly();// 调用实现方法
Cfly = plane;// 接口引用实现类对象实例
Cfly.fly();// 调用实现方法
}
}
/*结果:
小鸟用翅膀飞行
飞机用机翼飞行
*/
接口与匿名内部类
匿名类:用在其只用-次并简化代码的地方.
匿名类必须:要么继承一个类,要么实现一个接口.
实例:
// 使用回上述的Fly接口
// 新建一个类用于测试
public class Test_interface_poly2 {
public static void main(String[] args) {
Fly XFly = new Fly() {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("匿名类也飞了");
}
}; // 分号“;”不能缺少
XFly.fly();
}
}
构造过程实例:
public class A {
A(int p) {
System.out.println("带参构造子" + p);
}
A() {
System.out.println("无参构造子");
}
void method() {
System.out.println("fromA");
}
public static void main (String []args) {
new A().method();
A z = new A(){
void method() {
System.out.println("fram 匿名类0");
}
};// 分号不可缺
z.method() ;
A a = new A(1) {};
a.method() ;
A b = new A(2) {
void method() {
System.out.println("from 匿名类2");
};
};// 分号不可少
b.method();
}
}
结果:
无参构造子
fromA
无参构造子
fram 匿名类0
带参构造子1
fromA
带参构造子2
from 匿名类2
在Java创建线程或监听器对象时,经常使用这种匿名类语法
匿名类最后必须写上“;” 因为匿名类是一种语法
资料参考:仲恺农业工程学院——信科院课程组——《面向对象程序设计(Java)》