JavaSE复习---继承和多态

(一)继承

面向对象语言的三大特征:封装,继承,多态

   我们知道每种生物都会有各自的特征,但也会有一些共性,我们继承就是对这些共性来进行抽取,实现代码复用,我们把抽象出来的共性放到一个类中,把他叫做“ 父类 ”,然后在这些共性上进行扩展(加上每种生物自己的特征),这样产生新的类,就叫做子类(派生类)

  继承呈现了面向对象程序设计的层次结构,主要解决了:共性的抽取,来实现代码的复用

  就比如:猫和狗这两个类,他们都会吃东西,但是猫会抓老鼠,狗不会,狗会守大门,猫不会,但是他们吃东西这个特性是两者都有点,这样我们就可以把这个共性内容抽取,采用继承的思想来达到共用。

  上述是一个继承图,我们可以看到cat和dog继承了animal类,子类可以复用父类中的成员,并且拥有属于自己的成员,并且我们可以看到这里体现了继承实现代码复用的好处

1)继承的语法

其实也就是extends

注:子类会把父类的成员变量或者成员方法继承到子类中,但是被private修饰的不会被继承

2)父类成员访问

1.子类父类的成员变量不同名

2.子类父类有同名成员变量

  我们遵循的是如果子类有就访问子类,子类没有才访问父类,如果父类也没有就报错,如果变量同名访问自己的,如果我们想要访问父类中同名变量使用super.变量名(等一会讲)

3)super关键字

  我们上述说子类父类有同名成员,在子类调用时会优先使用子类的成员,但是如果想要访问父类的,就需要使用我们的super关键字,这个关键字的作用就是在子类方法中访问父类的成员

注:super其实跟this很像,super是父类继承下来的引用,所以super跟this一样,只能在非静态方法中使用

4)子类构造方法

  我们子类对象在构造时,需要先调用父类构造方法的,然后等待父类构造方法完成,才能执行子类的构造方法

如果我们不主动写父类的构造方法(super())系统会自动隐式的给我们加上

我们之前说类和对象的时候说,系统给我们的默认构造方法,只有在我们没有自己写的时候才有,当我们自己实现了一个构造方法,这个默认的构造方法就没有了,所以一旦我们自己写了一个父类构造方法,就需要我们手动的去调用,否则编译失败

在子类构造方法中super()调用父类构造方法时,必须super要是构造函数第一条语句,与this()冲突,所以两者只能有一个

5)super和this

这里来讲一下super和this的相同点和不同点

相同点:都是Java关键字

只能在类的非静态方法中使用访问非静态成员方法和字段

在构造方法中必须为构造方法中的第一条语句,所以不可以同时出现

不同点:this是当前对象的引用,super是当前对象父类的引用

在非静态成员方法中,this用来访问本类的引用,super访问父类引用

构造方法中,this用来调用本类构造方法,super用来调用父类构造方法(两者不能同时出现)

6)初始化代码块的顺序

跟之前的顺序一样   :静态代码块-->实例代码块-->构造方法,只不过父类要先于子类执行

注:静态代码块只会执行一次

7)继承方式

我们生活中,关系是很复杂的,我们上面说cat和dog继承animal,但是他们还可以继承其他父类

所以这里我们来看一下Java中支持的继承关系

还有一个就是我们刚才的dog和cat(不同类)都继承于同一个类

Java唯独不支持多继承也就是class A同时继承于class B和class C(但是接口可以解决这个问题,下篇我们再说)

8)final关键字

final是用来修饰变量,成员方法和类的

1.final修饰变量,就会把他变为常量(不可以修改)

2.修饰方法表示方法不能被重写

3.修饰类表示类不可以被继承

这里再来介绍下他的两个兄弟:finally和finalize()

finally:表示finally里面的代码无论如何都会执行(无论是报错还是抛异常都会执行)

finalize:用于内存回收前的一些清理操作

(二)多态

多态我们简单理解下就是多种形态,也就是完成一个行为,不同的对象会有不同的形态

1)多态实现条件

有三个:子类重写父类一个方法,发生向上转型,通过父类调用子类重写的方法

那接下来我们就来解析一下这三个条件

2)重写

重写也叫覆盖,就是子类对父类非静态,非private修饰,非final修饰,非构造方法等实现过程进行重写编写,函数名,返回值和形参都不变,只有函数体改变

他的好处就是子类可以根据需要,定义属于自己的行为

重写规则总结:

  1.子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致

  2.被重写的方法返回值类型可以不同,但是必须是具有父子关系的

  3.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected

  4.父类被static、private修饰的方法、构造方法都不能被重写。

  5.重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.

其实很好理解,重写除了方法体不同其余都相同,重载只有方法名相同其余都可以不同

这里来简单介绍一下静态绑定和动态绑定

静态绑定:代码的编译时能够清楚的知道要执行哪一个方法

动态绑定:代码的编译时不能够清楚的知道要执行哪一个方法,只有在运行时才可以知道

3)向上转型

其实就是实例化一个子对象但是由父类引用接收

向上转型也有三种使用方式:直接赋值

方法传参

方法返回

向上转型的优点:让代码实现更加简单灵活

缺点:向上转型后就不可以使用子类的特有方法了,如果我们想使用就需要使用向下转型

向下转型

在我们向上转型后,我们发现是调用不了自己的方法的所以我们可以强转回Dog类

但是这里有个问题,我们还有一个Cat类继承这个animal,cat也有自己的方法,那我们该怎么办?如果转为Dog类会有什么问题?我们怎么解决?

我们发现抛异常,因为我们向下转型animal转为Dog了,所以向下转型充满了不确定性,我们需要判断实际上animal这个引用,引用的究竟是哪一个类(也就是我们要判断他的类型)

这里可以使用instanceof

这样我们就可以很好判断类型,然后针对化的进行向下转型

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。

之后我们多态就只剩最后一步,通过父类引用调用子类重写的方法,这一步应该都会吧,就不说了

4)多态代码演示

讲完了上面三步,我们就可以使用好多态了

class Animal{
    int age;
    String name;
    public void say(){
        System.out.println("说话!!!!");
    }
}
class Dog extends Animal{
    public void say(){
        System.out.println(getClass()+" 狗在说话!!!!");
    }
}
class Cat extends Animal{
    public void say(){
        System.out.println(getClass()+" 猫在说话!!!!");
    }
}
public class Text {
    public static void func(Animal animal){
        animal.say();
    }
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.say();
        func(new Dog());
    }
}

5)多态的优缺点

1.能够避免使用大量的“if else”

例如我们现在要打印多个形状

public static void drawShapes() {
    Rect rect = new Rect();
    Cycle cycle = new Cycle();
    Flower flower = new Flower();
    String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
    
    for (String shape : shapes) {
        if (shape.equals("cycle")) {
            cycle.draw();
       } else if (shape.equals("rect")) {
            rect.draw();
       } else if (shape.equals("flower")) {
            flower.draw();
       }
   }
}

这样就需要我们重复多次的判断

如果使用多态,我们代码就可变成这样

2.可扩展性强

就还拿上面打印形状举例,如果我们新增一个形状,当我们没有使用多态时,我们就需要再添一个if-else,如果我们使用多态,就什么都不用做

多态缺点:

1.属性是没有多态的

当父类子类有同名属性时,通过父类引用,只能引用父类自己的成员属性

2.构造方法们没有多态

 
class B {
    public B() {
        // do nothing
        func();
   }
 
    public void func() {
        System.out.println("B.func()");
   }
}
 
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
   }
}
 
public class Test {
    public static void main(String[] args) {
        D d = new D();
   }
}

我们看上面这个代码,这个代码是有坑存在的 提问:num最后输出为多少?

我们来分析下这个代码,首先new D()会先调用构造方法,然后构造方法中会隐式调用父类构造方法,然后父类构造方法中调用func()(但是这里发生多态,我们实际上new的是D所以会调用D类中的func函数)但是这里我们D的构造方法还没有执行完,num还没有赋1,所以num打印0

所以我们尽量不要再构造方法中调用方法(如果这个方法被子类重写就会触发动态绑定,此时子类对象并没有构造完成,一些值还不正确)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值