《Java基础,Java多态入门到进阶:重写、重载、转型的逻辑与实战避坑》

多态

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个⾏为,当不同的对象去完成时会产⽣出不同的形态。说直白一些就是,同⼀件事情,发⽣在不同对象⾝上,就会产⽣不同的结果;先别想代码,想象一个遥控器上的"播放"按钮:

按下去,如果是DVD机,他就开始读光盘

如果是MP3播放器,他就开始放音乐

如果是手机上的视频APP,他就开始播放视频

同一个按钮"播放",作用在不同对象时,产生不同的行为,这就叫多态

多态的实现条件

1.必须在继承体系下

2.⼦类必须要对⽗类中⽅法进⾏重写

3.通过⽗类的引⽤调⽤重写的⽅法

多态体现:在代码运⾏时,当传递不同类对象时,会调⽤对应类中的⽅法

...

java 复制代码
class Animal {
    void makeSound () {
        System.out.println("动物发出声音");
 }
}
S

class Cat extends Animal{
    @OverrideS
    void makeSound() {
        System.out.println("喵喵喵");   
 }
}


class Dog extends Animal{
    @Override
    void makeSound() {
        System.out.println("汪汪汪");   
 }
}


class Test {
    public static void main(String[] args) {
        Animal a1 = new Dog();  //父类引用指向Dog对象
        Animal a2 = new Cat();  //父类引用指向Cat对象
        a1.makeSound();S
        a2.makeSound(); 
   }
}


//输出结果
汪汪汪
喵喵喵

...

a1的类型是Animal,但他实际指向的对象是Dog

a1.makeSound()编译的时候,编译器只知道Anima里面有makeSound(),所以语法通过

但运行时,Java虚拟机发现a1实际是Dog的makeSound(),输出"汪汪汪"

这就是一个方法调用表现出多种形态------多态

重写

重写也成为覆盖,重写是⼦类对⽗类⾮静态、⾮private修饰,⾮final修饰,⾮构造⽅法等的实现过程进⾏重新编写, 返回值和形参都不能改变。即外壳不变,核⼼重写!重写的好处在于⼦类可以根据需要,定义特定于⾃⼰的⾏为。 也就是说⼦类能够根据需要实现⽗类的⽅法

重写的规则

1.⼦类在重写⽗类的⽅法时,⼀般必须与⽗类⽅法原型⼀致: 返回值类型 ⽅法名 (参数列表) 要完全一致。(特殊情况:主要在返回值类型上,也就是协变类型,也就是子类重写方法时,可以把返回值类型改成父类返回值的子类,比如父类方法返回Animal,子类可以返回Dog,这时候方法原型的返回值类型不完全一致,但满足父子类的继承关系;除此之外方法名、参数列表还是得完全一致,这才是重写的核心)

2.被重写的⽅法返回值类型可以不同,但是必须是具有⽗⼦关系的(也就是协变类型)

3.访问权限不能⽐⽗类中被重写的⽅法的访问权限更低。例如:如果⽗类⽅法被public修饰,则⼦类中重写该⽅法就不能声明为 protected(public > protected > 包访问权限 > private)

4.⽗类被static、private修饰的⽅法、构造⽅法都不能被重写

5.重写的⽅法, 可以使⽤ @Override 注解来显式指定. 有了这个注解能帮我们进⾏⼀些合法性校验。(加了这个后,如果不小心把方法名、参数名之类的写错了编译器就会报错,此时我们就可以检查。说白了就是多了容错)

区别点 访问限定符 返回类型 参数列表
重写 可以降低限制 一定不能修改(除非可以构成父子关系) 一定不能修改
重载 可以修改 可以修改 必须修改

即:⽅法重载是⼀个类的多态性表现,⽽⽅法重写是⼦类与⽗类的⼀种多态性表现

向上转型和向下转型

向上转型

向上转型:实际就是创建⼀个⼦类对象,将其当成⽗类对象来使⽤

语法格式:父类类型 对象名 = new 子类类型()

...

java 复制代码
Animal a1 = new Cat();


//animal是⽗类类型,但可以引⽤⼀个⼦类对象,因为是从⼩范围向⼤范围的转换

...

使用场景

1.直接赋值

2.方法传参

3.方法返回

...

java 复制代码
//直接赋值
Animal a1 = new Dog();
//参数传递
public static void func(Animal animal) {
}
//返回值
public static Animal func2() {
    return new Dog();
}

...

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

向上转型的缺点:不能调用到子类特有的方法

向下转型

将⼀个⼦类对象经过向上转型之后当成⽗类⽅法使⽤,再⽆法调⽤⼦类的⽅法,但有时候可能需要调⽤⼦类特有的⽅法,此时:将⽗类引⽤再还原为⼦类对象即可,即向下转换

...

java 复制代码
public class Animal {
    void makeSound() {
        System.out.println("动物发出叫声");
  }


public class Dog extends Animal {
    void bark() {
        System.out.println("狗正在汪汪汪叫");   
 }
}


public class Test {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();
        dog = (Dog) animal;
        dog.bark();
       //程序可以通过编程,但运⾏时抛出异常---因为:dog实际指向的是animal,animal中并没有bark()这个方法

    }
}

...

向下转型⽤的⽐较少,⽽且不安全,万⼀转换失败,运⾏时就会抛异常。Java中为了提⾼向下转型的安全性,引⼊了 instanceof ,如果该表达式为true,则可以安全转换

使用多态的好处

1.能够降低代码的 "圈复杂度", 避免使⽤⼤量的 if - else

2.可扩展能⼒更强

避免在构造⽅法中调⽤重写的⽅法

不要在父类的构造方法中调用父类和子类同名的方法,如下:

...

java 复制代码
public class B {
    public B() {
    func();
    }

    public void func() {
    System.out.println("B.func()");
    }
}



public 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();
    }
}


//执行结果
D.func()0

...

构造 D 对象的同时, 会调⽤ B 的构造⽅法;B 的构造⽅法中调⽤了 func ⽅法, 此时会触发动态绑定, 会调⽤到 D 中的 func;此时 D 对象⾃⾝还没有构造, 此时 num 处在未初始化的状态, 值为 0;所以在构造函数内,尽量避免使⽤实例⽅法,除了final和private⽅法

结论:"⽤尽量简单的⽅式使对象进⼊可⼯作状态", 尽量不要在构造器中调⽤⽅法(如果这个⽅法被⼦类重写, 就会触发动态绑定, 但是此时⼦类对象还没构造完成), 可能会出现⼀些隐藏的但是⼜极难发现的问题

相关推荐
烤麻辣烫2 小时前
计算机思维--经典互联网应用
开发语言·学习·搜索引擎·数据库开发
xyq20242 小时前
WebForms 数据库连接详解
开发语言
甲方大人请饶命2 小时前
Java-集合进阶
java·开发语言
噗噗123 小时前
基于 Go 语言实现企业大群发任务的平滑限流与多线程漏斗调度器
java·开发语言
fie88893 小时前
基于MATLAB的GPS捕获、跟踪与PVT计算实现
开发语言·matlab
甲方大人请饶命3 小时前
Java-异常、File
java·开发语言
社交怪人3 小时前
【打印菱形】信息学奥赛一本通C语言解法(题号1028)
c语言·开发语言
历程里程碑3 小时前
53 多路转接select
linux·开发语言·数据结构·数据库·c++·sql·排序算法