Java时如何实现多态的(重载和重写)

本文深入探讨了Java的多态性,解释了多态的含义和实现方式,以及多态发生的条件。通过示例展示了如何在代码中实现多态,并对比了重写和重载的区别。多态依赖于继承和方法重写,通过父类引用指向子类对象来实现。同时,文章提到了动态绑定在多态中的关键作用,以及final和static方法与多态的关系。
摘要由CSDN通过智能技术生成

回顾一下Java的四大特性抽象,封装,继承,多态
其中封装是依靠访问修饰符(public,default,protected,private)实现的,继承是依靠关键字extends,那么多态又是依靠什么实现的呢?

什么是多态

多态的概念并不难,并且在实际编码中可以说是最最高频使用率。多态就是使得同一个行为具有多个不同表现形式或形态的能力。举个形象点的例子:对于 “打印” 这个行为,使用彩色打印机 “打印” 出来的效果就是彩色的,而使用黑白打印机 “打印” 出来的效果就是黑白的。我们就称 “打印” 这个行为是多态的,彩色打印效果和黑白打印效果就是 “打印” 这个行为的两个不同的表现形式。
在这里插入图片描述

同一个行为在不同的对象上会产生不同的结果。

多态发生的三个必要条件
看下面这段代码,首先,我们有一个基类 Shape,三个子类,并且都重写了基类的 draw 方法:

class Shape {
    void draw() {}
}
 
class Circle extends Shape {
    void draw() {
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    void draw() {
        System.out.println("Square.draw()");
    }
}
 
class Triangle extends Shape {
    void draw() {
        System.out.println("Triangle.draw()");
    }
}

多态的体现就在下面这几行代码里

Shape circle = new Circle();//画一个圆形
Shape square = new Square();//画一个正方形
Shape triangle = new Triangle();//画一个三角形

是否有些眼熟,上面这三行代码不就是向上转型吗!继承中提到的向上转型,它就是多态的体现。同样的一个 draw 方法,在这三个不同的对象上产生了三种不同的行为,多态在此体现的淋漓尽致。
注意点
这里需要注意的是,当使用多态方式调用方法时,编译器会首先检查父类中是否有该方法,如果没有,则编译错误;如果父类中有该方法,并且被子类重写,就会调用子类的这个方法;如果父类的方法没有被子类重写,就会调用父类的方法。

Shape circle = new Circle();
circle.draw(); // 调用的是 Circle 的 draw,不是Shape的,只有当Circle没有重写draw的时候才会调用Shape

简单来说:当父类引用变量指向子类对象后(多态),只能使用父类已声明的方法,但方法如果被重写会执行子类的方法,如果方法未被重写那么将执行父类的方法。

实现多态的三要素(必要条件)
1)继承
2)重写
3)父类引用指向子类对象:Parent p = new Child();
在这里插入图片描述
多态是如何发生的
那么,多态到底是如何发生的?编译器是如何知道父类 Shape 引用指向的是 Circle 而不是 Triangle 或 Square 呢?
若绑定发生在程序运行前,叫做静态绑定,也称前期绑定。你可能从来没有听说这个术语,因为它是面向过程语言不需选择默认的绑定方式,例如在 C 语言中就只有前期绑定这一种方法调用。

Shape circle = new Circle();
Shape square = new Square();
circle.draw(); 

对于上面的代码,Shape 即引用类型在编译期可知,不会被改变,而 Circle 作为实例对象类型在运行期才可知,可能会发生变化。所以如果使用前期绑定,在运行之前,编译器只知道有一个 Shape 引用,它无法得知究竟会调用哪个方法。
解决方法就是动态绑定 Dynamic Binding,在运行时根据对象的类型自动的进行绑定(JVM类加载),所以动态绑定也称运行时绑定。动态绑定是多态的基础。(自动绑定实例化对象对应的类)
注意:Java 中除了 static 和 final方法(private 方法属于 final 方法)之外,其他所有方法都是动态绑定。这意味着通常情况下,我们不需要判断动态绑定是否会发生,它是自动发生的。

final 不允许方法重写,而多态发生的条件之一就是重写,所以 final 方法会在编译期间就进行绑定,即静态绑定
static 方法是类直接拥有的的,与该类的任何一个对象都无关(该类的所有对象共同维护),所以也是静态绑定

重载和重写
方法的重写 Overriding 和重载 Overloading 都是是 Java 多态性的表现。
1.方法的重写是多态的必要条件,也是父类和子类多态性的表现
其中子类方法和父类方法名字相同,参数类型相同,访问权限子类必须大于等于父类,总结来说,子类方法和父类方法除了方法体不一样,访问权限可以有限制的修改之外,其余都是一样的。

class Shape {
    public void draw() {}
}
 
class Circle extends Shape {
    public void draw() {
        System.out.println("Circle.draw()");
    }
}

但是重写方法的返回类型一定要和父类方法一模一样吗?
答案其实是否定的,首先,我们需要知道方法的名字参数列表称为方法的签名。例如,draw() 和 draw(String) 是两个具有相同名字, 不同签名的方法。如果在子类中定义了一个与超类签名相同的方法, 那么子类中的这个方法就覆盖/重写了超类中的这个相同签名的方法。
但是返回类型不是签名的一部分, 因此,在覆盖/重写方法时, 一定要保证返回类型的兼容性。 允许子类将覆盖方法的返回类型定义为原返回类型的子类型。
假设shape类为

class Shape {
    public Shape draw() {
    	......
    }
}

子类重写draw方法

class Circle extends Shape {
    public Circle draw() {
        ......
    }
}

此时重写的方法返回类型其实和父类方法不同,专业术语来说,这两个 draw 方法具有可协变的返回类型。

2.方法重载并非多态的必要条件,不过可以理解成某个类的多态性的表现
所谓方法重载,就是一个类中定义了多个方法名相同,但是参数的数量或者类型不同。方法的返回类型和访问权限可以任意修改,不以它俩作为方法重载的标志。

class Circle extends Shape {
    public void draw() {
        System.out.println("Circle.draw()");
    }
    
    public void draw(int i) {
        System.out.println("Circle.draw()" + i);
    }
}

在这里插入图片描述
总结一下方法重载和方法重写的区别,其实最简单就是看是否在同一个类

在这里插入图片描述
在这里插入图片描述
既然方法可以重载,那么main方法可以重载吗?
答:只要是方法,都可以重载,但是,如果是作为程序的入口,那么 main 函数只有一种写法,Java 虚拟机在运行的时候只会调用带有参数是 String 数组的那个 main() 方法,而其他重载的写法虚拟机是不认的,只能人为的调用

class Test {
	public static void main(String[] args) {
		main(1);
	}
	public static void main(int i) {
		System.out.println("重载的 main 方法");
	}
}

运行的结果也很简单——“重载的main方法”
可以看出第一个main方法调用了重载后的main方法,打印语句,这也证明了main方法也可以重载,但是程序的入口仍然是String数组的那个main方法
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值