跳到主要内容Java 继承与多态详解 | 极客日志Javajava
Java 继承与多态详解
本文讲解 Java 中的继承与多态机制。继承方面涵盖单继承、多层继承及组合优于继承的原则,介绍了 final 关键字对变量、数组、类的限制以及 protected 访问修饰符。多态部分阐述了概念、向上转型与向下转型的实现方式,重点讲解了方法重写(Override)的规则与动态绑定机制。文章还分析了多态在降低代码复杂度和增强扩展性方面的优点,以及属性无多态性和运行效率略低的缺陷。
嘘1 浏览 一、继承
1.1. 继承的方式

猫类可以继承动物类,中华田园猫类可以继承猫类。同样地,在 Java 当中,可以实现以下几种继承方式:单继承、多层继承、不同类继承同一个类。但是,Java 当中不支持多继承。
public class A { }
public class B extends A { }
public class A { }
public class B extends A { }
public class C extends B { }
public class A { }
public class B extends A { }
public class C extends A { }
1.2. final 关键字
(1)final 关键字修饰变量或字段,表示常量,也就是不能修改。
final int a = 10;
a = 20;
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
final int[] array = new int[]{1,2,3};
array = new int[]{10,20,30};
array[0] = 100;
final 修饰的是 array 这个引用变量本身,也就是 array 在栈上的地址不能被修改,就不能再去修改 array 里面的元素,但我们可以通过 array 的下标来进行访问。
public final class Animal {
public int age;
public String name;
}
public class Dog extends Animal {
public void bark(){
System.out.println("汪汪叫");
}
}
此时 Dog 子类里面就会报错,继承关系将不会存在。查看 Dog 类里面 String 的源码,就可以看到 String 被 final 修饰了。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
1.3. 继承与组合
继承表示对象之间是 is-a 的关系,比如:狗是动物,猫是动物。组合表示对象之间是 apart-of 的关系,比如:轮胎是汽车的一部分。
面向对象中有一个比较重要的原则'多用组合、少用继承'或者说'组合优于继承'。组合确实比继承更加灵活,也更有助于代码维护。
1.4. protected 关键字
二、多态
2.1. 多态的概念
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。比如,猫在吃的行为是吃猫粮,而狗吃的是狗粮。但重要的是,我们要理解多态的思想。
2.2. 向上转型
public class Animal {
public int age;
public String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println("在吃饭");
}
}
public class Dog extends Animal{
public void bark(){
System.out.println("汪汪叫");
}
public void wag(){
System.out.println("摇尾巴");
}
public Dog(int age,String name){
super(age, name);
}
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Animal(10,"dahuang");
Dog dog = new Dog(6,"xiao");
animal = dog;
}
}
Dog dog = new Dog("大黄", 5);
test1(dog);
public static void test1(Animal animal){
}
public Animal test1(){
return new Dog("大黄", 4);
}
以 Animal 作为一个接口,可以返回 Dog,也可以返回 Cat;参数也是一样可以接受 Dog 里面的形参,也可以接受 Cat 里面的形参。
向上转型的缺点:不能调用到子类特有的方法。给大家举个简单的例子
public class Animal {
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println("在吃饭");
}
}
public class Dog extends Animal{
public void bark(){
System.out.println("汪汪叫");
}
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Dog(6,"小黑");
animal.eat();
animal.bark();
}
}
有了向上转型,就可以进行多态,但还得有另一个条件,就是实现重写。
2.3. 重写
多态实现条件:1. 必须在继承体系下 2. 子类必须要对父类中方法进行重写 3. 通过父类的引用调用重写的方法
重写也可以成为覆盖。重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。
方法重写的规则:1. 方法名相同 2. 参数列表相同(个数、数据类型、顺序都相同)3. 返回值相同 4. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
在 Dog 这个子类,我们不满足于只用父类里的方法,我们就可以通过编译器自动生成一个 Override 的方法,点击 eat,就可以实现重写了。
public void eat() {
super.eat();
}
public void eat(){
System.out.println("在吃饭");
}
我们来看一下里面的源码,一直到 Object 类里面。Object 就是所有子类的父类,包括 Animal 这个父类也是默认继承 Object 这个类里面。
动态绑定:也称为后期绑定 (晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。
所以综上所述,动态绑定可以总结到以下两点:1. 父类引用必须引用子类对象 2. 子类重写父类的方法,通过父类引用调用被重写的方法
注意:访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被 public 修饰,则子类中重写该方法就不能声明为 protected。父类被 static、private 修饰的方法、构造方法都不能被重写。
2.4. 向下转型
上面我们讲到了向上转型,子类来调用父类的方法。那么,反过来就是向下转型。
public class Animal {
public int age;
public String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
}
public class Bird extends Animal{
public Bird(int age, String name) {
super(age, name);
}
public void fly(){
System.out.println("正在飞");
}
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Bird(2, "金丝雀");
Bird bird = animal;
}
}
这里的错误原因就如同基本类型里面的 long 类型转成 int 类型,造成数据的丢失。同样在引用类型里面,大的也不能向小的转化。要想实行向下转型,就得强转。
Bird bird = (Bird)animal;
Animal animal1 = new Dog(5,"旺财");
Bird bird1 = (Bird)animal1;
animal1.fly();
报错的原因为类型转化异常。Bird 与 Dog 不是同一个类,因为强转而骗过了编译器,所以说,向下转型不安全。如果我们要避免这面这种错误,就可以使用下面的方法。
if(animal instanceof Bird){
bird = (Bird)animal;
bird.fly();
}
if(animal instanceof Dog){
Dog dog = (Dog)animal;
dog.bark();
}
2.5. 多态的优缺点
public static void eatfunc(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Bird bird = new Bird(2,"金丝雀");
eatfunc(bird);
Dog dog = new Dog(5,"斯派克");
eatfunc(dog);
System.out.println(bird);
System.out.println(dog);
}
以下是运行结果:可以看到 Bird 和 Dog 虽然都调用同一个父类里的 eat 方法,但经过对 eat 的方法重写之后,就会出现同一行为表现出不同的结果。
优点:1. 能够降低代码的'圈复杂度',避免使用大量的 if - else 2. 可扩展能力更强。如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。
缺陷:代码的运行效率降低。1. 属性没有多态性 当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性 2. 构造方法没有多态性