多态
- 一个父类的引用类型变量它既可以指向父类对象也可以指向子类对象,它可以根据当前时刻指向的不同,自动调用不同对象的方法,这就是多态
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}
public class M
{
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
aa.f();
bb.f();
aa = bb;//OK 把bb当作aa来看待,即B转换成A
bb = aa; //error 把aa当作bb来看待,即A转换成B
/** aa代表的是父类,是一般的,可以比喻成动物
bb代表的是子类,是具体的,可以比喻成狗
故bb可以看作动物来对待,因为狗是动物的一个实例
但是动物不可以当作狗来对待,因为还有其他动物 */
}
}
程序运行示例:
——————————————————————————————————
M.java:31: 错误: 不兼容的类型: A无法转换为B
bb = aa;
^
1 个错误
——————————————————————————————————
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}
public class M
{
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
aa.f(); //***
bb.f();
aa = bb;
aa.f(); //***
//这就是多态! 同样的代码做不同的事情
}
}
程序运行示例:
——————————————————————————————————
AAAA
BBBB
BBBB
——————————————————————————————————
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}
public class M
{
public static void main(String[] args)
{
A aa = new B();
aa.f(); //***
aa = new A();
aa.f(); //***
//这就是多态! 同样的代码做不同的事情
}
}
程序运行示例:
——————————————————————————————————
BBBB
AAAA
——————————————————————————————————
- aa可以根据它自己当前时刻指向的是A类对象还是A子类对象,而自动决定调用哪个对象的f方法,这就是多态
多态的优点
- 利用多态可以实现:
- 同一段代码做不同的事情
- 如:
- 假设A派生出B,B派生出C
- 试着编写一个函数实现调用整个A类族所有对象f方法
- 所谓A类族就是A类及其A的子孙类所形成的一个族群
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}
class C extends B
{
public void f()
{
System.out.printf("CCCC\n");
}
}
public class M
{
public static void g(A aa)
{
aa.f();
}
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
C cc = new C();
g(aa);
g(bb);
g(cc);
//这就是多态! 同样的代码做不同的事情
//向后兼容
}
}
程序运行示例:
——————————————————————————————————
AAAA
BBBB
CCCC
——————————————————————————————————
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
}
public class M
{
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
// aa = bb;
aa = (A)bb; //单独此行代码不会影响bb的本身类型,这条语句本身是错误的
//aa.f();
//bb.f();
}
}
程序运行示例:
——————————————————————————————————
Exception in thread "main" java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
at M.main(M.java:28)
——————————————————————————————————
程序运行示例:(三条注释语句取消后,且删除27行)
——————————————————————————————————
BBBB
——————————————————————————————————
class A
{
public void f()
{
System.out.printf("AAAA\n");
}
}
class B extends A
{
public void f()
{
System.out.printf("BBBB\n");
}
public void g()
{
System.out.printf("GGGG\n");
}
}
public class M
{
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
aa = bb;
aa.f(); //OK 此时父类aa在调用子类的重写函数
aa.g(); //error 此时父类aa在调用子类的特有函数 //*********
}
}
程序运行示例:
——————————————————————————————————
M.java:34: 错误: 找不到符号
aa.g(); //error 此时父类aa在调用子类的特有函数
^
符号: 方法 g()
位置: 类型为A的变量 aa
1 个错误
——————————————————————————————————
多态注意事项(难点)
- 子类对象可以直接赋给父类对象使用,但父类对象在任何情况下都不可以直接赋给子类引用,因为子类是父类的一种,但父类不是子类的一种,或者讲子类可以当做父类看待,但父类不可以当做子类看待,“狗可以当做动物看待,但动物不可以当做狗来看待”
通过父类引用只能访问子类对象从父类继承过来的成员
- 通过父类引用不能访问子类对象所特有的成员
- 父类引用永远不可能直接赋给子类引用
- 只有在父类引用本身指向的就是一个子类的对象时,才可以把父类引用强制转换成子类引用
- 其他情况下不允许把父类引用强制转化为子类引用,否则运行时会出错
class A
{
}
class B extends A
{
}
public class M
{
public static void main(String[] args)
{
A aa = new A();
B bb = new B();
bb = (B)aa; //16行 编译没有错误,但运行时出错! 因为aa指向的是父类的对象
A aa2 = new B();
bb = (B)aa2; //OK 因为aa2本身指向的就是一个B类对象,
// 所以可以进行强制转化,注意与16行的区别,在Java中绝不可能直接把父类引用赋给子类引用的
}
}