多态的基础语法
java中支持这样的一个语法:父类型的引用允许指向子类型的对象。这样语法被称为向上转型。
学习多条基础语法之前,我们需要普及两个概念:
第一个:向上转型(upcasting) 子----->父(自动类型转换)
第二个:向下转型(downcasting) 父----->子(强制类型转换,需要加强制类型转换符)
注意:以后在工作过程中,和别人聊天的时候,要专业一些,说向上转型和向下转型,不要说自动类型转换,也不要说强制类型转换,因为自动类型转换和强制类型转换是使用在基本数据类型方面的,在引用类型转换这里只有向上和向下转型。
重点注意:java中允许向上转型,也允许向下转型。无论是向上转型还是向下转型,两种类型之间必须有继承关系,没有继承关系编译器会报错。
向上和向下转型.png什么是多态?
多种形态,多种状态。
分析:现在给出Animal父类,给出继承的子类Cat,父类中有一个move()方法,子类也继承了父类的move()方法,父类中move方法输出的东西与子类中输出多东西不同,也就是说子类已经进行方法覆盖。
Animal a2 = new Cat();
a2.move();
分析a2.move();
java程序分为编译阶段和运行阶段。
编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法的时候,回去Animal.class字节码文件中找move()方法,找到了,绑定上move()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定)
运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以move的时候,真正参与move的对象是Cat(),所以运行阶段会动态执行Cat对象的move()方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)
多态表示多种形态:
编译的时候一种形态。
运行的时候另一种形态。
多态指的是:
父类型的引用指向子类型对象。
包括编译阶段和运行阶段。
编译阶段:绑定父类的方法。
运行阶段:动态绑定子类型对象的方法。
多种形态。
java中只有“类名”或者“引用”才能去“点”。
什么时候必须使用向下转型?
不要随便做强制类型转换。
当你需要访问的是子类对象中“特有”的方法。此时必须进行向下转型。
向下转型有风险,当你向下转型去访问子类对象中“特有”的方法的时候,在编译阶段,堆内存实际上创建的对象1与你定义的要去转换的对象2根本不是同一个对象,对象1和对象2之间也没有继承关系,此时会出现一个非常重要,非常经典的异常:
java.lang.ClassCastException:类型转换异常。
java.lang.NullPointerException:空指针异常。这个也非常重要。
instanceof运算符
instanceof(运行阶段动态判断)
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。
第二:instanceof的语法:
(引用 instanceof 类型)
第三:instanceof运算符的运算结果只能是:true/false
第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。
假如(c instanceof Cat)为true表示:
c引用指向的堆内存中的java对象是一个Cat。
假如(c instanceof Cat)为false表示:
c引用指向的堆内存中的java对象不是一个Cat。
程序员要养成一个好习惯:任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof运算符进行判断。(java规范中要求的。)
这样可以很好的避免:ClassCastException(类型转换异常)
多态在开发中的作用
软件在扩展新需求的过程当中,修改的越少越好。修改的越多,你的系统当前的稳定性就越差,位置的风险就越多。
其实这里涉及到一个软件的开发原则:
软件开发原则有七大原则(不属于java,这个开发原则属于整个软件业):
其中有一条最基本的原则:OCP(开闭原则)
开闭原则:对扩展开放(可以额外添加),对修改关闭(最好很少的修改已有程序)。
在软件的扩展过程当中,修改的越少越好。
多态在开发中的作用是:降低程序代耦合度,提高程序的扩展力。
面向抽象编程,不建议面向具体编程。
面向对象的三大特征:封装、继承、多态,一环扣一环。
有了封装,有了这种整体的概念之后。
对象和对象之间产生了继承。
有了继承之后,才有了方法的覆盖和多态
没有多态机制,方法覆盖也可以没有,如果父类的方法无法满足子类业务需求的时候,子类完全可以定义一个全新的方法。
方法覆盖和多态不能分开。
学习了多态机制之后:
“相同的返回值类型”可以修改一下么?
对于返回值类型是基本数据类型来说,必须一致。
对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小。(但意义不大,实际开发中不会这么写。)
super
super是一个关键字,全部小写。
super和this对比学习:
this:
this能出现在实例方法和构造方法中。
this的语法是:“this.”、“this()”。
this不能使用在静态方法中。
this.大部分情况下是可以省略的。
this.什么时候不能省略呢?在区分局部变量和实例变量的时候不能省略。
this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中其他的构造方法,目的是:代码复用
super:
super能出现在实例方法和构造方法中。
super的语法是:“super.”、“super()”。
super不能使用在静态方法中。
super.大部分情况下是可以省略的。
super.什么时候不能省略呢?
super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。
super()表示通过子类的构造方法调用父类的构造方法。
模拟现实世界中的这种场景:要想有儿子,需要先有父亲。
重要结论:当一个构造方法第一行:既没有this(),又没有super()的话,默认会有一个super();表示通过当前子类的构造方法调用父类的无参数构造方法。所以必须保证父类的无参数构造方法是存在的。
注意:this()和super()不能共存,它们都是只能出现在构造方法第一行。
无论是这样折腾,父类的构造方法是一定会执行的。(百分百的)
在java语言中不管是new什么对象,最后老祖宗的Object类的无参数构造方法一定会执行。(Object类中的无参数构造方法是处于“栈顶部”)
栈顶的特点:最后调用,但是最先执行结束。后进先出原则。
以后写代码的时候,一个类的无参数构造方法还是建议手动的写出来,如果无参数构造方法丢失的话,可能会影响到“子类对象的构建”。
我们可以在恰当的时间使用:super(实际参数列表);
当父类中的属性或者是方法被修饰,在其他类中不能被访问,这时,我不得不访问他,此时就可以采用super();来调用存储这些私有属性的构造方法。
super代表的是“当前对象(this)”的“父类型特征”。
注意:在构造方法执行过程中一连串调用了父类的构造方法,父类的构造方法又继续向下调用它的父类的构造方法,但是实际上对象只创建了一个。
super(实参)的作用是:初始化当前对象的父类型特征。并不是创建新对象。实际上对象只创建了1个。
super关键字代表队就是“当前对象”的那部分父类型特征。
例如:我继承了我父亲的一部分特征:眼睛、皮肤等。
super代表的就是“眼睛、皮肤等”。
“眼睛、皮肤等”虽然是继承了父亲的,但这部分是在我身上的。
super.什么时候不能省略?
父类和子类中有同名属性,或者说有相同的方法,如果想要通过子类中访问父类中的数据,super.不能省略
父中有,子中有,想要在子中访问父的东西,super.不能省略
super不是引用。super也不保存内存地址,super也不指向任何对象。super只是代表当前对象内部的那一块父类型的特征。
super. 不仅可以访问属性,也可以访问方法。
在子类中访问父类私有的数据,你使用super.是没有权限的。
私有的只能在本类中访问。
super的使用:
super.属性名 【访问父类的属性】
super.方法名(实参) 【访问父类的方法】
super(实参) 【访问父类的构造方法】
网友评论