Java编程思想之8多态

这一章看下来,感觉比较混乱。乱感觉主要乱在8.4  8.5。 开始读的时候,感觉8.1 8.2 8.3都挺乱的。读了两遍后发现前三节还是比较有条理的。

8.1主要讲了什么是多态,多态的好处。

8.2主要讲了什么情况会发生多态??

8.3主要讲了构造器内部里面的方法调用会发生多态。

8.4就一页,而且感觉一般用不到。用到了再看也行。

8.5也很简单,相当于一个总结,一个补充(向下转型)

我主要根据书上的内容,总结两个内容: 1.什么是多态,多态的好处; 2.什么情况下会发生多态?为什么这些情况下会发生多态,而别的情况不会发生多态?

什么是多态??

多态是面向对象的三个基本特征之一。也是比较不容易理解的一个。

多态,按照我的理解,应该是:根据调用对象的不同,而执行不同的方法。  这个应该是比较表面的,比较浅的定义。

多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术(《Java编程思想》155页)。 这个说法或许说明了最本质的东西,但是没有几年道行,很难理解。

其实这两种说法是一致的,正是因为多态可以 在运行是根据不同的对象,调用不同的方法。所以才能将改变的事物与未变的事物分开。不容易理解,就看书上149页的例子。函数tune(Instrment i)里面,调用了i.play()方法。实际的执行过程中,就会根据i的具体类型,而执行这个具体类型里的方法。

《java编程思想》148页头两段,写的真是好。摘抄一下:

多态是通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。多态不但能够改善代码的组织结构和可读性,还能创建可扩展的程序。

这四小句话,每一小句,都很耐人寻味的。下面的一段开始解释了。

  1. 先解释第二小句,什么是另一个角度呢??我们已经有一个角度了吗??答案是:有,封装。“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”是通过细节“私有化”把接口和实现分离开来。这是封装的两个特性,我们只把public方法暴露出来,而这个方法的逻辑实现,我们可能调用了很多private方法。所以,通过私有化将实现隐藏,而仅仅把public方法暴露。
  2. 解释第一小句,感觉它的这个角度(通过分离做什么和怎么做,实现接口与实现的分离),是跟抽象类/基类/接口的角度很象的。 或者说,他们其实是一个东西,基类(包括抽象类接口)与多态结合,才能实现这个分离做什么和怎么做。 基类(接口)规定了做什么,子类(接口的实现)负责怎么做。  但是,两者之间的结合是靠的多态。多态的作用是:消除类型之间的耦合关系。耦合关系越低,肯定就是可以将改变的东西和不变的东西分离的越好。还是第149页的上下两个例子,如果不用基类作为函数的参数,而是用的具体类。那么,这个Msic2类就跟具体类偶尔很大了,具体类的添加和删除都会影响到Msic2类。这个基类,相当于提供了一个中间层,消除了这个耦合。而具体运行时,多态可以根据调用者的不同,而执行不同的方法。所以,我认为:是基类(接口)和多态共同实现了这个特性(分离做什么和怎么做)。而多态这个特性的实现,必须依靠继承的一个特性,继承有两个特性:一. 
    代码复用;二.允许将对象视为它自己本身的类型或其基类型来加以处理。第二个特性太重要了,正是因为第二个特性,才得以让多态的特性得到了发挥。第二个特性,也是使用继承的重要标志。第二个特性允许将多种对象视为它自己本身的类型或其基类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。
  3. 第三句和第四句,也算是多态的好处吧。看着149页的代码,就能感觉到,第一个比第二个组织结构好,可读性好。  也容易扩展。

注:多态一般是消除了类使用时的耦合,我们只要使用抽象类或者接口就行。系统根本不需要知道我们到底使用了哪个具体类。 而这个类实例的创建,很容易发生强耦合,new 一个具体类。 这个问题解决是依靠了设计模式里面的工厂模式,将类的创建和类的使用彻底分开。《Java编程思想》也多次举例说明工厂模式。

什么方法的调用会发生多态??

书上8.2  8.3都是讲的哪些情况的调用会发生多态。但是,感觉讲的比较乱,如果从java虚拟机的角度出发,就很好搞定,也不用记太多的东西了。

8.2.1说道,有些方法是在编译期间绑定的,叫做前期绑定。有些方法是在运行时绑定的,叫做后期绑定的。后期绑定也叫做动态绑定或运行时绑定。显然,前期绑定不会发生多态,应为调用哪个方法已经确定了。 后期绑定才会发生多态。牢记:static方法,final方法,private方法是前期绑定,不会发生多态。  其余的方法是后期绑定,会发生多态。

我们知道,Java经过编译后,会编译成字节码。那么方法调用会编译成什么样的字节码呢??一共有四种方法调用字节码指令。分别是:

  1. invokestatic:调用静态方法;
  2. invokespecial:调用实例构造器<init>方法,私有方法和父类方法;
  3. invokevirtual:调用所有的虚方法;
  4. invokeinterface:调用接口方法。

只要能被invokstatic  invokespecial调用的方法,都可以在解析阶段确定唯一调用的版本。与之相反,其它方法就成为虚方法(final方法出外,虽然final方法是使用invokevirtual调用的,但是final方法无法被覆盖,没有其它版本,调用的结果肯定是唯一的。所以,java语言规范明确说明了final方法是一种非虚方法)。看下面的例子:

public class DynamicDispatch {

	static abstract class Human{
		protected abstract void sayHello();
	}

	static class Man extends Human{
		@Override
		protected void sayHello(){
			System.out.println("man say hello");
		}
	}

	static class Women extends Human{
		@Override
		protected void sayHello(){
			System.out.println("women say hello");
		}
	}

	public static void main(String[] args){
		Human man = new Man();
		Human women = new Women();
		man.sayHello();
		women.sayHello();

		man = new Women();
		man.sayHello();
	}

}

下面是进入到这个java文件目录后,编译并输出字节码文件(命令在图片里面)

主要看main方法,第0行new了一个对象实例。创建对象的过程中,会调用<init>方法,这个方法是Java编译器自动生成的,用来初始化对象实例的。第3行,是把0行的指针复制一下,因为4行的语句会耗费一个指针。7行是保存这个对象到局部变量。8到15行类似。

第16行,是将第一个局部变量载入内存,那么第一个局部变量是谁呢??我们知道,一个实例方法的第一个局部变量保存的是this,然后是各个函数参数的依次保存。然后是方法里面声明的变量。而静态方法是没有this变量的。所有这个main方法保存的第一个是函数的参数String[],保存在位置0。而位置1就是我们函数里面

Human man = new Man();

这个变量了。关键看第17行,这个指令会根据上一个变量的实际类型,来选择所应该调用的方法。只有到了运行期才会确定出这个方法的具体位置。#6指向的是常量池,里面是一个字符串“sayHello:()V",根据这个16行的实际类型,会先定位到这个实际类型本身,查找这个实际类型的所有方法,如果找到了这个sayHello()方法,就调用。否则的话,就搜索父类。这样一直搜索到Object()类。就是这样,实现了多态。
当然,有可能会优化这个搜索过程。具体可以看《深入理解Java虚拟机》,周志明老师写的。

类似的,可以这样反编译一下private方法,final方法的情况,很容易,就可以知道的了。

其实到这里,我感觉已经把这一章的主要东西说完了。为了让思路更加顺畅,下面我想总结一下,为什么private  final方法不发生多态???

为什么private  final方法不发生多态???

其实,很多时候都是这样的:能在尽早搞定的事儿,就不要拖到后面。  比如 尽早的警告代码里的错误等。  所以,能在解析期就确定的方法,就不要到运行期再确定。因为在运行时再去确认哪个方法,可能会影响执行速度。看下面的代码:

public class DynamicDispatch {

	static abstract class Human{
		protected abstract void sayHello();
		public final void love(){
			System.out.println("I love you");
		}
	}

	static class Man extends Human{
		@Override
		protected void sayHello(){
			System.out.println("man say hello");
		}
	}

	public static void main(String[] args){
		Human h = new Man();
		h.love();
	}
}

love()方法是final的。 main函数里面,不论这个Human h被实例化成哪个子类,h.love()永远调用的是Human类中的love方法,首先不可能调用到Human父类里面去,因为Human类已经有这个方法了。 也不可能调用到Human的子类里面,因为love()方法是final的,不能被子类覆盖的。所以,在解析期间完全可以确定这个love()应该调用的具体位置。所以,这个方法就不会多态了。

注:编译期间只是把方法的调用,转化到了调用常量池的某个字符串。在解析期间,才会把一起方法的常量池字符从转化为具体的方法位置,private方法,final方法都是在这个阶段失去了多态的可能性。而虚方法是不会转化的,所以会发生多态。

Java编程思想之8多态

时间: 2025-01-14 10:03:35

Java编程思想之8多态的相关文章

Java编程思想学习(六) 多态

1.Java语言的三大特性:继承.封装和多态. 继承:复用类的一种方法,可以简省很多代码: 封装:通过合并特征和行为来创建新的数据类型.[这种“数据类型”跟Java本身提供的8大“基本数据类型”的地位完全相同.Java通过封装这种方式来扩充数据类型.] 多态:消除创新的数据类型之间的耦合关系. 2.前期绑定:static和final方法都是前期绑定(在编译时绑定和执行): 3.后期绑定:Java中除了static和final方法,都是后期绑定(前面提到过,private方法属于final方法).

java编程思想第八章多态

前言: 封装:通过合并特征和行为创建新的数据类型. 实现隐藏:通过将细节"私有化",把接口和实现分离. 多态:消除类型间的耦合关系.也称作动态绑定,后期绑定或运行时绑定. 8.1再论向上转型: 对象既可以作为本身的类型使用,也可以作为基类的类型使用.这种做法成为向上转型. 其代码表现就是:父类引用指向子类. 多态允许在参数的地方使用,即参数处声明基类,用于接受基类或导出类实现. 8.2转机(主要是介绍如何实现多态) 为了解决多态问题,在java中提供了后期绑定的方法,即在运行时根据对象

【Java编程思想】8.多态

在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征. 多态分离了"做什么"和"怎么做",让接口和实现分离开,改善了代码的可读性和组织结构,创建了可拓展的程序. 封装,通过合并特征和行为来创建新的数据类型. 实现隐藏,通过将细节"私有化"把接口和实现分离开来. 多态,消除类型之间的耦合联系.多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要他们都是从同一基类导出来的.-->这种区别是根据方法行为的不同而表示出来的

Java编程思想---第八章 多态(上)

第八章  多态(上) 在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征. 多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来,多态不但能改善代码的组织结构和可读性,还能够创建可扩展的程序,无论在项目最初创建时还是在需要添加新功能时都可以生长程序.封装通过合并特征和行为来创建新的数据类型. 8.1 再论向上转型 在上一章中我们已经知道,对象可以作为它本身的类型使用,也可以作为它的基类使用,而这种把对某个对象的引用视为对其基类的引用的做法被称为向上转型. //: dem

Java编程思想笔记(多态)

      1.再论向上转型:       1(1).忘记对象类型       2.转机:       2(1).方法调用绑定       2(2).产生正确的行为       2(3).可扩展性:多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术.   2(4).缺陷:“覆盖”私有方法:只有非private方法才可以被覆盖:但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行.确切地说,在导出类中,对于基类中的private方法

Java编程思想(五) —— 多态(上)

上一章,Java编程思想(四) -- 复用类里面讲到了向上转型,感觉和多态放在一起写更好. 多态,polymorphism.一个重要的特性,篇幅太长了,分上下两篇写. (1)向上转型 class TV{ public static void show(TV tv){ System.out.println("TV"); } } public class LeTV extends TV{ public static void main(String[] args) { LeTV letv

《Java编程思想(第4版)》pdf

下载地址:网盘下载 内容简介 编辑 本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形.从Java的基础语法到最高级特性(深入的面向对象概念.多线程.自动项目构建.单元测试和调试等),本书都能逐步指导你轻松掌握.[1] 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作.本书的作者拥有多年教学经验,对C.C++以及Java语言都有独到.深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

【java编程思想--学习笔记(四)】对象导论

写这篇博客的前言: 长话短说,我希望通过阅读<java编程思想>来使我的代码 简洁可用 . 目的的层次不同,首先具体的目标是,了解Java的特性和巩固Java的基础. 更抽象的目的如下: 1.期待以巩固基础的方式,使代码优美,简洁,高效. 2.使自己写的模块能够开放适度,好用. 3.形成一种对代码是否优美的审美观. 于是<Java编程思想>第一章 对象导论 由此开始. 1.1 抽象过程 java 相对于命令式语言的优势在于只针对于待解问题建模.后者所做的主要抽象要求所做问题基于计算