多态
一个父类可以有多个子类,而在子类里可以重写父类的方法,这样每个子类里重写的代码不一样,自然表现形式就不一样。这样用父类的变量去引用不同的子类,在调用这个相同的方法的时候得到的结果和表现形式就不一样了,这就是多态,相同的消息(也就是调用相同的方法)会有不同的结果。
多态通过动态绑定(dynamic binding)技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
子类是对父类的改进和扩充。正是因为父类和不同子类之间有共性,所以才由父类的引用指向不同的子类。Father既可以指向Son1又可以指向Son2就是因为他们有相同的共性
1.向上转型
向上转型是父类引用指向子类对象。通俗地说就是是将子类对象转为父类对象。
父类引用指向子类对象指的是:例如父类Animal,子类Cat,Dog。其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类。
Animal animal = new Cat();
即声明的是父类,实际指向的是子类的一个对象。
2.向下转型
java中“向上转型”是自动的。但是“向下转型”却不是自动的。需要我们用强制类型转化。
Animal c = new Cat();
Cat c1 = (Cat)c; //不允许“向下转型”,需用强制类型转化
3.多态的意义
多态方式声明是一种好的习惯。当我们创建的类,使用时,只用到它的超类或接口定义的方法时,我们可以将其索引声明为它的超类或接口类型。
它的好处是,如果某天我们对这个接口方法的实现方式变了,对这个接口又有一个新的实现类,我们的程序也需要使用最新的实现方式,此时只要将对象实现修改一下,索引无需变化。
比如Map<String,String> map = new HashMap <String,String>();
想换成HashTable实现,可以Map<String,String> map = new HashTable <String,String>();
这种小范围的使用可能体现的用处不明显。
当我们使用Spring,或者设计数据库连接使用时,可能会明显些。
我们可能会随着业务发展,数据库的实现从mySql改为Oracle等。如果我们设计的时候,将数据库驱动的索引直接定义为mySql特定的驱动时,切换Oracle就要修改源码了。如果定义为接口的方式,如Dirver等,此时的切换,只要修改配置文件数据库驱动即可,源码中对数据库的各种操作都无需修改。
4.经典案例
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{
}
class D extends B{
}
public class Demo {
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("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D
注意:
1.父类中的方法只有在子类中没被重载,才可以被父类类型的引用调用;
2.对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法
3.子类会继承父类的所有函数,有些没有明确写出来但是还是存在于子类中
4.父类不能调用父类中没有声明而子类新声明的方法
补充知识点:
1.当父类对象的引用指向子类对象时,声明对象的类型决定了可调用的成员方法的范围(范围是父类已声明的方法),新建对象的类型决定具体执行哪一个实现。
class X {
public void show(Y y){
System.out.println("x and y");
}
public void show(){
System.out.println("only x");
}
}
class Y extends X {
public void show(Y y){
System.out.println("y and y");
}
public void show(int i){
}
}
class Z extends X {
public void show(Y y){
System.out.println("z and z");
}
public void show(int i){
}
}
class main{
public static void main(String[] args) {
X x = new Y();
x.show(new Y());
x=new Z();
x.show(new Y());
x.show();
}
}
//结果
//y and y
//z and z
//only x
X x=new Y();声明类型是X表示可以调用show(Y y)和show()方法,新建类型是Y说明要调用Y中的show(Y y)和show()
2.继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
abcd的关系是这样的:C/D —> B —> A
我们先来分析4 : a2.show(b)
- 首先,a2是类型为A的引用类型,它指向类型为B的对象。A确定可调用的方法:
show(D obj)和show(A obj)。
a2.show(b)
==>this.show(b)
,这里this指的是B。- 然后.在B类中找
show(B obj)
,找到了,可惜没用,因为show(B obj)
方法不在可调用范围内,this.show(O)
失败,进入下一级别:super.show(O)
,super指的是A。- 在A 中寻找
show(B obj)
,失败,因为没用定义这个方法。进入第三级别:this.show((super)O)
,this指的是B。- 在B中找
show((A)O)
,找到了:show(A obj)
,选择调用该方法。 输出:B and A
我们再来看一下9:b.show(d)
- 首先,b为类型为B的引用对象,指向类型为B的对象。没有涉及向上转型,只会调用本类中的方法。
- 在B中寻找
show(D obj)
,方法。现在你不会说没找到了吧?找到了,直接调用该方法。 输出 A and D。