本帖最后由 321哈哈哈 于 2017-10-12 02:23 编辑
绑定:将 方法调用 和 方法主体 关联起来 后期绑定(运行时绑定/动态绑定/多态):在运行时根据对象的类型进行绑定(虽然调用方法的是父类引用,但在运行时会检查该对象的实际类型,执行的是该子类的重写后的方法) (方法调用机制,根据安置在对象中的“类型信息”) (调用方法的时候将当前对象的地址值传给了方法的隐形形参this,从this找到对象,根据其“类型信息”找到它的类,找到类中的方法定义;方法调用就和方法主体关联起来了) (发送消息给某个对象,让该对象去断定应该做什么事情。按它自己的方式做) 向上转型:“忘记”对象本身的类型,当成父类的类型 一个方法仅接收基类作为参数(子类对象向上转型是可以的),不管导出类的存在,编写的代码只是与基类打交道(比如其中调用方法的都是父类引用,即操作基类接口)。 (可扩展性:一个良好的OOP程序中,大多数或者所有方法都这样写,而且只与基类接口进行通信。这样可增加新的子类,但该方法不需要任何改动对子类同样适用) 多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。 多态是程序语言对规律的表达,规律是不变的。 Java中除了static和final(含private)方法之外,其他方法都是后期绑定(自动发生)。 静态方法其实是属于类的(虽然也可以被对象调用),执行时没有多态,直指基类(如果是基类引用调用的)的静态方法。构造方法(隐式static)也不具有多态性。 final的方法不会被重写,只有一个版本,后期绑定就没有意义了。 直接访问域(字段/属性),没有用多态 一种尴尬的情况: 父类构造方法中调用了自己的一个方法,该方法会被子类继承并重写,重写后会访问子类的成员。(可能我们的本意只是调用重写前的方法) 问题出在创建子类对象的时候,会先执行父类构造,执行到该语句时,动态绑定(this找到的是子类对象,判断当前对象的类型)会找到子类的重写方法来执行,但此时子类成员还没有按照我们的意愿初始化(值是分配空间时产生的默认值或者null),会产生混乱的结果(不但子类掺和进来,得到的还是莫名其妙的值,同时没有语句报错) 结论: 编写构造器时,“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”(唯一能安全调用的是自己的final方法,private也是final的,它们不会被覆盖,可以放心用;static的应该也可以,因其没有多态性) 注意: 除了调用static和final的方法,调用其他方法的时候动态绑定(多态)是自动发生的(肯定会先判断当前对象的类型,然后寻找方法主体),每一个普通方法都是这样。 我们调用普通方法分两种情况:调用本类的方法(直接写方法名即可,效果等同于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”关系:子类有扩展的接口(额外方法实现其他特性) |