一、多态
1.多态多态的三个条
public class ObjectTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father fa=new Son();//条件3.父类引用指向子类对像
fa.test();
}
}
class Father{
void test()
{
System.out.println("Father");
}
}
class Son extends Father{//条件1.继承
void test()
{
System.out.println("Son"); //条件2.子类重写父类方法
}
}
输出结果为:Son
2.方法的重写
重写的方法,子类方法的参数列表和返回值,必须与父类方法一致,才是重写。
举个不是重写的例子,更容易理解重写:
A.在Father 和Son类基础上定义A和B,A和B中的printl分别接收Father类和Son类参数
class A{
void printl(Father f){
System.out.println("A Father");
}
}
class B extends A{
void printl(Son son){
System.out.println("B son");
}
}
运行测试的方法:
public static void main(String[] args) {
// TODO Auto-generated method stub
Father fa=new Father();
Son son=new Son();
B b=new B();
b.printl(fa);
b.printl(son);
}
运行结果:
A Father
B son
这个例子中,父类中带参数的方法printl,在子类中,改变了参数,printl参数改变为Father类的子类Son类。
这种情况下,实际上子类B的printl函数并没有重写父类的printl类,而是进行了重载。
所以:重写,子类方法的参数列表和返回值,必须与父类方法一致(包括参数是父类、和子类的关系也不行,而是同一类才可),才是重写
注:子类中方法 的参数是父类方法的参数的子类,这一情况是重载,而非重写,在理解多态的时候有用到。
3.父类引用指向子类对像
我们经常看到的一种情况。父类引用指向子类对象
Father fa=new Son();
同时,还有一种情况也是父类引用指向子类对象。
Father和Son类是继承关系,前面有写,A类仍然如下:
class A{
void printl(Father f){
System.out.println("A Father");
}
}
测试类:
public class ObjectTest {
public static void main(String[] args) {
Son son=new Son();
A a=new A();
a.printl(son);
}
}
其中,A类定义的时候,printl方法的参数是Father类对象,而在测试类中,调用printl方法的时候,传递的是参数Father类的子类Son类的对象,这时候,也是父类引用指向子类对象的情况,即:
形式参数Father f指向实参子类引用Son son---》Father f=son
以上是对多态理解的准备工作
二.多态和重载混搭的情况下,多态的理解(重头戏)
1.多态下的重载
例1. Father和Son仍是父类子类关系。A,B关系也未变,如下:
class A{
void printl(Father f){
System.out.println("A Father");
}
}
class B extends A{
void printl(Son son){
System.out.println("B son");
}
}
测试类:
public class ObjectTest {
public static void main(String[] args) {
Father fa=new Father();
Son son=new Son();
A a=new B();
a.printl(fa);
a.printl(son);
}
}
可以看出,A a=new B();有继承,有父类引用指向子类对象,那么,形没形成多态呢?调用printl方法,分别传递父类Father对象,和子类Son对象,输出是什么呢?结果是:全都输出了“A Father”
A Father
A Father
为什么呢,因为不满足子类重写父类方法这个条件,未形成多态。也就是B类中的printl 没有重写A类中的printl,而是重载
B经过在 第一部分多态当中,关于重写的分析,可以知道,A类和B类,printl不是重写而是重载的条件。
重载的形成,等价于类A和类B的内容如下:
class A{
void printl(Father f){
System.out.println("A Father");
}
}
class B {//注意这里去掉了继承A
void printl(Father f){
System.out.println("A Father");
}
void printl(Son son){
System.out.println("B son");
}
}
也就是此种B去掉了继承,和上边B有继承A是等价的。
A类根本就没什么改变,A的printl接收 参数 fa和接收son参数,都是调用了A中同一个方法。
此种情况对应的是,A和B未形成多态的情况(主要是B中未形成重写而是形成了方法的重载)
第二个例子,如果A和B形成了多态了呢?
A和B改造如下,主要是B:
class A{
void printl(Father f){
System.out.println("A Father");
}
}
class B extends A{//满足了继承和chongxie
void printl(Father f){
System.out.println("B Father");
}
void printl(Son son){
System.out.println("B son");
}
}
测试类不变:
public class ObjectTest {
public static void main(String[] args) {
Father fa=new Father();
Son son=new Son();
A a=new B();
a.printl(fa);
a.printl(son);
}
}
可以看出来,A a=new B();满足了多态的三个条件,形成了多态,这时候,结果如何呢?如下:
B Father
B Father
输出的第二个B father就容易不好理解了
第一种,先看a.printl(fa);这个好理解一些。
完完全全符合多态的变现,父类引用调用子类方法printl(Father f)
而第二种 a.printl(son);输出的结果,有点难以理解,为什么调用的仍然是printl(Father f)呢?
首先应该明确,B类中是哪个方法形成了多态?答案是只有B中的printl(Father f) 形成多态,而B中printl(Son son)和B中重写的printl(Father f)形成了重载,由于A中根本没有此方法,所以,printl(Son son)完完全全是B自己定义的方法,并没有重写A中的方法,所以,B中的printl(Son son)根本就没有形成多态,也就是A类的对象a,根本调用不了,也找不到B类中的这个B自己的方法 printl(Son son)。(只有形成多态,或者子类中只是简单继承,才能调用)
(或者这么理解,假设B中printl(Son son)和B中定义的随便一个别的名字的方法如haha()没有本质区别,都是B自己定义的新方法,对A是不可见的),所以,a.printl(son); 调用的仍然是B中形成多态的printl(Father f)
所以,这里面,需要理解 重写和重载,再加上形成多态的三个条件。
例3.加深一步理解多态:
B类改为如下,都调用一下 形参类里面的方法 test(),其他类不变。
class Father{
void test()
{
System.out.println("Father");
}
}
class Son extends Father{//条件1.继承
void test()
{
System.out.println("Son"); //条件2.子类重写父类方法
}
}
class A{
void printl(Father f){
System.out.println("A Father");
}
}
class B extends A{
void printl(Father f){
System.out.println("B Father");
f.test();
}
void printl(Son son){
System.out.println("B son");
son.test();
}
}
测试类也不变
public class ObjectTest {
public static void main(String[] args) {
Father fa=new Father();
Son son=new Son();
A a=new B();
a.printl(fa);
a.printl(son);
}
}
根据例2 的分析,都调用B中的printl(Father f)是没什么错的了,就是看B中printl(Father f)的 f.test()的输出内容。
首先,测试类中a.printl(fa) ,调用B中的 printl(Father f)先输出了"B Father",然后,形参f指向的是实参fa,也就是,在
printl(Father f)中 形参f ----指向---->实参fa,这时候,f.test()调用fa.test()没什么问题,输出“Father”。
其次,测试类中a.printl(son);也是调用B中的 printl(Father f)先输出了"B Father"
然后呢,形参f指向的是实参son,形参f ----指向---->实参son,眼熟,这不是父类引用指向子类对象的吗?再看看Father和Son类,有继承,有重写,有父类引用指向子类对象,三个条件都满足,形成多态 f.test();调用子类方法,输出“Son”
所以,最终输出为
B Father
Father
B Father
Son
所以,识别重载和重写,辨别多态
三.重写
A.重写的定义
子类要重写父类方法,需要满足三个条件,
1.相同的方法名
2.相同的参数列表
3.相同的参数类型(参数类型为父类,子类关系也不行,必须相同,否则为重载了)
则,子类方法覆盖父类方法,
一些注意点:
1.构造函数是不能重写的
2.如果子类不能继承父类的方法,如:父类方法是final或者private,是不能重写的
B.重写@Override的作用,
重写时候,方法加上@Override注解和不加,都是重写,区别在于,加上@Override会检查父类是否有这个方法,是否构成了重写,否则编辑报错,
也就是父类没有方法test(),而在子类的test()加上@Override的注解,会编译报错。
如果没加的话,一不小心写错名字了,就构成重载了
四.重载overload
重载:重载的时候,方法名必须是一样的,可以参数类型,参数个数,参数顺序不一致,就构成了重载。
特别要说明的是:返回值不同,无法判断是否构成重载,会报错。
可以想象一下,两个一模一样的函数,一个返回String,一个返回int,调用的时候,怎么判断调用哪个,编译的时候就报错了。
五.总结
1.父类和子类同名方法,参数也是父类和子类的方法,不不构成重写,是重载。
2.实参的子类对象,赋值给形参父类引用,也是父类引用指向子类对象
3.判断多态,一定需要判断一下,是否子类重写了父类方法
4.父类引用,调用不了子类对象特有的方法。只能调用子类对象继承的方法和多态重写的方法
5.构造函数不能重写
6.@Override 在编译阶段判断父类有没有此方法,是不是重写
7.返回值不同,不是重载,因为根本不知道要调用哪个函数,编译报错
8.重载:1.方法名相同 2.参数列表不同(参数类型,个数,顺序)
8.重写:1.方法名相同2.参数列表完全相同(参数类型,个数,顺序)