本节目标:
1.掌握继承及用法
2.了解继承的组合
3.掌握多态的用法
目录
前言
我们知道,面向对象语言的三大特性是封装,继承和多态。在上一节,我们了解了什么是封装及封装的用法。在这一节,我们要了解oop的另外两个特性——继承和多态。
一、继承
1.1为什么要用到继承?
通俗地讲,java中的继承有些类似现实生活中的继承关系,子女会继承父母的财产,因为java是面向对象的思想,我们可以将子和父都看成一个对象,所以java中的类也有这种继承关系,例如,狗和猫狗都是动物,所以我们可以把动物看成父类,把猫和狗看成子类。
class Dog { public String name; public int age; public void bark() { System.out.println(name+"正在汪汪叫"); } public void eat() { System.out.println(name+" 正在吃饭"); } } class Cat { public String name; public int age; public void miaomiao() { System.out.println(name+" 正在喵喵叫"); } public void eat() { System.out.println(name+" 正在吃饭"); } }
此时我们发现, 上述代码中有很多重复代码
有什么办法可以将这些重复的代码提取出来,大家都可以调用呢。这时就要用用到继承的思想了。我们可以将代码共性进行抽取,创建一个新的类Animal,用来放置重复代码。
class Animal { public String name; public int age; public void eat() { System.out.println(name+" 正在吃饭"); } }
然后将Dog类和Cat类与Animal建立连接,使用关键字 extends ,此时Animal为父类,Dog和Cat类为子类。继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
class Dog extends Animal{ public void bark() { System.out.println(name+"正在汪汪叫"); } } class Cat extends Animal{ public void miaomiao() { System.out.println(name+" 正在喵喵叫"); } }
1.2 继承的概念
1.3 父类成员的访问
1.子类和父类不存在同名成员变量
public class Animal {int a ;int b ;}public class Dog extends Animal {int c ;public void method (){a = 10 ; // 访问从父类中继承下来的 ab = 20 ; // 访问从父类中继承下来的 bc = 30 ; // 访问子类自己的 c}}
2. 子类和父类成员变量同名
public class Test { int a = 5; int b = 4; int c = 3; } class Dog extends Test{ int a; int b; public void method(){ a = 100; b = 101; c = 102; System.out.println(a); System.out.println(b); System.out.println(c); } public static void main(String[] args) { Dog dog = new Dog(); dog.method(); } }
最终打印结果:
我们得到结论,成员变量的访问遵循就近原则,子类有优先访问子类自己的,子类没有再去调用父类的。调用成员方法时同理。
1.4 super 关键字
class Test { int a = 5; int b = 4; int c = 3; } class Dog extends Test{ int a = 100; int b = 500; public void func() { System.out.println(super.a); } public static void main(String[] args) { Dog dog = new Dog(); dog.func(); } }
最终打印结果,a = 5,调用了父类的成员变量。
要注意一点的是:super不能用在静态方法中,所以无法在main方法中调用,我们重新写了一个非静态的 func 方法,然后在main方法中调用func方法,就能够得到想要的结果。
super关键字总结:
1.5 子类构造方法
class Animal { public String name; public int age; public Animal(String name, int age) { // 父类的构造方法 this.name = name; this.age = age; System.out.println("Animal (String name,int age)"); } public void eat() { System.out.println(name+" 正在吃饭"); } } class Dog extends Animal{ public Dog (String name,int age) { super(name,age); //调用父类的构造方法,帮助初始化子类从父类继承过来的成员,并不会生成父类对象 System.out.println("Dog (String name,int age)"); } public void bark() { System.out.println(name+"正在汪汪叫"); } } class Cat extends Animal{ public Cat (String name,int age) { super(name,age); System.out.println("Cat (String name,int age)"); } public void miaomiao() { System.out.println(name+" 正在喵喵叫"); } }
1.6 super 和 this
相同点:
1. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
1.7 再谈初始化
class Animal { public String name; public int age; public Animal(String name, int age) { this.name = name; this.age = age; System.out.println("Animal (String name,int age)"); } static { System.out.println("Animal::{静态代码块}"); } { System.out.println("Animal::{实例代码块}"); } public void eat() { System.out.println(name+" 正在吃饭"); } } class Dog extends Animal{ public Dog (String name,int age) { super(name,age); //调用父类的构造方法,帮助初始化子类从父类继承过来的成员,并不会生成父类对象 System.out.println("Dog (String name,int age)"); } static { System.out.println("Dog::{静态代码块}"); } { System.out.println("Dog::{实例代码块}"); } public void bark() { System.out.println(name+"正在汪汪叫"); } } class Cat extends Animal{ public Cat (String name,int age) { super(name,age); System.out.println("Cat (String name,int age)"); } public void miaomiao() { System.out.println(name+" 正在喵喵叫"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog("black",5); } public static void main1(String[] args) { Dog dog = new Dog("xiaohei",2); dog.bark(); dog.eat(); System.out.println("--------------------"); Cat cat = new Cat("huahua",1); cat.miaomiao(); cat.eat(); } }
最终打印结果:
1.8 protected 关键字
还记得上节讲过的四个访问修饰限定符吗,其中有一个关键字 protected 上节没有介绍到,他主要用于继承。
范围 | public | protected | default | private |
同一包中同一类 | √ | √ | √ | √ |
同一包中不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的非子类 | √ |
为什么protected 主要用于继承呢,通俗讲因为继承有时需要在不同包中继承,能够在不同包中使用的关键字只有 public 和 protected , 但 public 是公共的,相对太不安全,所以继承中一般使用protected。
1.9 继承方式
但在Java中只支持以下几种继承方式:
1.单继承
2.多层继承
3.不同类继承同一个类
注意:Java中不支持多继承。
1.10 final关键字
fifinal关键可以用来修饰变量、成员方法以及类。
1.11 继承与组合
二、多态
2.1.多态的概念
![](https://i-blog.csdnimg.cn/blog_migrate/216a54b9bdfbc1f75b03e11471730d7d.png)
2.2 多态实现条件
class Animal { public String name; public int age; public Animal(String name, int age) { this.name = name; this.age = age; } public void eat() { System.out.println(name+" 正在吃东西"); } } class Dog extends Animal{ public Dog(String name, int age) { super(name, age); } public void bark() { System.out.println(name+" 正在汪汪叫"); } @Override //注解 //对 eat 方法进行了重写 public void eat() { System.out.println(this.name+" 正在吃狗粮!"); } } class Cat extends Animal { public Cat(String name, int age) { super(name, age); } public void miaomiao() { System.out.println(name+" 正在喵喵叫"); } @Override public void eat() { System.out.println(this.name+"正在吃猫粮"); } public static void main(String[] args) { Dog dog = new Dog("xiaobai",18); dog.eat(); dog.bark(); }
2.3 方法的重写
1.最基本的返回值,参数列表,方法名必须是一样的。
2.被重写的方法的访问修饰限定符在子类中要大于等于父类的。(从小到大:private,default(默认的),protected,public),比如,当父类被public修饰时,子类必须是public。
3.被 private 修饰的方法是不可以被重写的。
4.被 static 修饰的方法是不可以被重写的。
5.被 final 修饰的方法是不可以被重写的。
6.构造方法也是不可以被重写的。
7.重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写
重写和重载的区别
区别点 | 重写(override) |
重载
(overloade)
|
参数列表 | 一定不能修改 | 必须修改 |
返回类型
| 一定不能修改【除非可以构成父子类关系】 | 可以修改 |
访问限定符 | 一定不能做更严格的限制(可以降低限制) | 可以修改 |
2.4 向上转移和向下转型
2.4.1 向上转型
Animal animal1 = new Cat("七月三",6);
animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
常见的可以发生向上转型的三个时机或场景:
1. 直接赋值
Animal animal1 = new Cat("七月三",6);
2. 方法的参数,传参的时候进行向上转型
public static void eatfunc(Animal animal) { animal.eat(); } public static void main3(String[] args) { Dog dog = new Dog("小白",4); eatfunc(dog);}
3.返回值向上转型
public static Animal eatfun() { Dog dog = new Dog("小白",4); return dog;}
2.4.2 向下转型
public class Test{ public static void main(String[] args) { Animal animal = new Dog("xiaohuang",7); //向下转型 Dog dog = (Dog) animal; dog.bark(); }
2.5 多态的优缺点
class Shape { public void draw() { System.out.println("画一个图形!"); } } class Rect extends Shape{ @Override public void draw() { System.out.println("□!"); } } class Flower extends Shape { @Override public void draw() { System.out.println("❀!"); } } class Cycle extends Shape { @Override public void draw() { System.out.println("○!"); } }
假如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:
class Shape { public void draw() { System.out.println("画一个图形!"); } } class Rect extends Shape{ @Override public void draw() { System.out.println("□!"); } } class Flower extends Shape { @Override public void draw() { System.out.println("❀!"); } } class Cycle extends Shape { @Override public void draw() { System.out.println("○!"); } } public class Test { //不用多态 public static void main(String[] args) { Rect rect = new Rect(); Flower flower = new Flower(); Cycle cycle = new Cycle(); String[] strings = {"cycle","rect","cycle","rect","flower"}; for (String x:strings) { if(x.equals("cycle")) { cycle.draw(); } else if (x.equals("rect")) { rect.draw(); } else if (x.equals("flower")) { flower.draw(); } } } }
如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单:
public static void main(String[] args) { Shape[] shapes = {new Cycle(),new Rect(),new Cycle(),new Rect(),new Flower()}; for (Shape shape:shapes) { shape.draw(); }
class Triangle extends Shape {@Overridepublic void draw () {System . out . println ( " △ " );}}
总结
这一节涵盖的部分比较多,相对来说更难理解,但只要理解了继承和多态,我们的代码能力肯定会有一个质的飞跃!