第五章、继承
1、子类,超类
1.1子类构造器
一个对象变量可以指示多种实际类型的现象称为多态。在运行时能自动选择适当的方法,称为动态绑定。
2.2多态
在Java程序设计语言中,对象变量是多态的,一个Emp类型的变量既可以引用一个Emp类型的对象,也可以引用Emp类的任何一个子类的对象(例如Manager、Secretary)。
2.3方法调用
编译器查看对象的声明类型和方法名,如果遇到多个名字相同的但参数不一样的方法,编译器会一一列举所有的方法和其超类中所有可能被调用的候选方法。接下来,编译器会确定方法调用中提供的参数类型,如果在方法中存在一个与所提供参数类型完全匹配的方法,就会确定选择这个方法。这个过程称为重载解析。
如果是private、static、final方法,那么编译器将可以准确的知道应该调用哪个方法。这称为静态绑定。
2.4阻止继承:final类和方法
有时候,我们可能希望阻止人们利用某个类定义子类。不允许扩展的类称为final类。如果在定义类的时候使用了final修饰符就表明这个类是final修饰符。
类中的某个特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法,例如:
public class Emp
{
...
public final String getName()
{
return name;
}
...
}
2.5强制类型转换
进行强制类型转换的唯一原因是:要在暂时忽视对象的实际类型之后使用对象的全部功能。
需要注意的是,只能在继承层次内进行强制类型转换。
2.6抽象类
如果自下而上在类的基础层次结构中上移,位于上层的类更有一般性,可能更加抽象。
抽象方法充当着占位方法的角色,它们在子类中具体实现。扩展抽象类可以有两种选择。一种是在子类中保留抽象类中的部分或所有抽象方法仍未定义,这样就必须将子类记为抽象类;另一种做法是定义全部方法,这样一来,子类就不是抽象的了。
2.7受保护访问
在有些时候,可能希望限制超类中的某个方法只允许子类访问,或者更少见的,可能希望允许子类的方法访问超类的某个字段。为此,需要将这些类方法或字段声明为受保护(protected)。
2、Object:所有类的超类
Object类是Java中所有类的始祖,在Java中每个类都扩展了Object。
2.1 equals方法
该方法用于检测一个对象是否等于另一个对象。
2.2相等测试与继承
如果隐式和显式的参数不属于同一个类,equals如何处理是一个很有争议的问题。下面给出编写一个完美的equals方法的建议:
(1)显式参数命名为otherObject,稍后需要将它强制转换成另一个名为other的变量。
(2)检测this与otherObject是否相等:if(this == otherObject) return true;
(3)检测otherObject是否为null,如果为null,返回false。这项检测是很必要的。
if(otherObject ==null) return false;
(4)比较this与otherObject的类,如果equals的语义可以在子类中改变,就使用getClass检测:
if(getClass() != otherObject.getClass()) return false;
如果所有的子类都有相同的相等性语义,可以使用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
(5)将otherObject强制转换成相应类类型的变量:ClassName other = (ClassName) otherObject
(6)现在根据相等性概念的要求来比较字段。
2.3 hashCode 方法
散列码(hash code)是由对象导出的一个整型值。每个对象都有一个默认的散列码,其值由对象的存储地址得出。
2.4 toString 方法
该方法会返回表示对象值的一个字符串。
3、对象包装器与自动装箱
有时,需要将int这样的基本类型转换为对象。所有的基本类型都有一个对应的类。
由于每个值分别包装在对象中,所以ArrayList<Integer>
的效率远远低于int数组。因此,只有程序员操作的方便性比执行效率更重要的时候,才会考虑对较小的集合使用这种构造。
幸运的是,有一个很有用的特性,从而可以很容易的向ArrayList<Integer>
添加int类型的元素。下面这个调用list.add(3);
将自动的变换为list.add(Integer.valueOf(3))
这种变换称为自动装箱。
相反的,当将一个Integer对象赋给一个int值时,将会自动拆箱。也就是说,编译器将以下语句:int n = list.get(i);
转换成int n = list.get(i).intValue();
自动装箱和拆箱甚至也适用于算术表达式。
最后要强调的是,自动装箱和自动拆箱是编译器要做的工作,而不是虚拟机。
4、枚举类
在第三章已经看到如何定义枚举类型。下面是一个经典例子:
public enum Size{ SMALL, MEDIUM, LARGE }
实际上,这个声明定义的类型是一个类,且有三个实例,不能再构造新的对象了。因此在比较两个枚举类型的值时,不需要调用equals,直接用==就可以了。
5、反射
反射库提供了一个丰富且精巧的工具集,可以用来编写能够动态操纵Java代码的程序。使用反射,Java可以支持用户界面生成器、对象关系映射器以及很多其他需要动态查询能力的开发工具。
能够分析类能力的程序称为反射。反射机制可以用来:在运行时分析类的能力、在运行时检查对象、实现泛型数组操作代码、利用Method对象。
在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。不过有一个特殊的Java类访问这些信息。保存这些信息的类名为Class。该对象会描述一个特定类的属性。最常用的Class方法就是getName。这个方法将返回类的名字。