Java多态-创新互联

多态

多态相对于封装和继承这两个名词就不是那么生活化了。这个词最早是一个生物的专业用词,它的指的是同一个物种在不同的环境当中可能出现的多样化表现。那么衍生到了面向对象编程中,它的含义呢,我们用一句经典来话来描述:相同的行为,不同的实现。

成都创新互联公司是一家专注于网站设计制作、成都网站建设与策划设计,藤县网站建设哪家好?成都创新互联公司做网站,专注于网站建设十载,网设计领域的专业建站公司;建站业务涵盖:藤县等地区。藤县做网站价格咨询:18982081108

1、多态指的是行为的多样性,没有属性多态这一说;

2、相同的行为指的是同名方法,也就是说方法名相同,我们就认为这是相同的行为。 由此,我们前面学习的方法重载和方法重写其实都是多态的表现。

最近面试看到的:属性能不能重写?1、首先这个说法不专业,重写特指的是方法,但是它要描述的意思其实我们懂,那就是父类中定义了一个属性,子类能不能再定义一个同名属性把它给覆盖掉;

2、在语法上,这是可以的,不会报错。但是子类定义的属性是不会把父类定义的属性给覆盖掉;而是两个属性同时存在于对象身上,父类定义的存在于子类对象的父类对象部分(上半截),子类定义的存在于子类对象的子类特有部分(下半截)。那么,如何区分呢?用this.访问子类定义的该属性;用super.访问父类定义的该属性。

3、在实际场景中,这种设计是没有意义的。因为父类定义了一个属性,子类通过继承自动拥有了这个属性,那么子类再去定义一个一摸一样的属性是没有任何意义的,属于重复劳动。这是属于子类的设计人员没有理解父类设计的意图。

多态的意义

封装是面向对象的基础; 继承是在封装的基础上,实现代码的复用性; 多态考量的是代码的丰富度。

多态分为两种: 1、静态多态;2、动态多态。

这里的静态和我们前面学习的static关键字没有关系,它描述的是:在编译期既能确定某个多态方法的具体执行效果; 这里的动态多态是指在运行期,根据绑定对象的不同,才能确定多态方法执行的效果。

我们在前面学过的方法的重载和方法的重写都是属于静态多态。静态多态虽然有丰富度的体现,但是是不够的,因为它在编译期就把执行效果固定下来了。而我们以后用得更多的是动态多态。

动态多态是由两个技术合并使用才有的效果:动态绑定技术 + 方法的重写。

动态绑定技术

首先我们从现象上来看一个效果: 本类引用 指向 本类对象; 父类引用 指向 子类对象;

在Java当中只有这两种情况,也就是说赋值符号左右两端如果类型不一致,那么只有后面这种情况。

从内存上解释

为什么父类引用可以指向子类对象呢? 既然是父子关系,还是在做继承,那么还是跟内存叠加有关系。每一个子类对象的上半截都是一个完整的父类对象部分,当父类引用指过去的时候,是能够看到完整的父类对象信息的。

没有继承关系的类,不能保证A类引用指到B类对象,能看到B类对象中有A类定义的内容,所以不允许!

子类引用指向父类对象的时候,父类对象里面没有子类特有部分,所以缺失了内容,也不允许!

从场景上解释

一个对象属于子类,一定也属于它的父类。子和父是is-a关系,这是说得通的。

但是一个对象属于父类,你能保证它一定属于子类吗?

而没有继承关系的,那就更说不通的了,比如:指鹿为马。 明明是一个鹿的对象,你偏要用马的引用去指向它,肯定是不对的。你可以说它是动物、可以说它是宠物、因为鹿is-a动物;鹿 is-a 宠物;都是可行的,但鹿 is not a 马。

动态体现在哪儿?

如果我们手上现在拥有一个父类引用,那么我们就不能确定这个引用到底是指向哪一个具体的对象的了。

它既可能指向自己类型的对象,也可能指向自己的各种子类对象。而自己的某个方法,是可以被子类重写成不同的实现效果的。那么用这个引用执行重写方法,也就不能确定到底重写前还是重写后,是A子类重写后,还是其它子类重写后的效果了。只有等程序运行起来以后,根据该引用具体绑定的对象到底是谁,才能够运行出具体的效果。--- 这就是动态的体现。

语法细节

转型技术

赋值符号左右两端数据类型不一致,就会发生数据类型转换。 父类引用 = 子类对象 也是两端数据类型不一致,所以也属于转型技术。

我们用我们前面学习的基本数据类型转型技术去推衍现在的引用数据类型的转型技术。

基本数据类型转型技术

1、前提:不是所有基本数据类型之间都能够进行转型,boolean不参与的;

2、自动类型转换 小类型值 赋值给 大类型的变量强调:这里的大小 是 数据类型能表示的范围的大小,而不是空间的大小。

int num = 'A'; float f = num; double d = 100;

效果:不需要使用任何特殊语法,直接完成类型转换并且赋值成功。

3、强制类型转换 大类型的值 赋值给 小类型的变量

效果: 3-1、直接书写后,编译报错; 3-2、使用(目标类型)的强转语法,编译才能够通过; 3-3、运行的效果是有风险性的,精度有可能丢失。

引用数据类型转型技术

1、前提:不是所有引用数据类型之间都能够做转型,只有有继承关系的类型之间才可以;

2、自动类型转换 子类对象 赋值给 父类引用 子类表示的范围 是小于 父类表示的范围,所以仍然是把小的数据值交给大的变量。与基本数据类型的自动转换要求是一摸一样的。

只是说它有另一个特殊的名字,又叫做“向上转型”而已。因为在继承树上,父类在上,子类在下;

效果:不需要使用任何特殊语法,直接完成类型转换并且赋值成功。

3、强制类型转换 大类型的值 赋值给 小类型的引用

又被称为"向下转型".

效果: 1、直接书写后,编译报错; 2、使用(目标类型)的强转语法,编译才能够通过; 3、运行的效果是有风险性的,风险性是运行时报错(ClassCastException),中断程序的执行。

对风险性探讨:1、我们发现并不是每次强转都一定成功,那么情况时如何的呢? 要想强转以后,编译通过且运行也通过,只有一种情况,那就是:最终的内存结果只能是本类引用指向本类对象。

2、强转语法只是在编译期进行一个强制性的语法说明,说明什么呢?告诉编译器,我这个父类引用确实是指向的某种子类对象。

3、但是运行起来以后,如果满足你的强制说明,那么没有问题运行成功;如果不满足你的强制说明,那么就会运行失败,报出ClassCastException。

强调:强转语法不是把A类对象变成B对象,只是在编译期达成的一个口头约束而已。

父类引用指向子类对象,我们能访问什么呢?

1、父类引用只能看到子类对象从父类继承而来的属性和行为,当然要受访问修饰符限制;注意:子类对象身上是有子类特有属性和行为的,只是站在父类引用的角度上看不到而已。

2、要想看到,要把引用换成子类引用,这个时候要使用强转语法,同时还必须保证运行起来以后真正给出的对象是符合强转语法的。

3、特例是在父类中定义,被子类重写的行为。由于这个行为是定义在父类中,所以父类引用能看到;而对象又是子类对象,所以执行的效果是子类重写后的效果。

练习:书写一个宠物类Pet,拥有一个叫的行为;书写Pet的两个子类Dog和Cat,分别实现叫的行为。再书写一个主人类Master,拥有一个宠物对象,主人有一个行为是喂养feed,在该方法中调用自己宠物叫的行为。最后,在main方法中测试。

instanceof

在练习当中,我们看到在有的场景里面,确实会存在需要强转的情况。但是,强转又有风险度,所以,我们需要先判断类型是否匹配,这就要用到instanceof了。

instanceof是一个关键字,也是一个运算符。它是专门用来判断一个对象是否属于某个类型的,运算后的结果是boolean型结果。

语法: 对象 instanceof 类型

instanceof是专门用来规避强转带来的ClassCastException风险的,类似于非空判断专门用来解决NullPointerException风险的。

多态的应用

多态参数

当我们在设计参数的时候,把参数的类型设计为父类类型,那么所有的子类对象都能够通过这个参数,传递到这个方法里面去。

​
public void cure(角色 r){
​
    if(r instanceof 士兵){
    
    }else if(.....){
    
    }
​
}
​

这样的多态应用带来了面向对象设计中非常重要的一个原则:开闭原则。

开闭原则:Software should be opened for extension, but closed for modification。

软件对于扩展来说应该是开放的,对于修改来说应该是关闭。

也就是说好的软件设计,应该能够可以随着业务的扩展去增加新的功能,但是不应该修改已有的功能。

多态集合

int[] array = new int[10];

Object[] arrayObj = new Object[10];

可以解决数组只能存放同一数据类型元素的问题。

补一下 switch的新语法 var关键字

Java语言和JavaScript语言有一个很重要的区别: Java语言是强类型的编程语言;而JS是弱类型的。

int num = 10;
String msg = "hello";
var num = 10;
var name = "zhang3";
var pi = 3.14;
​
num = "hello";
​

在15年左右的时候,Java想去学习JS的这种声明变量的简便性,所以它也引入了var关键字来声明变量。

但问题是,Java本身是强类型的,引入了以后又不能改变它的强类型本质,所以导致这个关键字不伦不类有很多限制。

1、必须要对var声明的变量马上赋值,因为编译器是根据这个赋的值的类型才能判定这个变量是什么数据类型的;

2、一旦赋值成功后,这个变量的类型就被确定了,后面也不能更改,也不能存放其它类型的数据值;

3、var声明的变量只能是局部变量,不能声明为属性的类型,也不能声明为参数的类型。

4、所以在java中这个var变量的使用就没有啥意义了。 唯一的意义可能仅仅在于如果一个局部变量的类型名很长的时候,可以帮助我们少写点代码。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站名称:Java多态-创新互联
浏览路径:http://ybzwz.com/article/jgpje.html