hi,大家好,今天为大家带来多态的讲解
💚💚💚💚1.对于引用的理解
💚💚💚💚2.多态的定义
💚💚💚💚3.多态的实现条件
💚💚💚💚4.重写
💚💚💚💚5.向上转型
💚💚💚💚6.动态绑定
💚💚💚💚7.静态绑定
💚💚💚💚8.向下转型
💚💚💚💚9.多态优缺点
💚💚💚💚9.重写的小点
今天的知识比较多,也会有代码帮助理解
1.为了加深对引用的理解,用一个swap交换函数来帮助理解
class MyValue {
public int value;
}
public class TestDemo {
public static void swap(MyValue my1,MyValue my2){
int tmp= my1.value;
my1.value=my2.value;
my2.value=tmp;
}
public static void main(String[] args) {
MyValue myValue1=new MyValue();
MyValue myValue2=new MyValue();
myValue1.value =20;
myValue2.value=30;
swap(myValue1,myValue2);
System.out.println( myValue1.value);
System.out.println(myValue2.value);
}
}
运行结果如下
这个代码实现了交换,下面来画图理解一下引用
创建变量,在栈上开辟栈帧,new对象时在堆上
2 什么是多态呢???
多态就是多种形态,完成某个行为,最后的结果不尽相同。这个概念比较抽象,下面来举一个例子,相同的食材,给两个不同的人做,两个人做出来的味道是不一样的,比如做番茄炒蛋,有甜口的,还有咸口的,那么这就可以叫做多态,土豆可以做成炸土豆条,炸土豆片,这也是多态
3.多态的实现条件
实现多态需要三个条件
1.继承
2.重写
3.通过父类引用调用重写的方法
继承在上一篇已经介绍过了,我们现在直接说重写,说到重写,就得说一说重写的条件了
4.重写
1.方法名,返回值,参数列表必须完全一致,返回类型可一样也可不一样,但必须具有父子关系
2.重写又可以称作覆盖,是子类对父类的非静态,非构造,非fianl修饰,非private修饰的方法的重写
3.重写的方法访问修饰符的访问权限要大于等于父类的
4.父类被static、private修饰的方法、构造方法都不能被重写。
5.重写的时候,加上一个@Override注解,帮助我们检查重写的番薯方法是否有问题
下面来一段代码
6.被final 修饰的方法不能被重写,称作密封方法
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
}
class Dog extends Animal{
public void wangwang(){
System.out.println(name+"正在汪汪叫");
}
@Override
public void eat(){
System.out.println(name+"正在吃狗粮");
}
}
class Bird extends Animal{
public String wing;
public void fly(){
System.out.println(name+"正在飞");
}
public void eat(){
System.out.println(name+"正在吃鸟粮");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Animal animal1=new Dog();
animal1.name="贝贝";
animal1.eat();
Animal animal2=new Bird();
animal2.name="喳喳";
animal2.eat();
}
}
在这段代码中我们可以看到子类重写了父类的eat方法,那么疑问就来了,我们看到引用是父类引用,那么运行结果应该是父类的name,但是父类并没有起名字,所以是null+正在吃饭,那么这就要提到向上转型和动态绑定了
5.向上转型
在重写过程中,我们需要用到动态绑定,因为在第二点提到了是实现多态的第三点是,通过父类引用去调用重写的方法,所以要用到向上转型,向上转型就是从子类到父类,父类的引用指向子类的对象
如蓝笔画出来的那个一样,就是向上转型,父类引用指向子类对象
6.动态绑定
什么叫做动态绑定
就是在代码运行的时候很智能的帮我们调用子类中重写的父类方法,在编译的时候它并不能识别出调用哪个方法,但是在运行的时候就可以了,就像上面的代码运行结果一样,new 的是谁,就会调用哪个子类的重写方法
7.静态绑定
与动态绑定相对应得就是静态绑定,静态绑定又叫做早绑定,一般用于重载中,在编译的时候就知道应该执行哪一个方法,当然,在多态中一般是不用得,但是也能用,但是它不安全,一般不敢用,下面来写举个例子
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
}
class Dog extends Animal{
public void wangwang(){
System.out.println(name+"正在汪汪叫");
}
@Override
public void eat(){
System.out.println(name+"正在吃狗粮");
}
}
class Bird extends Animal{
public String wing;
public void fly(){
System.out.println(name+"正在飞");
}
@Override
public void eat(){
System.out.println(name+"正在吃鸟粮");
}
}
class Cat extends Animal{
public void miaomiao(){
System.out.println(name+"正在喵喵叫");
}
@Override
public void eat() {
System.out.println(name+"正在吃猫粮");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Animal animal=new Dog();
Dog dog=(Dog)animal;
dog.name="贝贝";
dog.eat();
dog.wangwang();
System.out.println("=======");
Cat cat=(Cat)animal;
cat.eat();
cat.miaomiao();
}
public static void mai1(String[] args) {
Animal animal1=new Dog();
animal1.name="贝贝";
animal1.eat();
Animal animal2=new Bird();
animal2.name="喳喳";
animal2.eat();
Animal animal3=new Cat();
animal3.name="小花";
animal3.eat();
}
}
这个就是向下转型,很不安全,如下图,子类引用指向父类对象,报错了
但是有方法防止报错,使用库提供的instanceof函数,,现在对这个代码进行改良
这句话的意思是判断子类对象指向的引用是否包含Cat类,这样就不会报错了,但我们还是不提倡这样写
9.多态的优缺点
优点:能够降低代码的 "圈复杂度", 避免使用大量的 if - else
多态缺点
父类的属性不能重写,父类的构造方法不能重写
10.重写的小点
避免在构造方法中调用重写的方法
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
public D () {
super();
}
@Override
public void func() {
//System.out.println("fafdadssa!!!!!");
System.out.println("D.func() " + num+" 因为父类此时还没有走完!");
}
}
public class Test4 {
public static void main(String[] args) {
D d = new D();
}
}
当大家看到这段代码的时候大家觉得结果是几,一定有人以为是1,大漏特漏!!!
运行他看看
看,结果是0,为啥呢,因为父类都没有构造完成,所以不能到子类那一步,不要写这种代码,有坑,大家避雷!!!
下面写一个图形类的代码加深大家对多态的理解
class Shape {
public void draw() {
System.out.println("画图形!");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("画矩形!");
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("画圆!");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("❀!");
}
}
public class Test3 {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void drawMap() {
Rect rect = new Rect();
Cycle cycle = new Cycle();
Flower flower = new Flower();
Shape[] shapes = {cycle,rect,cycle,rect,flower};
//int[] array = {1,2,3,4};
for(Shape shape : shapes) {
shape.draw();
}
}
/* public static void drawMap3() {
Shape rect = new Rect();
Shape cycle = new Cycle();
Shape flower = new Flower();
Shape[] shapes = {cycle,rect,cycle,rect,flower};//这里完成了向上转型!!!,和上面的写法其实是一样的
//int[] array = {1,2,3,4};
for(Shape shape : shapes) {//for each 遍历
shape.draw();
}
}
*/
public static void main(String[] args) {
drawMap();
//drawMap2() ;
/*Rect rect = new Rect();
Cycle cycle = new Cycle();
drawMap(rect);
drawMap(cycle);
drawMap(new Flower());*/
}
}
向上转型有三种方式
1.直接赋值
2.方法传参
3.方法返回值
显然上述代码用的是方法传参
好了,今天的讲解就到此结束,下一期为大家带来抽象类和接口!!!
886!!!🎉🎉🎉👀👀👀
备注:抽象类小部分知识在11.13日笔记