什么是多态?
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
多态是继封装、继承之后,面向对象的第三大特性。
继承可以从另一个类继承属性和方法,而多态可以让这些方法执行不同的任务。
多态体现为:父类引用变量可以指向子类对象。(前提条件:必须有父子类关系)
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
多态的好处:
可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
多态的定义与使用格式:
定义格式:父类类型 变量名=new 子类类型();
多态性(Polymorphism): 向上转型
多态性是对象多种表现形式的体现。
FinalExam finalExam = new FinalExam();
GradedActivity exam = finalExam; 向上转型
在Java中, 引用变量可以是多态的, 即它可以存放不同类对象的地址,只要这些类是它声明的类的派生类即可。
当一个引用变量含有多态性时,Java执行的是动态绑定或者迟绑定。
代码示例1:
package multistate;
public class Human {
public void sleep() {
System.out.println("Human sleep..");
}
public static void main(String[] args) {
Male m = new Male();
m.sleep();
Human h = new Male(); //向上转型
h.sleep();
}
}
class Male extends Human {
@Override
public void sleep() {
System.out.println("Male sleep..");
}
public void speak() {
System.out.println("I am Male");
}
}
class Female extends Human {
@Override
public void sleep() {
System.out.println("Female sleep..");
}
public void speak() {
System.out.println("I am Female");
}
}
注意:1.父类引用指向的或者调用的方法是子类的方法,这个叫动态绑定
2.向上转型后父类引用不能调用子类自己的方法
3.不能把基类对象赋值给派生类的引用变量
多态:基类型对象访问派生类重写的方法
多态的三个必要条件:
1.继承:在多态中必须存在有继承关系的子类和父类。
继承只能单继承,不能同时继承多个父类,但是可以有多个接口。
2.重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
3.向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
重写:
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。
重写的好处:子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
方法的重写规则:
1.参数列表与被重写方法的参数列表必须完全相同。
2.返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类
3.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
4.父类的成员方法只能被它的子类重写。
5.声明为 final 的方法不能被重写。
6.声明为 static 的方法不能被重写,但是能够被再次声明。
7.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
8.子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
9.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
10.构造方法不能被重写。
11.如果不能继承一个类,则不能重写该类的方法。
重写和重载的区别:
区别点 | 重载方法 | 重写方法 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
总结:
方法的重写和重载是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
代码实例2:
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.print("吃鱼 ");
}
public void work() {
System.out.print("抓老鼠 ");
}
}
class Dog extends Animal {
public void eat() {
System.out.print("吃骨头 ");
}
public void work() {
System.out.print("看家 ");
}
}
执行以上程序,输出结果为:
吃鱼 抓老鼠 吃骨头 看家 吃鱼 抓老鼠
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
抽象类和抽象方法:
通过abstract关键字将方法修饰为抽象的,此时的方法称为抽象方法。
抽象方法是出现在基类中的一种方法,但要求在派生类中被重写。
•一个抽象方法只有方法头,没有方法主体。
•访问修饰符 abstract 返回类型 方法名(参数列表);
包含抽象方法的类就会自动变成抽象类,因此必须以abstract关键字声明。
public abstract class 类名
•如果派生类没有重写抽象方法,编译器会报错。
•抽象方法被用来确保派生类会实现这个方法。
•不论抽象类是否含抽象方法,其都不允许实例化,即不能创建抽象类的对象,因为其描述的是抽象概念。它只能作为其他类的基类。
•若父类是抽象类,且子类不想成为抽象类,则子类必须将父类中的所有抽象方法重写为带方法体的普通方法,否则子类仍必须是抽象类。
接口源于抽象的设计,首先看看什么是抽象:
抽象方法必须用abstract关键字进行修饰
abstract class text {
public abstract void f();
}
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
有了抽象类,子类就可以继承并且实现抽象方法来实现多态。