名称屏蔽:如果java的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本(这一点于C++不同)。因此,无论是在该层或者它的基类中对方法进行定义,重载机制都可以正常工作。这句话的意思是指:如果基类有方法 void method() String method(int i),那么在导出类中定义 int method(char a) 并不会吧基类的方法给屏蔽,基类的重载方法是可以用的。导出类同样可以重载。
类的复用有两种方法:组合(composition)和继承
组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形,一般在新类中嵌入一个现有类的private对象。但有时,允许类的用户访问新类中的组合部分是极具意义的。如果成员对象自身都隐藏了具体实现,那么将它声明为public也是安全的。
组合一般是将现有类型作为新类型底层实现的一部分来加以复用,而继承复用的是接口,由于导出类具有基类的接口,所以它可以向上转型至基类。
向上转型
新类是现有类的一种类型。
向上转型中子类将能看到父类被覆盖字段
在子类继承父类之后,如果子类与父类有同名的字段和方法,那么子类中的子段会代替或隐藏父类的子段,说明子类字段覆盖了超类字段,但可以通过super关
键字去访问超类字段。但是,在我们将子类对象向超类转型的时候就会发生这个奇怪的现象,子类对象居然可以看到父类曾经被覆盖掉的字段!
一定要注意:当把子类转换成超类时,子类可以见到或访问被隐藏的同名变量。Java允许名字重复的原因是,允许将来把新的字段加到超类中,而不影响已经使用了该名字的现有子类,子类将会继续使用自己的字段副本。除非让子类以超类对象的形式出现,方法可以覆盖,但是字段不能被覆盖。注意:最好不要隐藏超类中的字段名。
所以我们在进行向上转型的时候一定要注意:不要访问子类中那些“覆盖”掉父类的字段(它并没有真正覆盖掉,在向上转型的时候就可以访问的到),要么将子类字段改名(在你知道父类代码的情况下),要么通过方法来访问字类字段(方法即使同名也肯定能覆盖掉)。
在向上转型的过程中,类接口中唯一可能发生的事情是丢失方法,而不是获取它们。
是否需要从新类向基类进行向上转型,如果必须向上转型,则继承是必要的。
final数据
1. 它可以是一个永不改变的“编译期常量(compile-time constant)”。
2. 它可以是一个在运行期被初始化的值,而你不希望它被改变。
在编译期常量的情况下,编译器可以将该常量值带入任何可能用到它的计算式中。就是说,可以在编译期执行计算式,减轻了一些运行期的负担。这类常量必须是基本类型。
一个既是static又是final的域只占有一份不能改变的存储空间。
对于原始类型,final使数值恒定不变,而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法对它进行改变以指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径。
Java允许生成空白final(Blank final),所谓空白final是指被声明为final但又未给定初值的数据成员。无论什么情况,编译器都确保空白 final在使用前必须被初始化。但是,空白final在关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却又保持其恒定不变的特性。
Java允许你以在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中更改参数引用所指向的对象。你可以读参数,却无法修改参数。
使用final方法的原因有两个。第一个原因是把方法锁定,以预防任何继承类修改它的意义。这是出于设计的考虑:你想要确保在继承中方法行为保持不变,并且不会被重载。
使用final方法的第二个原因是效率。如果你将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌(inline)调用。C++中的内联函数,空间换时间
类中所有的private方法都被隐含是final的。由于你无法取用private方法,所以你也无法重载之。如果你试图重载一个private方法(隐含是final的),看起来是奏效的,而且编译器也不会给出错误信息。覆盖只有在某方法是基类的接口的一部分时才会出现。如果某方法是private,它就不是基类的接口的一部分。
当你将某个类的整体定义为final时(通过将关键字final置于它的定义之前),你就声明了你不打算继承该类,而且也不允许别人这样做。Final类禁止继承,所以final类中所有的方法都隐式指定为final。
每个类的编译代码都存在于它自己的独立的文件中。该文件只在需要使用程序代码时才会被加载。一般来说,你可以说:“类的代码在初次使用时才加载。”这通常是指知道类的第一个对象被构建时才发生加载,但是当访问static数据成员或是static方法时,也会发生加载。
初次使用之处也是静态初始化(static初始化)发生之处。所有的static对象和static代码段都会在加载时依程序中的顺序(即,你定义类时的书写顺序)依次初始化。当然,static只会被初始化一次。
用final修饰的成员变量表示常量,值一旦给定就无法改变!
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初
始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
多态通过分离“做什么”和“怎么做”,从另一个角度将接口和实现分离开来。
“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来。多态的作用则是消除类型之间的耦合关系。
多态方法调用允许一种类型表现出与其他相识类型之间的区别,只要他们都是从同以基类导出而来的。这种区别是根据方法行为的不同而表现出来的,虽然这些方法都可以通过同一个基类来调用。
向上转型可能会缩小接口,但不会比基类接口更窄。
前期绑定:若在程序执行前进行绑定(如果有的话,由编译器和链接程序实现)后期绑定:在运行时根据对象的类型进行绑定。也称动态绑定或运行时绑定
c语言都是前期绑定,java除了static方法和final方法外,其他所有的方法都是后期绑定,final可以有效地"关闭”动态绑定。
基类为自它那里继承而来的所有导出类建立一个公用接口,导出类通过覆盖这些定义,来为每种特殊类型提供单独的行为。
从通用的基类继承出的新的数据类型,从而新添一些新的功能,而那些操纵基类的接口的方法不需要任何改动就可以运用于新类。这正是“可扩展性”的程序的一般特征。
多态,将改变的事物与未变的事务分离开来。
只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这时编译器不会报错,但是也不会按照我们所期望的来指向。
如果一个类包含一个或多个抽象方法该类必须被限定为抽象的。也可以创建一个没有任何抽象方法的抽象类,仅是为了组织产生这个类的任何对象。
如果想从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有的抽象方法提供方法定义。如果不这样做,那么导出类便也是抽象类,且编译器将强制我们用abstract关键字来限定这个类。
对象调用构造器的顺序:
1.调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,后是下一层导出类,等到那个,知道最低层的导出类。
2.按声明顺序调用成员的初始化方法
3.调用导出类构造器的主体
清理的时候应该首先对其导出类进行清理,然后才是基类。这是因为导出类的清理可能会调用基类中的某些方法,所以需要使基类中的构件仍起作用而不应该过早地销毁它们。
初始化实际的过程是:
1.
在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
2.
按前面所述调用基类构造器
3.
按声明的顺序调用成员的差距是花方法
4.
调用导出类的构造器主体
这样做有一个优点,那就是所有东西都至少初始化成零(或者某些特殊数据类型中与:零等价的值),而不是仅仅留作垃圾。其中包含通过“组合”而嵌入一个类内
部的对象引用,其值是null。所以如果忘记为该引用进行初始化,就会在运行时出现异常。查看输出结果时,会发现其他所有的东西的值,都是会是零,这通常也
正是方法问题的证据。
编写构造器时有一条准则:用尽可能简单的方法使对象进入正常状态;如果可以的,避免调用其他的方法。在构造器内唯一能够安全调用的方法是基类中的final方法(也适合于private)。这些方法不能被覆盖。
导出类中接口的扩展部分不能被基类访问,因此,向上转型,不能调用新方法。
向上转型会丢失具体类型信息,但向上转型是安全的。
在java中,所有的类型转换都会得到检查。所以即使我们知识进行一次普通的加括号形式的类型转型,在进入运行期仍会对其检查,以便保证它的确是我们希望的那种类型。否则,会返回一个ClassCastException异常。运行时类型识别RTTI。