Java中的多态

1.1 向上转型

对象即可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种把对某个对象的引用视为对其基类型的引用的做法被称为向上转型。但是向上转型时,每个类在调用方法时都会使用父类的对象,而忘记了自己的对象类型。

在上述问题中,我们是否要将会进行向上转型的方法中的参数直接使用成其本身类型的参数呢?这样或许会更加直观。但是那样又会出现一些新的问题:比如说我们要在Instrument(乐器)类型中的每一个子类都编写一个tune()方法,这时当我们以后要在Instrument中添加新的类型时,我们将需要非常多的编程工作,这显然是十分麻烦的。

如果只写这样一个简单方法,它仅接收基类作为参数,而不是那些特殊的导出类。这样情况会变得更好吗?也就是说,如果不管导出类的存在,编写的代码只是与基类打交道,会不会更好?这正是多态所允许的。

多态即是允许一个方法只接收基类作为参数,然后基类的导出类可以通过向上转型方便的将自己的对象当成参数传入该方法中。

1.2 方法调用绑定

说到这里,其实上我们在数据进行向上转型后,对于在方法中如何判断其原本是哪一个对象类型这一问题还没有解决。为了解决这一问题我们将学习方法调用绑定:

将一个方法调用同一个方法主题关联起来被称为绑定。若在程序执行前进行绑定(由编译器和连接程序实现),叫做前期绑定。一个非面向对象编程的编译器产生的函数调用就是使用前期绑定(编译器将产生对一个具体函数名字的调用)。Java中无法使用前期绑定,因为当编译器只有一个Instrument引用时,它无法知道究竟调用哪个方法才对。

而解决这个问题的方法就是后期绑定,它的含义就是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定运行时绑定。编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定根据不同的编程语言有所不同,但是,不管怎样都必须在对象中安置某种 “类型信息”

Java中所有的方法都是通过动态绑定来实现多态,我们就可以编写只与基类打交道的程序代码了,并且这些代码对所有的导出类都可以正确运行。换句话说,发送消息给某个对象,让该对象取断定应该做什么事

class Shape{

public void draw(){};

public void erase(){};

}

class Circle extends Shape{

public void draw(){

System.out.println(“Circle.draw()”);

};

public void erase(){

System.out.println(“Circle.erase()”);

};

}

class Square extends Shape{

public void draw(){

System.out.println(“Square.draw()”);

};

public void erase(){

System.out.println(“Square.erase()”);

};

}

class Triangle extends Shape{

public void draw(){

System.out.println(“Triangle.draw()”);

};

public void erase(){

System.out.println(“Triangle.erase()”);

};

}

public class Shapes {

public static void main(String[] args) {

Shape shape1=new Circle();

Shape shape2=new Square();

Shape shape3=new Triangle();

shape1.draw();

shape2.draw();

shape3.draw();

}

}

Output:

Circle.draw()

Square.draw()

Triangle.draw()

在“几何形状”例子中,有一个基类Shape,以及多个导出类–如Circle、Square、Triangle等。下面的继承图展示它们之间的关系:

在这里插入图片描述

这里,创建了一个Circle对象,并把得到的引用立即赋值给Shape,这样做看似错误(将一种类型赋值给另外一种类型);但实际上是没问题的,因为通过继承,Circle就是一种Shape。因此,编译器认可这条语句。

1.3 缺陷

在“覆盖”私有方时,只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象。这时虽然编译器不会报错,但是也不会按照我们所期望的来执行。确切地说,在导出类中,对于基类中的private方法,最好采用不同的名字。

域与静态方法不具有多态性;域是在编译期进行解析的;静态方法与类相关联,而非与单个的对象相关联。当使用向上转型调用域属性,会调用父类属性;当使用向上转型调用static方法时,会调用父类方法;调用非static方法时,是调用子类方法的,也就是多态性。

构造器和多态


构造器不同于其他种类的方法,涉及到多态时仍是如此;尽管构造器并不具有多态性(它们实际是static方法,只不过该static声明是隐式的);但是,构造器可通过多态在复杂的层次结构中运作;

2.1 构造器的调用顺序

基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊任务:检查对象是否被正确地构造;导出类只能给你访问它自己的成员,不能访问基类中的成员。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化;因此,必须令所有构造器都得到调用,否则就不可能正确构造完整对象

class Meal{

Meal(){

System.out.print(“Meal()->”);

}

}

class Bread{

Bread(){

System.out.print(“Bread()->”);

}

}

class Cheese{

Cheese(){

System.out.print(“Cheese()->”);

}

}

class Lettuce{

Lettuce(){

System.out.print(“Lettuce()->”);

}

}

class Lunch extends Meal{

Lunch(){

System.out.print(“Lunch()->”);

}

}

class PortableLunch extends Lunch{

PortableLunch(){

System.out.print(“PortableLunch()->”);

}

}

public class Sandwich extends PortableLunch{

private Bread b=new Bread();

private Cheese c=new Cheese();

private Lettuce l=new Lettuce();

public Sandwich(){

System.out.print(“Sandwich()->”);

}

public static void main(String[] args) {

new Sandwich();

}

}

Output:

Meal()->Lunch()->PortableLunch()->Bread()->Cheese()->Lettuce()->Sandwich()->

这个例子展示组合、继承以及多态在构建顺序上的作用:

在这里,用其他类创建了一个复杂的类,而且每个类都有一个声明它自己的构造器。其中最重要的Sandwich,它反映了三层继承以及三个成员对象。当在 main()里创建了一个Sandwich对象后,就可以看到输出结果。这里可以反映复杂对象调用构造器的顺序:

  • 1.调用基类构造器;这个步骤会不断地反复递归下去,从构造这种层次结果的根,直至最底层的导出类。

  • 2.按声明顺序调用成员的初始化方法;(每次进入一个父类的时候,在调用构造器之前,会先初始化成员方法或域)

  • 3.调用导出类构造器的主体

构造器的调用顺序是很重要的。当进行继承时,我们已经知道基类的一切,并且可以访问基类中任何声明为public和protected的成员。这意味着在导出类中,必须假定基类的所有成员都是有效的。

2.2 构造器内部的多态方法的行为

构造器调用的层次结构带来了一个有趣的两难问题。如果在构造器的内部调用正在构造的对象的某个动态绑定方法,那会发生什么情况呢?

在一般的方法内部,动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的那个类,还是属于那个类的导出类。如果要调用构造器内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义。然而,这个调用的效果可能相当难于预料,因为被覆盖的方法在对象被完全构造之前就会被调用

class Glyph{

void draw(){

System.out.println(“Glyph.draw()”);

}

Glyph() {

System.out.println(“Glyph() before”);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Java面试核心知识点笔记

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

Java中高级面试高频考点整理

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

最后分享Java进阶学习及面试必备的视频教学

蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
中…(img-lKOI8NN3-1712601612593)]

Java中高级面试高频考点整理

[外链图片转存中…(img-NnnwMZQk-1712601612593)]

[外链图片转存中…(img-TyBeuZdM-1712601612593)]

最后分享Java进阶学习及面试必备的视频教学

[外链图片转存中…(img-ZSTuQ1mE-1712601612593)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值