A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

© 321哈哈哈 中级黑马   /  2017-10-4 11:06  /  947 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 321哈哈哈 于 2017-10-12 02:23 编辑

绑定:将 方法调用 方法主体 关联起来
后期绑定(运行时绑定/动态绑定/多态):在运行时根据对象的类型进行绑定(虽然调用方法的是父类引用,但在运行时会检查该对象的实际类型,执行的是该子类的重写后的方法)
(方法调用机制,根据安置在对象中的“类型信息”)
(调用方法的时候将当前对象的地址值传给了方法的隐形形参this,从this找到对象,根据其“类型信息”找到它的类,找到类中的方法定义;方法调用就和方法主体关联起来了)
(发送消息给某个对象,让该对象去断定应该做什么事情。按它自己的方式做)
向上转型:“忘记”对象本身的类型,当成父类的类型
一个方法仅接收基类作为参数(子类对象向上转型是可以的),不管导出类的存在,编写的代码只是与基类打交道(比如其中调用方法的都是父类引用,即操作基类接口)。
(可扩展性:一个良好的OOP程序中,大多数或者所有方法都这样写,而且只与基类接口进行通信。这样可增加新的子类,但该方法不需要任何改动对子类同样适用)
多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。
多态是程序语言对规律的表达,规律是不变的。
Java中除了staticfinal(含private)方法之外,其他方法都是后期绑定(自动发生)。
静态方法其实是属于类的(虽然也可以被对象调用),执行时没有多态,直指基类(如果是基类引用调用的)的静态方法。构造方法(隐式static)也不具有多态性。
final的方法不会被重写,只有一个版本,后期绑定就没有意义了。
直接访问域(字段/属性),没有用多态
一种尴尬的情况:
父类构造方法中调用了自己的一个方法,该方法会被子类继承并重写,重写后会访问子类的成员。(可能我们的本意只是调用重写前的方法)
问题出在创建子类对象的时候,会先执行父类构造,执行到该语句时,动态绑定(this找到的是子类对象,判断当前对象的类型)会找到子类的重写方法来执行,但此时子类成员还没有按照我们的意愿初始化(值是分配空间时产生的默认值或者null),会产生混乱的结果(不但子类掺和进来,得到的还是莫名其妙的值,同时没有语句报错)
结论:
编写构造器时,“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”(唯一能安全调用的是自己的final方法,private也是final的,它们不会被覆盖,可以放心用;static的应该也可以,因其没有多态性)
注意
除了调用staticfinal的方法,调用其他方法的时候动态绑定(多态)是自动发生的(肯定会先判断当前对象的类型,然后寻找方法主体),每一个普通方法都是这样。
我们调用普通方法分两种情况:调用本类的方法(直接写方法名即可,效果等同于this.方法名),其他类中调用(对象名.方法名)。
延伸
好的面向对象设计要考虑未来扩展的可能性,在调用普通方法的时候应该充分考虑并利用动态绑定(多态),用父类引用操作父类接口,留出足够的扩展性,让子类以后去发挥(原理:如果对象实际类型是其子类,方法得到的this引用是指向子类对象的,执行的是子类重写后的方法)
如果确定了不想有什么扩展性(比如是本类逻辑的实现代码,和其他类无关),就想老老实实地运行指定代码,完成特定功能,就调用final(含private)或者static等相对安全的方法(private是本类的“助手”方法,不是接口,不能重写;final的方法不会被重写;static方法执行时只会指向指定类的那个静态方法;三种都没有多态性),这些方法动态绑定不会出来捣乱。
上面的例子就是个反例,不想有扩展性,却调用了一个会被继承重写的可扩展的方法。一旦方法得到的this引用指向的是子类对象(如例中所示),执行的就不是我们预期的代码。
协变返回类型
重写后方法的返回类型可以是原方法返回类型的子类
一条通用准则:“用继承表达行为间的差异,并用字段表达状态上的变化”
现实事物有状态和行为,对应的类有属性(字段)和方法。
继承后重写,子类之间行为就有了差异
成员对象(父类引用)类型可以变化(可能是不同子类对象),使所在对象状态发生变化
(状态模式p165,继承和组合结合使用)
向下转型:
前提是之前进行了向上转型(用父类引用接收子类对象)
其结果只能转为对象本身的实际类型
如果不是往实际类型转,在运行时会报错ClassCastException
这种在运行期间对类型进行检查的行为称作“运行时类型识别”(RTTI)(runtime type information
RTTI提供了一种方法,使你在试图向下转型之前,查看你所要处理的对象的实际类型??
扩展自己的编程视野,不仅考虑类的成员(属性和方法)和消息,还要考虑类与类之间的共同特性以及它们之间的关系(继承,组合,代理...)。
以获得更快的开发过程,更好的代码组织,更好的扩展性,更容易的代码维护。
Tip
封装通过合并特征和行为来创建新的数据类型
“实现隐藏”则通过将细节私有化把接口和实现分离开
多态的作用则是消除类型之间的耦合关系??
创建对象初始化顺序:(注意2 3 4的顺序)
1. 给对象分配空间,将空间初始化成二进制的零(对象的所有成员值为默认值或者null
2. 调用基类构造方法(执行之前也有基类成员的初始化,类似3
3. 按照声明顺序初始化成员(声明语句的赋值)
4. 调用导出类的构造方法
如果遇到需要清理的情况,小心的创建dispose(处理)方法。(通常不必执行清理工作)??
销毁顺序应该和初始化顺序相反
当覆盖基类的dispose方法时,务必记住调用基类版本的dispose
如果一个对象同时是其他多个对象的成员(指向它的引用有多个),需要在这个对象内部设置引用计数器refCount,外界每次调用它的dispose方法都会使计数器-1,当没有引用指向它时,再清理自己。P161
Is-a”关系:子类没有新接口
Is-like-a”关系:子类有扩展的接口(额外方法实现其他特性)

3 个回复

倒序浏览
回复 使用道具 举报
加油!
回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马