- 浏览: 117646 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (130)
- JUnit4学习 (0)
- Spring3.X学习 (2)
- 日记 (1)
- 文学类 (2)
- Java (15)
- Thingking In Java (11)
- org.apache.poi (4)
- XML (2)
- Log4j (1)
- Jar包收集 (2)
- ExtJs (1)
- 汇编语言 (11)
- 开发工具 (0)
- 电子书 (2)
- Oracle (6)
- Ajax (1)
- Jquery (2)
- myBatis (1)
- Spring2.5学习 (6)
- Tomcat (1)
- MyEclipse (1)
- JSP (1)
- Linux shell 脚本攻略 (7)
- Python3 (2)
- HTML5 (5)
- JavaScript (7)
- Hadoop-1.2.1 (2)
- Python2.7 (12)
- Django (3)
- 软件安装 (1)
- 高级Bash脚本编程指南 (7)
- Linux命令 (3)
- Ansible (2)
- MySQL (2)
- 病历 (1)
- 操作系统 (1)
- CSS (0)
- CSS3 (0)
- 面试题 (1)
最新评论
-
hw1287789687:
http://www.cnblogs.com/hccwu/p/ ...
Java获取真实的IP地址 -
liubey:
String ip = request.getHeader(& ...
Java获取真实的IP地址 -
bewithme:
我记得uploadify这破东西只能在chrome浏览器中才有 ...
Struts2结合Jquery.uploadify上传插件的应用 -
MrLee23:
http://mrlee23.iteye.com/admin/ ...
Struts2结合Jquery.uploadify上传插件的应用 -
crysik:
import com.eshore.ppm.model.com ...
Struts2结合Jquery.uploadify上传插件的应用
2013年6月17日 星期一 23时34分54秒
第八章 多态
在面向对象的程序设计语言中,多态是继续数据抽象和继承之后的第三种基本特征。
多态通过分离做什么和怎么做,从另一角度讲接口和实现分离开来。
”封装“是通过合并特征和行为来创建新的数据类型。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要他们都是从同一基类导出而来的。
多态(也成作动态绑定,后期绑定,或运行时绑定)
8.1 再论向上转型
对象既可以作为它自己本身的类型使用,也可作为它的基类使用。把这种对某个对象的引用视为对其基类类型的引用的做法称为向上转型。
package chapter8;
import chapter8.Wind;
/*@name Bath.java
* @describe 8.1 再论向上转型
* @since 2013-06-18 0:36
* @author 张彪
*/
public class Music {
public static void tune(Instrument i){
i.play(Note.C_SHARP);
}
public static void main(String[] args){
Wind w=new Wind();
tune(w); // 向上转型 此处的参数应该是Instrument,但是传入的是导出类Wind
}
}
8.1.1 忘记对象类型
为什么所有人故意忘记对象的类型呢???
8.2 转机
8.2.1 方法调用绑定
将一个方法的调用同一个方法的主体关联起来被称为绑定。
前期绑定---在程序运行前进行绑定。
后期绑定---在运行时根据对象的类型进行绑定。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑 定----它会自动发生。
为什么要将某个方法声明为final呢???
R:1 防止他人覆盖该方法。2 可以有效“关闭”动态绑定,或者说告诉编译器不需要对其进行动态绑定。这样编译器就可以为final方法调用生成更有效的代码。大多数情况 下,这样做不会对程序性能有什么改观。所以,最好根据设计来决定是否使用final。
8.2.2 产生正确的行为
具体例子为chapter8\shape\Shape.java
8.2.3 可扩展性
多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。
8.2.4 缺陷:“覆盖”私有方法
结论:只有非private方法才可以被覆盖。
8.2.5 缺陷:域与静态方法
package chapter8.super1;
/*@name Bath.java
* @describe 8.2.5 静态方法行为不具有多态性
* @since 2013-06-19 0:25
* @author 张彪
*/
class StaticSuuper{
public static String staticGet(){
return "Base staticGet";
}
public String dynamicGet(){
return "Base dynamicGet";
}
}
class StaticSub extends StaticSuuper{
public static String staticGet(){
return "Driver staticGet";
}
public String dynamicGet(){
return "Driver dynamicGet";
}
}
public class StaticFileAccess {
//静态方法与类,而并非与单个对象相关联
public static void main(String[] args){
StaticSuuper sb= new StaticSub();
System.out.println(sb.staticGet()); //Base staticGet
System.out.println(sb.dynamicGet()); //Driver dynamicGet
}
}
8.3 构造器与多态
尽管构造器并不具有多态性(它们实际上是static方法,只不过该static声明是隐式的),但还是非常有必要理解构造器怎样通过多态在复杂的层次机构中运作。
8.3.1 构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到正确的调用。
对象调用构造器要遵循下面的顺序:
1.调用基类构造器
2.按声明顺序调用成员的初始化方法
3.调用导出类构造器的主体
8.3.2 继承与清理
对象销毁的顺序应该和初始化顺序相反,对于字段则意味着与声明的顺序相反(因为字段的初始化是按照声明的顺序进行的)。对于基类,(遵循C++中析构函数的形式),应先对导出 类进行清理,然后才是基类。
//如果成员对象存在一个其他一个或多个对象共享的情况,问题就变的比较复杂。
package chapter8;
/*@name Bath.java
* @describe 8.3.2 继承与清理
* @since 2013-06-19 21:36
* @author 张彪
*/
//使用引用计数器来跟踪仍旧访问着共享对象的对象数量
class Shared{
private int refcount=0;
private static long counter=0;
private final long id=counter++;
public Shared(){
System.out.println("Creating "+this);
}
public void addRef(){
refcount++;
}
protected void dispose(){
if(--refcount == 0){
System.out.println("dispose "+this);
}
}
public String toString(){
return "Shared "+id;
}
}
class Composing{
private Shared shared;
private static long counter=0;
private final long id=counter++;
public Composing(Shared shared){
System.out.println("Creating "+this);
this.shared=shared;
this.shared.addRef();
}
protected void dispose(){
System.out.println("disping "+this);
shared.dispose();
}
public String toString(){
return "Composing "+id;
}
}
public class ReferenceCounting {
public static void main(String[] args){
Shared s=new Shared();
Composing[] composing={new Composing(s),new Composing(s),new Composing(s),new Composing(s) };
for(Composing c:composing){
c.dispose();
}
}
}
8.3.3 构造器内部的多态方法的行为
构造器调用的层级结构带来了一个有趣的两难为题。如果在一个构造器的内部调用正在构造的对象的某个动态绑定方法,会发生什么情况呢?
package chapter8;
/*@name Bath.java
* @describe 8.3.3 构造器内部的多态行为的方法
* @since 2013-06-19 21:36
* @author 张彪
*/
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);
}
}
/*Glyph before draw()
RoundGlyph.draw() , radius=0
Glyph after draw()
RoundGlyph.RoundGlyph(), radius=5*/
//由输出的结果可知:此时被调用的是RoundGlyph.draw()方法,但是radius的值却是0. why?????
初始化的实际过程:
1.在其他任何事物发生前,将分配给对象的存储空间初始化为二进制的零。
2.如前所述那也调用基类构造器。此时调用被覆盖的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故, 我们此时会发现radius的值为0。
3.按照声明的顺序调用成员的初始化方法。
4.调用导出类的构造器主体
注意:在编码构造器时有一条有效的准则:"用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法"。在构造器内部,唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法),这些方法不会被覆盖,因此也就不会产生令人惊讶的问题。
8.4 协变返回类型
Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖的方法可以返回基类方法的返回类型的某种导出类型。
package chapter8;
/*@name Bath.java
* @describe 8.4 协变返回类型
* @since 2013-06-19 22:36
* @author 张彪
*/
//Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖的方法可以返回基类方法的返回类型的某种导出类型。
class Grain{
public String toString(){return "Grain";};
}
class Wheat extends Grain{
public String toString(){return "Wheat";};
}
class Mill{
Grain process(){return new Grain();};
}
class WheatMill extends Mill{
Wheat process(){return new Wheat();};
}
public class CovariantReturn {
public static void main(String[] args){
Mill m=new Mill();
Grain g=m.process();
System.out.println(g); //Grain
m=new WheatMill();
System.out.println(m.process()); //Wheat
}
}
8.5 用继承进行设计
当我们在使用现成的类来建立新类时,如果首先考虑使用继承技术,反倒会加重我们的设计负担。
更好的方式是选择“组合”,尤其是不能十分确定应该使用哪一种方式时。组合不会强制我们的程序设计进入继承的层次结构中。
8.5.1 纯继承与扩展
纯粹的“is--a”关系:即导出类的方法和基类的方法完全一样。
“is--like--a”关系:即导出类除了有基类的方法外,还有自己扩展的方法。
8.5.2 向下转型与运行时类型识别
由于向上转型(在继承层次中向上移动)会丢失具体的类型信息,所以我们就想,通过向下转型---也就是在继承层次中向下移动---应该能够获取类型的信息。
package chapter8;
/*@name Bath.java
* @describe 8.5.2 向下转型
* @since 2013-06-19 22:54
* @author 张彪
*/
class Useful{
public void f(){}
public void g(){}
}
class MoreUseful extends Useful{
public void f(){}
public void g(){}
public void v(){}
}
public class RTTI {
public static void main(String[] args){
Useful[] x={new Useful(),new MoreUseful()};
((MoreUseful)x[1]).v();
}
}
8.6 总结
多态意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同的接口,以及使用该接口的不同形式;不同版本的动态绑定方法。
2013-06-19 23:04 记 @@tangxiacun.tianhequ.guanzhou
第八章 多态
在面向对象的程序设计语言中,多态是继续数据抽象和继承之后的第三种基本特征。
多态通过分离做什么和怎么做,从另一角度讲接口和实现分离开来。
”封装“是通过合并特征和行为来创建新的数据类型。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要他们都是从同一基类导出而来的。
多态(也成作动态绑定,后期绑定,或运行时绑定)
8.1 再论向上转型
对象既可以作为它自己本身的类型使用,也可作为它的基类使用。把这种对某个对象的引用视为对其基类类型的引用的做法称为向上转型。
package chapter8;
import chapter8.Wind;
/*@name Bath.java
* @describe 8.1 再论向上转型
* @since 2013-06-18 0:36
* @author 张彪
*/
public class Music {
public static void tune(Instrument i){
i.play(Note.C_SHARP);
}
public static void main(String[] args){
Wind w=new Wind();
tune(w); // 向上转型 此处的参数应该是Instrument,但是传入的是导出类Wind
}
}
8.1.1 忘记对象类型
为什么所有人故意忘记对象的类型呢???
8.2 转机
8.2.1 方法调用绑定
将一个方法的调用同一个方法的主体关联起来被称为绑定。
前期绑定---在程序运行前进行绑定。
后期绑定---在运行时根据对象的类型进行绑定。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。这意味着通常情况下,我们不必判定是否应该进行后期绑 定----它会自动发生。
为什么要将某个方法声明为final呢???
R:1 防止他人覆盖该方法。2 可以有效“关闭”动态绑定,或者说告诉编译器不需要对其进行动态绑定。这样编译器就可以为final方法调用生成更有效的代码。大多数情况 下,这样做不会对程序性能有什么改观。所以,最好根据设计来决定是否使用final。
8.2.2 产生正确的行为
具体例子为chapter8\shape\Shape.java
8.2.3 可扩展性
多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。
8.2.4 缺陷:“覆盖”私有方法
结论:只有非private方法才可以被覆盖。
8.2.5 缺陷:域与静态方法
package chapter8.super1;
/*@name Bath.java
* @describe 8.2.5 静态方法行为不具有多态性
* @since 2013-06-19 0:25
* @author 张彪
*/
class StaticSuuper{
public static String staticGet(){
return "Base staticGet";
}
public String dynamicGet(){
return "Base dynamicGet";
}
}
class StaticSub extends StaticSuuper{
public static String staticGet(){
return "Driver staticGet";
}
public String dynamicGet(){
return "Driver dynamicGet";
}
}
public class StaticFileAccess {
//静态方法与类,而并非与单个对象相关联
public static void main(String[] args){
StaticSuuper sb= new StaticSub();
System.out.println(sb.staticGet()); //Base staticGet
System.out.println(sb.dynamicGet()); //Driver dynamicGet
}
}
8.3 构造器与多态
尽管构造器并不具有多态性(它们实际上是static方法,只不过该static声明是隐式的),但还是非常有必要理解构造器怎样通过多态在复杂的层次机构中运作。
8.3.1 构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到正确的调用。
对象调用构造器要遵循下面的顺序:
1.调用基类构造器
2.按声明顺序调用成员的初始化方法
3.调用导出类构造器的主体
8.3.2 继承与清理
对象销毁的顺序应该和初始化顺序相反,对于字段则意味着与声明的顺序相反(因为字段的初始化是按照声明的顺序进行的)。对于基类,(遵循C++中析构函数的形式),应先对导出 类进行清理,然后才是基类。
//如果成员对象存在一个其他一个或多个对象共享的情况,问题就变的比较复杂。
package chapter8;
/*@name Bath.java
* @describe 8.3.2 继承与清理
* @since 2013-06-19 21:36
* @author 张彪
*/
//使用引用计数器来跟踪仍旧访问着共享对象的对象数量
class Shared{
private int refcount=0;
private static long counter=0;
private final long id=counter++;
public Shared(){
System.out.println("Creating "+this);
}
public void addRef(){
refcount++;
}
protected void dispose(){
if(--refcount == 0){
System.out.println("dispose "+this);
}
}
public String toString(){
return "Shared "+id;
}
}
class Composing{
private Shared shared;
private static long counter=0;
private final long id=counter++;
public Composing(Shared shared){
System.out.println("Creating "+this);
this.shared=shared;
this.shared.addRef();
}
protected void dispose(){
System.out.println("disping "+this);
shared.dispose();
}
public String toString(){
return "Composing "+id;
}
}
public class ReferenceCounting {
public static void main(String[] args){
Shared s=new Shared();
Composing[] composing={new Composing(s),new Composing(s),new Composing(s),new Composing(s) };
for(Composing c:composing){
c.dispose();
}
}
}
8.3.3 构造器内部的多态方法的行为
构造器调用的层级结构带来了一个有趣的两难为题。如果在一个构造器的内部调用正在构造的对象的某个动态绑定方法,会发生什么情况呢?
package chapter8;
/*@name Bath.java
* @describe 8.3.3 构造器内部的多态行为的方法
* @since 2013-06-19 21:36
* @author 张彪
*/
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);
}
}
/*Glyph before draw()
RoundGlyph.draw() , radius=0
Glyph after draw()
RoundGlyph.RoundGlyph(), radius=5*/
//由输出的结果可知:此时被调用的是RoundGlyph.draw()方法,但是radius的值却是0. why?????
初始化的实际过程:
1.在其他任何事物发生前,将分配给对象的存储空间初始化为二进制的零。
2.如前所述那也调用基类构造器。此时调用被覆盖的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故, 我们此时会发现radius的值为0。
3.按照声明的顺序调用成员的初始化方法。
4.调用导出类的构造器主体
注意:在编码构造器时有一条有效的准则:"用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法"。在构造器内部,唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法),这些方法不会被覆盖,因此也就不会产生令人惊讶的问题。
8.4 协变返回类型
Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖的方法可以返回基类方法的返回类型的某种导出类型。
package chapter8;
/*@name Bath.java
* @describe 8.4 协变返回类型
* @since 2013-06-19 22:36
* @author 张彪
*/
//Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖的方法可以返回基类方法的返回类型的某种导出类型。
class Grain{
public String toString(){return "Grain";};
}
class Wheat extends Grain{
public String toString(){return "Wheat";};
}
class Mill{
Grain process(){return new Grain();};
}
class WheatMill extends Mill{
Wheat process(){return new Wheat();};
}
public class CovariantReturn {
public static void main(String[] args){
Mill m=new Mill();
Grain g=m.process();
System.out.println(g); //Grain
m=new WheatMill();
System.out.println(m.process()); //Wheat
}
}
8.5 用继承进行设计
当我们在使用现成的类来建立新类时,如果首先考虑使用继承技术,反倒会加重我们的设计负担。
更好的方式是选择“组合”,尤其是不能十分确定应该使用哪一种方式时。组合不会强制我们的程序设计进入继承的层次结构中。
8.5.1 纯继承与扩展
纯粹的“is--a”关系:即导出类的方法和基类的方法完全一样。
“is--like--a”关系:即导出类除了有基类的方法外,还有自己扩展的方法。
8.5.2 向下转型与运行时类型识别
由于向上转型(在继承层次中向上移动)会丢失具体的类型信息,所以我们就想,通过向下转型---也就是在继承层次中向下移动---应该能够获取类型的信息。
package chapter8;
/*@name Bath.java
* @describe 8.5.2 向下转型
* @since 2013-06-19 22:54
* @author 张彪
*/
class Useful{
public void f(){}
public void g(){}
}
class MoreUseful extends Useful{
public void f(){}
public void g(){}
public void v(){}
}
public class RTTI {
public static void main(String[] args){
Useful[] x={new Useful(),new MoreUseful()};
((MoreUseful)x[1]).v();
}
}
8.6 总结
多态意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同的接口,以及使用该接口的不同形式;不同版本的动态绑定方法。
2013-06-19 23:04 记 @@tangxiacun.tianhequ.guanzhou
- chapter8.rar (6.2 KB)
- 下载次数: 0
发表评论
-
第十三章 字符串
2013-08-06 00:50 9112013年8月1日 星期四 21时05分59秒 第十三章 字 ... -
第十二章 通过异常处理错误
2013-08-01 21:04 7272013年7月10日 星期三 00时04分21秒 第十二章 ... -
第十一章 持有对象
2013-07-09 00:49 9322013年6月24日 星期一 20时57分09秒 第十一章 ... -
第十四章 类型信息
2013-07-02 01:26 9212013年6月25日 星期二 23时12分42秒 第十四章 ... -
第十章 内部类
2013-06-24 20:47 9742013年6月23日 星期日 16时50分56秒 第十章 内 ... -
第九章 接口
2013-06-23 16:46 8452013年6月20日 星期四 21时41分40秒 第九章 接 ... -
Thinking in Java Fourth Edition Source Code
2013-06-17 23:10 495Thinking in Java F ... -
第七章 复用类
2013-06-17 22:36 5522013年6月16日 星期日 21时06分54秒 第七章 复 ... -
第六章 访问权限控制
2013-06-16 21:05 8522013年6月16日 星期日 11时10分46秒 第六章 访 ... -
第五章 初始化与清理
2013-06-16 10:58 6032013年6月15日 星期六 16 ...
相关推荐
C++程序设计教程 第8章 类(共29页) C++程序设计教程 第9章 对象生灭(共28页) C++程序设计教程 第10章 继承(共19页) C++程序设计教程 第11章 面向对象编程(共21页) C++程序设计教程 第12章 多态(共8页) C++...
Java第8章 继承和多态含源代码 Java第8章 继承和多态含源代码Java第8章 继承和多态含源代码 Java第8章 继承和多态含源代码 Java第8章 继承和多态含源代码 Java第8章 继承和多态含源代码
【完整课程列表】 ... Java基础入门教程 第1章 Java 简介...Java基础入门教程 第8章 文件流的输入输出操作(共37页).ppt Java基础入门教程 第9章 Java的高级特性(共32页).ppt jdk api 1.7.chm JDK_API_1_6_zh_CN.CHM
第8章--virtual函数与多态
Java程序设计:第8章 继承和多态.ppt
第8章动物类派生——继承与多态.ppt
C++程序设计:第八章 继承与多态.pdf
C 程序设计课件:第八章 继承与多态.ppt
C++ 程序设计课件:第八章 继承与多态.ppt
这是吴廼陵主编的C++教材第八章的答案,包括了填空题选择题,以及大部分程序的源代码。
继承(inheritance): 该机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构。...
第1章 C#入门 ...1.1.2 认识C≠语言 1.2 Visual Studio.NET集成开发环境简介 1.2.1 VS.NET起始页 ...第8章 多态 第9章 接口 第10章 异常处理 第11章 委托和事件 第12章 文件操作 第13章 课程设计 参考文献
第8章内联函数 第9章命名控制 第10章引用和拷贝构造函数 第11章运算符重载 第12章动态对象创建 第13章继承和组合 第14章多态和虚函数 第15章模板和包容器类 第16章多重继承 第17章异常处理 第18章运行时类型识别
第1章 Java概述 第2章 Java语言基础 第3章 类的封装、继承和多态 第4章 接口、内部类和Java API基础 ...第8章 Applet应用程序 第9章 输入/输出流和文件操作 第10章 网络通信 第11章 数据库应用 第12章 综合应用设计
第10章 static修饰符.pptx 第10章 枚举.pptx 第11章 异常和断言.pptx 第12章 JDK8中的日期.pptx 第12章 字符串、日期.pptx 第13章 容器和泛型.pptx 第14章 流与文件(1)....第8章 包装器类.pptx 第9章 内部类.pptx
完整版精品java教程 清华大学新版Java课件 Java语言程序设计(第2版) 第8章 线程(共78页).ppt 完整版精品java教程 清华大学新版Java课件 Java语言程序设计(第2版) 第9章 GUI图形用户界面(共139页).ppt
C++程序设计教程 第8章 类(共29页) C++程序设计教程 第9章 对象生灭(共28页) C++程序设计教程 第10章 继承(共19页) C++程序设计教程 第11章 面向对象编程(共21页) C++程序设计教程 第12章 多态(共8页) C++...
第8章 指针 第9章 构造数据类型 第10章 类和对象 第11章 继承 第12章 多态 第13章 运算符重载 第14章 输入/输出流 第15章 文件 第16章 命名空间 第17章 引用与内存管理 第18章 标准...