javaSE 【面向对象编程】初阶 多态、抽象类、接口

面向对象编程

一、多态

1.1向上转型

image-20211114193546008

把子类对象赋值给父类对象的引用

多态体现为父类引用变量可以指向子类对象


public class Test1 {
    
    
    public static void main(String[] args) {
    
    
        Animal animal=new Animal("动物!!");
        Dog dog=new Dog("gougou");
        
        animal=dog;   //animal这个引用指向了dog引用所指的对象
        //把子类对象赋值给父类对象的引用
        animal=new Dog("gougou");
    }
}

上面两种方法都可以

向上转型发生的时机:

1、直接赋值

2、方法的传参

3、方法的返回值

1.1.1直接赋值

Animal animal=new Dog("gougou");
//animal父类的引用指向dog子类对象

1.1.2方法传参

public class Test {
    
    
    public static void func1(Animal animal1) {
    
    

    }
      //第一种方式
        Animal animal=new Dog("gougou");
        func1(animal);//Animal animal1=animal  -> animal=new Dog;
      //第二种方式
            Dog dog=new Dog("gougou");
            func1(dog);
  }

以上两种均可以

1.1.3方法的返回

第一种方式:

public class Test {
    
    
    public static void func1(Animal animal1) {
    
    

    }
     public static Animal func2(){
    
    
         //第一种方式
        Dog dog=new Dog("gougou");
        return dog;
         
        //return new Dog("gougou");  //这是简化的
    }  
  }

返回对象是Animal直接return dog是一样的,也是把子类对象赋值给父类对象的引用。

第二种方式:

public class Test {
    
    
   public static Dog func3() {
    
    
        Dog dog=new Dog("gougou");
        return dog;
    }
     public static void main(String[] args) {
    
    
        Animal animal1=func3(); 
     }
}

返回的对象是Dog,需要把子类对象赋值给父类对象的引用。

1.2动态绑定

满足的条件:

1、发生向上转型 -> 父类引用需要引用子类的对象。

2、通过父类引用来调用子类和父类同名重写方法。

上面的条件中提到了重写,那我们就先来看看什么是重写

1.2.1重写

重写也称为覆写/覆盖(Override)

先看这张图,针对eat()方法

image-20210913114212395

上面animal1确找不到eatDog()方法,其实道理很简短,前面的类型就是Animal的类型,再加上是子类Dog继承的父类Animal,所以变量animal1只能去Animal父类里面的方法。也得出一个结论:通过父类引用 调用方法或者成员变量的时候,只能调用父类自己特有的方法或者成员

我们现在改一下都把方法改成一样的会出现什么情况??

image-20210913115110623

运行成功了,

这就引出了重写

重写:

1、方法名相同

2、方法的返回值相同

3、方法的参数列表相同

1.2.1.1重写的注意事项:

1、重写和重载完全不一样。(下期预告,区分javaSe里面容易混淆重写和重载的区别,等等)

2、static修饰的静态方法不能重写

3、final也不能被重写,final是密封方法:当前方法不能被重写。

4、重写中子类的方法的访问权限一定要大于等于父类的访问权限。

private<包(什么都不加)<protected<public

5、 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).

竞然调用的都是Animal的类为什么最后调用了Dog的类尼?

我们再回到动态绑定

public static void main(String[] args) {
    
    
        Animal animal=new Animal("动物");
        animal.eat();
    
        Animal animal1=new Dog("gougou");
        animal1.eat();//向上转型

    /*
    *多态
    *运行时绑定
    *父类引用 引用子类对象。同时,通过父类引用调用同名的覆盖方法,此时就会发生运行时绑定
    */
    }

满足以上条件的时候,==在编译过程中,调用的是父类的方法,但是运行的时候,实际上调用的是子类的方法。==这就叫做动态规划!!!

image-20210914172453609

从反编译这个图亦可以看出动态绑定

1.3向下转型

上面说了向上转型,向下转型顾名思义就是把向上转型概念反过来

image-20210914173425709

image-20210914215219047

完成了向上转型之后为什么又会报错尼?在编译器上提示的是他们是两种不同的类型,其实可以用包含关系来判断,狗是一种动物,但是动物不一定是狗,所以Animal的范围要广一点,强制类型转换成Dog就不会报错了。

image-20211021234117062

为什么强制类型转了还是报错尼??,没有发生向上转型,导致类本身引用的对象还是不一样

所以结论:前提,那就是要发生了向上转型才能发生向下转型,只有发生了向上转型才能发生向下转型

image-20211021234146649

但是向下转型有时候不靠谱:

image-20210914220656502

animal本质上引用的是Dog对象,是不能转换成Bird对象的,会显示类型转换异常,为了避免这种情况会用到instanceof

instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全了 。

1.4在构造方法中调用重写的方法(一个坑)

new 一个Dog对象,又从Dog子类里面又传进父类Animal构造方法里,如果eat()方法在前,直接调用下面的eat()方法,没有执行this.name=name,所以为null,右边构造方法里的eat()方法放在下面是执行了this.name=name之后调用的,所以可以打印出来。这就是构造方法里面的一个坑。

1.5理解多态

有了上面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(polypeptide) 的形式来设计程序了

class Shape {
    
    
    public void drew(){
    
    

    } 
}
class Cycle extends Shape {
    
    
    //调用同名的覆盖方法
    @Override
    public void drew() {
    
    

        System.out.println("画一个○");
    }
}

class Rect extends Shape {
    
    
    //调用同名的覆盖方法
    @Override
    public void drew() {
    
    

        System.out.println("画一个方片♦");
    }
}
public class TestDome {
    
    
    public static void drewMap(Shape shape) {
    
    
        shape.drew();
    }

    public static void main(String[] args) {
    
    
        //向上转型,方法传参
        Cycle cycle=new Cycle();
        drewMap(cycle);

        Rect rect=new Rect();
        drewMap(rect);
    }
}

*注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。*

在这个代码中, 分割线上方的代码是 类的实现者 编写的, 分割线下方的代码是 类的调用者 编写的 。

多态是一种思想,通过父类引用调用同名的覆盖方法可以表现出不同的形式,这就是多态,前提是父类引用对象重写了父类draw()方法;

image-20210916215726952

多态顾名思义, 就是 “一个引用, 能表现出多种不同形态” 。

或者也可以这样说:当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现
(和 shape 对应的实例相关), 这种行为就称为 多态

1.5.1使用多态的好处

一、类调用者对类的使用成本进一步降低

1、多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可 。

二、能够降低代码的 “圈复杂度”, 避免使用大量的 if - else

三、可扩展能力更强

二、抽象类:

父类 Shape 中的 draw 方法好像并没有什么实际工作 ,绘制图形都是由Shape 的各种子类的 draw 方法来完成的 ,像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstractmethod), 包含抽象方法的类我们称为 抽象类(abstract class)

2.1语法规则:

abstract class Shape {
    
    
    abstract public void draw();
}

包含抽象方法的类叫做抽象类。方法和类都由abstract修饰的。

注意事项:

1、抽象类当中可以定义数据成员和成员方法

2、抽象类不能直接实例化.

image-20210916221201128

尽然不能实例化,那他意义在哪?

3、存在的意义就是为了被继承。

4.如果一个类继承了抽象类,那么这个类必须重写所有的抽象方法。抽象方法也不能用final修饰,final和abstract不能共存。

image-20210916222301055

5、抽象方法不能被private修饰。本身存在的意义就是继承。

6、一个抽象类A可以被另外一个抽象类B继承,但是如果有另外普通类c继承了这个抽象类B,那么普通类c里面所有的抽象方法都被重写。

image-20210916223748061

三、接口

3.1语法规则:

对于接口,里面的组成只有抽象方法全局常量,所以很多时候为了书写简单,可以不用写public abstract 或者public static final。并且,接口中的访问权限只有一种:public,即:定义接口方法和全局常量的时候就算没有写上public,那么最终的访问权限也是public,注意不是default。以下两种写法是完全等价的:

接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合。接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现

1、接口是用interface来修饰

2、接口里面的方法只能是抽象方法。且,接口当中的方法默认是public abstract的。

image-20210917090803386

3、在JDK1.8开始,接口当中的方法,可以是普通方法,但前提是:这个方法是有default修饰的。(默认方法)

image-20210917091031621

4、接口同样不可以进行实例化。

5、类和接口的关系是implements(实现),同样该类需要实现我们的接口当中所有的抽象方法.

image-20210917092147641

6、一个类可以使用implements实现n个接口,每个接口之间使用逗号分割开就可以了。

image-20210917093029902

为什么可以实现多个接口尼?因为可以多继承。

7、接口和接口之间的关系,使用extends关键来维护。意为扩展。扩展该接口其他接口的功能。

interface A {
    
    
    void func1();
}

interface B {
    
    
    void func2();
}

interface C extends A,B {
    
    
    void func3();
    
}

8、接口也是可以发生向上转型和多态的。他只需要关注,这个类的功能。

猜你喜欢

转载自blog.csdn.net/qq_54219272/article/details/120375713