文档章节

java基础---->多态性

小强斋太
 小强斋太
发布于 2016/11/09 20:07
字数 1458
阅读 12
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

多态性 

在面向对象中多态性实际上是面向对象里的一个最大的最有用的特点,对于多态性在java中有两种体现:

   1、  方法的重载及覆写

   2、  对象多态性:指的是父类对象和子类对象之间的转型操作

一、对象多态性:

继承允许将对象视为它本身或者它的父类的类型来处理,即将继承自同一父类的基类可以视为同一类处理,一份代码就可以毫无差别的运行在这些不同类型上。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要它们是从同一基类导出的。虽然这些方法都通过基类调用。

实现原理:后期绑定:在运行时根据对象的类型进行绑定。后期绑定也称为多态绑定或者运行时绑定。

Java中除了static方法和final方法(private方法属于final方法),其他的方法都是后期绑定。

只有普通方法调用时多态的。

对象多态性的例子

基类,形状类 Shape.java

public class Shape {
  public void draw() {}
  public void erase() {}
}

子类1,圆形 Circle.java

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

子类2,三角形 Triangle.java

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

子类3,正方形 Square.java

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

多态性体现

public class Shapes {

	public static void main(String[] args) {
		Shape shape1 = new Circle();
		Shape shape2 = new Triangle();
		Shape shape3 = new Square();

		shape1.draw();
		shape2.draw();
		shape3.draw();
	}
} 
/* Output:
Circle.draw()
Triangle.draw()
Square.draw()
*/

二、多态性理解:

 2.1只有非私有方法才可以被覆盖

“覆盖”私有方法:

class PrivateOverride {

    private void f() {

       System.out.println("privatef()");

    }

}

 

public class Derived extends PrivateOverride {

 

    public void f() {

       System.out.println("publicf()");

    }

 

    public static void main(String[] args) {

       PrivateOverride po = new Derived();

       po.f();//异常The method f() from the type PrivateOverride is not visible

    }

 

}


例子2:覆盖私有方法的现象,编译器不报错,但是不会按照我们期望的执行。

public class PrivateOverride {

    private void f() {

       System.out.println("private f()");

    }

 

    public static void main(String[] args) {

       PrivateOverride po = new Derived();

       po.f();

    }

}

 

class Derived extends PrivateOverride {

    public void f() {

       System.out.println("public f()");

    }

}

/*

 * Output: private f()

 */

2.2、父类和子类有相同的属性,且为public的情况疑问

只有普通方法的调用时多态的,如果访问某一个成员变量,该成员变量是public的,且父类子类都有这个属性,它的访问是在编译期进行解析。

虽然发生向上转型,Super sup = new Sub();sup.field就没有多态性,访问的是父类的field。

class Super {
	public int field = 0;

	public int getField() {
		return field;
	}
}

class Sub extends Super {
	public int field = 1;

	public int getField() {
		return field;
	}

	public int getSuperField() {
		return super.field;
	}
}

public class FieldAccess {
	public static void main(String[] args) {
		Super sup = new Sub(); // Upcast
		System.out.println("sup.field = " + sup.field + ", sup.getField() = "
				+ sup.getField());
		Sub sub = new Sub();
		System.out.println("sub.field = " + sub.field + ", sub.getField() = "
				+ sub.getField() + ", sub.getSuperField() = "
				+ sub.getSuperField());
	}
} 
/* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*/

2.3静态方法不具有多态性

如果某一个方法是静态的,它的行为就不具有多态性。

class StaticSuper {
	public static String staticGet() {
		return "Base staticGet()";
	}

	public String dynamicGet() {
		return "Base dynamicGet()";
	}
}

class StaticSub extends StaticSuper {
	public static String staticGet() {
		return "Derived staticGet()";
	}

	public String dynamicGet() {
		return "Derived dynamicGet()";
	}
}

public class StaticPolymorphism {
	public static void main(String[] args) {
		StaticSuper sup = new StaticSub(); // Upcast
		System.out.println(sup.staticGet());//静态方法,调用子类的
		System.out.println(sup.dynamicGet());//非静态方法
	}
}
/*
 * Output: Base staticGet() Derived dynamicGet()
 */

三、构造器内部调用的方法被子类覆盖的情况

构造器内部调用的方法被子类覆盖了。这个调用的效果很难预料,因为被覆盖的方法在对象被完全构造之前就会调用。这一定会造成一些难以发现的错误。

class Glyph {
	void draw() {
		System.out.println("Glyph.draw()");
	}

	Glyph() {
		System.out.println("Glyph() before draw()");
		draw();
		System.out.println("Glyph() after draw()");
	}
}

class RoundGlyph extends Glyph {
	private int radius = 1;

	RoundGlyph(int r) {
		radius = r;
		System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
	}

	void draw() {
		System.out.println("RoundGlyph.draw(), radius = " + radius);
	}
}

public class PolyConstructors {
	public static void main(String[] args) {
		new RoundGlyph(5);
	}
} 
/* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/

输出结果显示当Glyph的构造器调用draw()方法时候,radius不是默认初始值1而是0。

仔细分析这段代码,可以发现

1.在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。

2.如前所述那样调用基类构造器,此时,调用被覆盖后的draw()方法(注意,要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0

3.按照声明的顺序调用成员的初始化方法。

4调用导出类的构造器主体

这样有一个优点,至少保证所有的东西初始化为零或者null,而不仅仅是垃圾。其中通过‘组合’而嵌入到一个类内部的对象引用,其值是null,如果忘记为该值进行初始化,均会在运行时候出现异常。查看结果时候,发现其他所有的东西都是0.

结论:编写构造器的准则

用尽可能简单的方法使对象进入正常状态,可以的话,避免调用其他的方法。构造器内唯一能够安全调用的方法是基类中的final方法(也适用于private,它们自动属于final方法),这些方法不能被覆盖,因此也不会出现上述令人惊讶的问题。

四、向上转型

发生向上转型后,子类中自己的定义的操作是无法通过父类对象找到的。

class Useful {
	public void f() {}
	public void g() {}
}

class MoreUseful extends Useful {

	public void f() {}
	public void g() {}
	public void u() {}
	public void v() {}
	public void w() {}

}

public class RTTI {
	public static void main(String[] args) {
		Useful[] x = { new Useful(), new MoreUseful() };
		x[0].f();
		x[1].g();
//		x[1].u();// 编译错误 The method u() is undefined for the type Useful
		((MoreUseful) x[1]).u(); // 
		((MoreUseful) x[0]).u(); // 必须现有upCast才能有downCast,错误的向下转型,java.lang.ClassCastException
	}

}
小强斋太
粉丝 0
博文 181
码字总数 0
作品 0
广州
私信 提问
加载中
请先登录后再评论。
浅入浅出Android(003):使用TextView类构造文本控件

基础: TextView是无法供编辑的。 当我们新建一个项目MyTextView时候,默认的布局(/res/layout/activity_main.xml)中已经有了一个TextView: <TextView 运行效果如下: 修改其文本内容...

樂天
2014/03/22
718
1
程序猿媛一:Android滑动翻页+区域点击事件

滑动翻页+区域点击事件 ViewPager+GrideView 声明:博文为原创,文章内容为,效果展示,思路阐述,及代码片段。文尾附注源码获取途径。 转载请保留原文出处“http://my.oschina.net/gluoyer...

花佟林雨月
2013/11/09
4.3K
1
研究虚拟机--Jikes RVM

Jikes研究虚拟机(Jikes Research Virtual Machine,简称Jikes RVM)是一种成熟的用于执行Java程序的虚拟机,其早期版本与当前版本分别在通用公共许可证(CPL)与Eclipse公共许可证(EPL)下开...

匿名
2013/02/13
1.2K
0
集群存储系统--YFS

YFS集群存储系统由多个元数据服务器(MDS)、多个块数据服务器(CDS)和多个客户端(client)互联组成集群; 数据被分成64M固定大小的数据块(Chunk),每个数据块在CDS本地以常规文件的形式...

匿名
2013/02/19
1.8K
0
WebUI自动化测试框架--Dagger

Dagger是网易杭州研究院QA团队开发的一个轻量级、运行稳定的WebUI自动化测试框架,主要基于Selenium及TestNg可以认为是对Selenium进行二次封装的一个框架(俗称 造轮子 )。之所以把这个轮子...

ChenKan
2013/03/05
2.8W
6

没有更多内容

加载失败,请刷新页面

加载更多

Hacker News 简讯 2020-08-15

最后更新时间: 2020-08-15 03:01 Welders set off Beirut blast while securing explosives - (maritime-executive.com) 焊工在固定炸药的同时引爆了贝鲁特爆炸 得分:144 | 评论:132 Factor......

FalconChen
今天
24
0
OSChina 周六乱弹 —— 老椅小猫秋乡梦 梦里石台堆小鱼

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @小小编辑 :《MOM》- 蜡笔小心 《MOM》- 蜡笔小心 手机党少年们想听歌,请使劲儿戳(这里) @狄工 :腾讯又在裁员了,35岁以上清退,抖音看到...

小小编辑
今天
61
1
构建高性能队列,你不得不知道的底层知识!

前言 本文收录于专辑:http://dwz.win/HjK,点击解锁更多数据结构与算法的知识。 你好,我是彤哥。 上一节,我们一起学习了如何将递归改写为非递归,其中,用到的数据结构主要是栈。 栈和队列...

彤哥读源码
今天
17
0
Anaconda下安装keras和tensorflow

Anaconda下安装keras和tensorflow 一、下载并安装Anaconda: Anaconda下载 安装步骤: 如果是多用户操作系统选择All Users,单用户选择Just Me 选择合适的安装路径 然后勾选这个,自动配置环境...

Atlantis-Brook
今天
7
0
滴滴ElasticSearch千万级TPS写入性能翻倍技术剖析

桔妹导读:滴滴ElasticSearch平台承接了公司内部所有使用ElasticSearch的业务,包括核心搜索、RDS从库、日志检索、安全数据分析、指标数据分析等等。平台规模达到了3000+节点,5PB 的数据存储...

滴滴技术
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部