小比特的日常--多态笔记

s实践是检验真理的唯一标准!

多态:一个引用可以表现出多种行为/特性。
向上转型 ::产生的原因

类名称        类引用        =new        该类对象;

父类名称         父类对象               =new        子类对象;

之前我创建对象的代码是: 当前类名称,当前类引用,=new 当前类对象();

鸭子的父类是鸟,鸟的父类是动物。

现在我让 Animal bird1= new bird();  从右向左去读,鸟是一个动物, 这是不错的。

现在我让 Bird    bird1= new Duck();  从右向左去读,鸭子是一个鸟,这是不错的。

现在我让Animal  animal1=new Duck(); 从右向左去读,鸭子是一个动物,这也是不错的。

推论:只要是有继承的传递关系,都可以由父类名称去new一个子类对象。

初始化

然后让Bird 继承Animal, 让Duck继承Bird。 再创建一个Test类。

多态最大的意义在:参数统一化,降低使用难度。

假如在Test中创建一个fun()方法来接收Animal以及其子类的对象作为参数,如果没有向上转型:如下

 自然界Animal的对象多如繁星,由于每个动物类型不一样,有多少子类方法就得重载多少次!

而且使用者要先知道Animal有的子类多少个对象,如下:

 向上转型产生的原因:

 

 写一行 fun(先祖) 就行了,省略掉了下面的两行。

问:当Animal,有一个新的子类的时候很容易扩展吗?

比如唐老鸭是鸭子的子类,无非多双筷子,fun不用动的。如下:

下一步我在每一个类中都假如一个eat方法,Animal 的public 会让所有子类继承,每个子类都有自己的eat方法。(eat方法就是重写方法,后面会讲)

public void eat(){
        System.out.println("Animal 类的 eat方法");
    }

 在Test 的fun中调用eat方法:

输出结果表示:fun中animal 局部变量的引用调用eat方法时,当传入不同的对象时,表现出来不同的eat方法行为---> 多态性

同一个引用(变量名称),同一个方法名称,根据对象的不同表现出来了不同的行为---> 多态性

下面讲讲为什么会这样? 这个方法的本质就在于方法重写。

《下面看看什么是方法重写》

在说方法重写之前,复习下方法重载。

发生在同一个类中,定义了若干个方法名称相同,参数列表不同的一组方法---重载方法 (overload)根据不同的参数调用不同的方法。

发生在有继承关系的类之间,子类定义一种方法,该方法只有权限和父类不同,此方法就是重写

(override)

那么,fun(Animal animal){animal.eat()}调用的是谁???

代码附上,复制到本地看看结果就不需要解释了:

代码段1

package polymorphism;

public class Test {
    public static void main(String[] args) {
        Animal animal  =new Animal();
        Animal animal1 =new Bird();
        Animal animal2 =new Duck();
        Animal animal3 =new MrsTang();
        //调用fun传入 animal1,2,3
        fun(animal);
        fun(animal1);
        fun(animal2);
        fun(animal3);
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}

代码段2

package polymorphism;

public class Bird extends Animal{
    public void eat(){
        System.out.println("Bird 类的 eat方法");
    }
}

代码段3

package polymorphism;

public class MrsTang extends Duck{
    public void eat(){
        System.out.println("MrsTang 类的 eat方法");
    }
}

代码段4

package polymorphism;

public class Duck extends Bird{
    public void eat(){
        System.out.println("Duck 类的 eat方法");
    }
}

代码段5

package polymorphism;

public class Animal {
    public void eat(){
        System.out.println("Animal 类的 eat方法");
    }
}

回答:调用的是重写的eat方法。

那么如果子类没有重写这个方法,我该调用谁呢?

try:创建一个Animal的子类 Me

package polymorphism;

public class Me extends Animal{
}
package polymorphism;

public class Test {
    public static void main(String[] args) {
        Animal animal  =new Animal();
        Animal animal1 =new Bird();
        Animal animal2 =new Duck();
        Animal animal3 =new MrsTang();
        Animal animal4 =new Me();
        //调用fun传入 animal1,2,3
        fun(animal);
        fun(animal1);
        fun(animal2);
        fun(animal3);
        fun(animal4);
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}
Animal 类的 eat方法
Bird 类的 eat方法
Duck 类的 eat方法
MrsTang 类的 eat方法
Animal 类的 eat方法

看到输出的时候:如果Me类没有重写eat方法,输出的就是“Animal 类的 eat方法”,就是说调用的是父类的eat方法。

如果Me是MrsTang的子类呢?

Try:

package polymorphism;

public class Me extends MrsTang{
}
Animal 类的 eat方法
Bird 类的 eat方法
Duck 类的 eat方法
MrsTang 类的 eat方法
MrsTang 类的 eat方法

果然不错,输出的就是直接父类的eat方法。

那么如果直接父类也没有重写eat方法呢?

try: 

package polymorphism;

public class Test {
    public static void main(String[] args) {
        Animal animal  =new Animal();
        Animal animal1 =new Bird();
        Animal animal2 =new Duck();
        //这两行不重写时
        Animal animal3 =new MrsTang();
        Animal animal4 =new Me();
        fun(animal);
        fun(animal1);
        fun(animal2);
        fun(animal3);
        fun(animal4);
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}
package polymorphism;

public class Me extends MrsTang{
}
package polymorphism;

public class MrsTang extends Duck{
//    public void eat(){
//        System.out.println("MrsTang 类的 eat方法");
//    }
}

Animal 类的 eat方法
Bird 类的 eat方法
Duck 类的 eat方法
Duck 类的 eat方法
Duck 类的 eat方法

结果显示:Me 的直接父类MrsTang没有重写时,两个子类都调用了Duck的重写。

最极端的没意义,就不试了。

那么,当发生重写时,权限要求子类权限大于等于父类权限。private<defalt<protected<public

(不包含private权限)

Try:

 结果显示:方法权限有要求子类大于等于父类

那么,请你试试父类fun权限private,子类public,

try:

IEDA不让用private

删掉改成default权限可以。

之前我说过,private 中使用Setter 和Getter方法,子类才有权限访问父类的方法。

所以我把Test的main方法放到Animal 这个类下,让他可见,然后注释掉Test类

try:

package polymorphism;

public class Animal {
    private void eat(){
        System.out.println("Animal 类的 eat方法");
    }
    public static void main(String[] args) {
        Animal animal  =new Animal();
        Animal animal1 =new Bird();
        Animal animal2 =new Duck();
        //这两行不重写时
        Animal animal3 =new MrsTang();
        Animal animal4 =new Me();
        fun(animal);
        fun(animal1);
        fun(animal2);
        fun(animal3);
        fun(animal4);
    }
    public static void fun(Animal animal){
        animal.eat();
    }
}

package polymorphism;

public class Bird extends Animal{
    public void eat(){
        System.out.println("Bird 类的 eat方法");
    }
}
package polymorphism;

public class MrsTang extends Duck{
    public void eat(){
        System.out.println("MrsTang 类的 eat方法");
    }
}
package polymorphism;

public class Duck extends Bird{
    public void eat(){
        System.out.println("Duck 类的 eat方法");
    }
}
package polymorphism;

public class Me extends MrsTang{
}
Animal 类的 eat方法
Animal 类的 eat方法
Animal 类的 eat方法
Animal 类的 eat方法
Animal 类的 eat方法
@overide 校验重写命令;

为什么重写报错?

 因为:私有权限的方法没法被重写。 

方法重写能不能重写一个static方法?

答:多态的本质--父类调用了多个子类的对象,子类对象重写相应的方法, 才有 了特殊的行为。而static 和对象无关。方法重写只发生在普通方法中。

 所以重写方法不能发生在static方法中。

重写对比重载
重载重写
概念        方法名称相同,参数的类型及个数不同        方法名称,返回值类型,参数的类型及个数完全相同
范围        一个类        继承关系
限制        没有权限要求        被重写的方法不能拥有比父类更严格的访问控制权限,不含private。
static        没要求        不能重写static方法

记住:使用父类调用普通方法时,若子类重写了该方法,则调用该对象所在子类重写后的方法。

向上转型发生的时机

方法传参和引用赋值

方法的返回值

返回一个Animal类型的对象,起名叫test,但是实际代码实现的时候ruturn一个子类的对象。第一张图是优化第二张图的。


默认先调用父类的无参构造fun,然后调用子类重写后的fun,然后输出num=0 ,注意此时子类对象还没初始化,现处于super()阶段,num自然是0,

package polymorphism;
//父类-唐老鸭
public class MrsTang {
    public MrsTang(){fun();}
    public void fun(){System.out.println("MrsTang 类的 fun方法");}
}
package polymorphism;
//子类-米老鼠
public class Mouse extends MrsTang{
    private  int num=10;
    public void fun(){System.out.println("Mouse.fun,num="+num);}

    public static void main(String[] args) {
        Mouse mouse=new Mouse();
        mouse.fun();
    }
}

拿去DeBug看看。

package polymorphism;

public class MrsTang extends Animal{
    public MrsTang(){eat();
        System.out.println("MrsTang类的 eat方法");}

    public void play(){System.out.println("MrsTang 类独有的 play方法");}


}
package polymorphism;

public class Animal {
    public  void eat(){
        System.out.println("Animal 类的 eat方法");
    }
    public static void main(String[] args) {
        Animal animal=new MrsTang();
        animal.eat();
    }

}

Animal 类的 eat方法
MrsTang类的 eat方法
Animal 类的 eat方法
 

如果我调用子类拓展的play方法,发现调用不了

 那么注意:

类名称         引用名称        =new        类 实例();

引用名称    方法名称();

能通过 " " 访问的方法   类名称  说了算,能访问的这些方法必须都在类中定义过,编译器现在类中查找是否包含指定方法。

至于这个方法到底表现出什么样子,我们后边的 实例 方法说了算。

实例会重写这个方法,表现出新特性。

总之,先判断有没有这个调用,如果有,再判断是啥样子。

回到之前的play调用,有什么方法可以让我调用子类的拓展方法吗?

TRY TRY TRY:

Animal animal =new 唐老鸭();

animal.play();

animal这个引用是披着鸭子皮的动物,本质上是一个鸭子,披上了Animal 的外衣,此时只能调用Animal中定义的方法,比如这个鸭子它不会下蛋,不会浮水,不会嘎嘎嘎,但是会吃喝拉撒睡。

如果此时要调用play方法,需要脱掉这层外衣,还原为子类引用--------向下转型

子类名称          自类引用        =(子类名称)        父类引用

MrsTang mrstang       =(MrsTang) animal;  // 脱掉animal对应的对象的外衣还原为具体的子类引用

向下转型没有new 新的对象,

将父类引用强制类型转换为子类引用:

子类  is  a  父类 ---> 天然的 唐老鸭是一个动物;

父类  is  a  子类  ---> 动物不一定是唐老鸭。。  

package polymorphism;

public class Animal {
    public  void eat(){
        System.out.println("Animal 类的 eat方法");
    }
}
package polymorphism;

public class MrsTang extends Animal{
    public MrsTang(){eat();
        System.out.println("MrsTang类的 eat方法");}

    public void play(){System.out.println("MrsTang 类独有的 play方法");}
    public static void main(String[] args) {
        Animal animal=new MrsTang();
        animal.eat();
        //play在Animal 中不存在,将animal引用还原为MrsTang类引用
        MrsTang mrsTang=(MrsTang)animal;
        mrsTang.play();
    }
}

Animal 类的 eat方法
MrsTang类的 eat方法
Animal 类的 eat方法
MrsTang 类独有的 play方法

 new了几次就有几个对象,变得只是引用的名称。

比如说:Animal animal =new 我自己();

我就是一个动物的化,我只应该具备 一些动物的共通行为,吃喝拉撒睡;

但是写代码这个行为就必须在 人这个类中才有。强转为人,因为写代码是人独有的。

要发生向下转型必须先发生向上转型,  毫无关系的两个类无法强转。

怎么理解这句无关系的类 不能强转呢?

看图中代码 绿字是诠释。 ClassCastRxception=类型转换异常

 要发生向下转型首先就需要先发生向上转型,  new一个类个人家产生关系,之后在可以强转。

下面教一教如何规避 :当发生向下转型的时候如何规避掉类型转换异常

 

 答案就是--用if else 来判断  是否可以向下转型:

package polymorphism;

public class MrsTang extends Animal{
    public MrsTang(){eat();
        System.out.println("MrsTang类的 eat方法");}
    public void play(){System.out.println("MrsTang 类独有的 play方法");}
    public static void main(String[] args) {
        Animal animal1=new Animal();
        Animal animal2=new MrsTang();
        // 如果 animal1和  MrsTang之间有关系,我才强转
        if (animal1 instanceof  MrsTang){
            MrsTang mrsTang=(MrsTang) animal1;
            System.out.println("animal1转型成功!");
        }else {
            System.out.println("animal1不是指向MrsTang类型的引用");
        }
        if (animal2 instanceof  MrsTang){
            MrsTang mrsTang=(MrsTang) animal2;
            System.out.println("animal2转型成功!");
        }else {
            System.out.println("animal2不是指向MrsTang类型的引用");
        }
}}
用一个父类输出多个子类
package Sharp;

/**
 * 所有图形当前的父类
 */
public class Sharp {
    //打印当前图形的样子,什么样子呢?不知道
    // 当前的Sharp没有具体的实现,就叫空实现,就是一个空的{}。
    public void print(){}
}
package Sharp;

public class Cycle extends Sharp{
    @Override  //检查当前有没有正确复写
    public void print(){
        System.out.println("圆形");
    }

}
package Sharp;

public class Square extends Sharp {
    //快速重写的快捷键  Alt+Insert 或者直接写方法名

    @Override
    public void print() {
        System.out.println("正方形");
    }
}
package Sharp;

public class Triangle extends Sharp{
    @Override
    public void print() {
        System.out.println("三角形");
    }
}
package Sharp;

public class Test {
    public static void main(String[] args) {
        //这个方法可以接受所有Sharp类,调用其print方法打印图形
        fun(new Cycle());
        fun(new Square());
        fun(new Triangle());
    }
    //向上转型,子类对象被一个父类引用
    public static void fun(Sharp sharp){
        sharp.print();
    }
}

圆形
正方形
三角形

这三个子类都是Sharp的子类且都重写了print()方法,多态是非常依赖子类重写方法的!普通父类没法强制要求子类重写方法。

解释:比如我创建一个子类是椭圆形,但是没有重写print()方法,父类是没法强制要求子类去写的,输出也没有问题。

若要强制要求子类去重写父类方法的化,就需要用到抽象类。

抽象类

人类

此时Sharp ,print()方法都是抽象的概念,所以这就是一个抽象类。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值