多态的概念
Java中当父类的引用指向子类的对象时就称为多态。
多态的作用:允许不同的类对同一消息做出响应,即同样的请求可以根据请求对象的不同来采取不同的方法来实现,这就是多态。
多态的前提条件
实现多态的三个必要条件:
- 必须存在继承关系。
- 子类需要重写父类的方法。
- 父类的引用指向子类的对象。(该方法称为向上转型。)
多态的优点
- 提高了代码的复用性(来源于继承)。
- 提高了代码的扩展性。
- 使代码使用更加灵活,简化了代码编写的过程。
多态中成员访问特点
多态中父类引用指向子类的对象,在通过对象调用时有以下特点:
- 成员变量访问的是父类的成员变量,无法直接调用子类中的成员变量,不要搞混成员变量没有重写这一说。谁的变量就是谁的,子类中定义了和父类名称一样的成员变量此时并没有覆盖父类的成员变量,而是共存的情况。如果想通过对象调用子类的成员变量请直接创建子类的对象。子类中的方法调用成员变量时如果子类中定义了则访问子类的,子类中没有定义则访问父类的。
- 成员方法调用中,只能访问父类中有的成员方法,如父类中的方法被子类重写则访问子类中的方法,不能访问子类特有的成员方法。
- 静态的方法可以使用对象调用,调用时只能访问父类中的静态方法,但是静态方法是相对于类的关系,调用静态方法时请用类名.方法名的方式去调用,不要使用对象来调用,静态方法不存在重写的说法。
- 构造方法是对对象的初始化,不可以继承。
- 多态不能访问子类中特有的成员方法,但是也可以通过强制转化的方式让父类的引用转变成子类的引用。从而访问子类特有的成员方法,这种方法叫做向下转型,但是此时一定要注意转换关系,必须为父类引用指向子类对象的前提下才能使用,如果没搞清楚转化关系将两个不相关的类之间进行转化就会造成运行期间错误。所以实际开发不建议使用向上转型,需要使用子类特有的方法请创建子类对象使用。
使用方法举例:
class Animal {
int a = 10;
public Animal() {
super();
}
public void eat() {
System.out.println("动物吃东西");
}
public void run() {
System.out.println("动物在地上跑");
}
}
class Cat extends Animal {
int a = 20;
public Cat() {
super();
}
public void eat() {
System.out.println("猫吃鱼");
}
public void sleep() {
System.out.println("猫总是在睡觉");
}
}
class Dog extends Animal {
public Dog() {
super();
}
public void eat() {
System.out.println("狗吃骨头");
}
public void sleep() {
System.out.println("狗总是不好好睡觉");
}
}
public class Text {
public static void main(String[] args) {
Animal c = new Cat(); // 父类引用指向子类对象
System.out.println(c.a); // 这里访问的是父类的成员变量
System.out.println(new Cat().a); // 可以通过子类的对象来访问子类成员变量
c.eat(); // 子类中如果重写了方法则访问子类的
c.run(); // 子类中没有重写则访问父类的//c.sleep(); 不能访问子类特有的成员方法
Cat d = (Cat) c; // 强制向下转型
d.sleep(); // 此时可以访问子类特有的方法
// ClassCastException异常演示
c = new Dog(); // 将Cat变成Dog
c.eat(); // 此时是Dog类重写了父类访问的是Dog类的方法
Dog e = (Dog) c; // 强制向下转型
Cat f = (Cat) c; // 类型转化错误,此时c是Dog,不能转化为不相关的Cat类属于运行期间错误,可以通过编译,所以实际开发中
// 不建议使用向下转型,需要使用子类特有方法时请创建子类对象或者使用子类匿名对象
}
}
/**
* * 输出为:1020猫吃鱼动物在地上跑猫总是在睡觉狗吃骨头 Exception in thread "main"
* java.lang.ClassCastException: ExtendsText.Dog cannot be cast to
* ExtendsText.Catat ExtendsText.Text.main(Text.java:55)
*/
例题分析
class A {
public void show() {
show2();
}
public void show2() {
System.out.println("我");
}
}
class B extends A {
public void show2() {
System.out.println("爱");
}
}
class C extends B {
public void show() {
super.show();
}
public void show2() {
System.out.println("你");
}
}
public class DuoTaiTest {
public static void main(String[] args) {
A a = new B();
a.show();
B b = new C();
b.show();
}
}
这道题中的关键是因为B类是继承了A类的所以会继承A类所有的成员方法所以实际的B类是这样的
class B extends A {
public void show() {
show2();
}
public void show2() {
System.out.println("爱");
}
}
此时看主方法,A类引用指向B类对象然后执行了show方法,此时因为子类没有重写show是直接继承下来的,所以还是执行的是父类中的方法,执行了
show2();
然后转到本类的show2方法中执行了
System.out.println("爱");
所以输出一个爱字
然后再看主方法B类引用指向了C类对象然后执行了show方法,因为C类中对show方法重写了所以执行的是子类的方法,此时执行
super.show();
super表示访问父类中的show方法,父类中没有重写show方法所以执行的是继承自A类的show方法运行如下语句
show2();
然后跳转到本类的show2方法处执行
System.out.println("你");
所以再输出一个你字
最终的结果为
爱
你
这道题需要注意两点:
- 继承中会继承父类所有的成员变量和方法,当子类中没有重写时执行的就是父类中的方法。
- 获取到执行show2()方法的语句后是回到本类中执行show2(),而不是在父类中执行。