第5章 继承

类、超类和子类

Object:所有类的超类

泛型数组列表

对象包装器和自动装箱

参数数量可变的方法

枚举类

反射

继承设计的技巧

5.1 类、超类和子类

关键字extends表明正在构造的新类派生于一个已存在的类。已存在的类称为超类(super class)、基类(base class)或父类(parent class);新类称为子类(subclass)、派生类(derived class)或孩子类(child class)。

在父类中的方法,子类不一定适用,需要提供一个新的方法来覆盖(override)超类中的这个方法。

调用父类的方法可以通过super解决这个问题,调用父类的构造,仅限子类构造中,super()。

注释:有些人认为super与this引用是类似的概念,实际上,这样比较并不太恰当。这是因为super不是一个对象的引用,不能将super赋值给另一个对象变量,他只是一个指示编译器调用超类方法的特殊关键字。

一个对象变量(例如,变量e)可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动选择调用哪个方法的现象称为动态绑定(dynamic binding)。

5.1.1 继承层次

由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy);在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链(inheritance chain)。

5.1.2 多态

"is-a" 规则的另一种表述法是置换规则。他表明程序中出现超类对象的任何地方都可以用子类对象置换。

在Java程序设计语言中,对象变量是多态的。

5.1.3 动态绑定

1)编译器查看对象的声明类型和方法名。

2)接下来,编译器将查看调用方法时提供的参数类型。

3)如果是private方法、static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,我们将这种调用方式称为静态绑定(static binding)。

4)当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。

虚拟机预先为每个类创建了一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。

5.1.4 阻止继承:final类和方法

不允许扩展的类被称为final类。

类中的特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法(final类中的所有方法自动地成为final方法)。

将方法或类声明为final主要目的是:确保它们不会在子类中改变语义。

5.1.5 强制类型转换

只能在继承层次内进行类型转换。

在将超类转换成子类之前,应该使用instanceof进行检查。

5.1.6 抽象类

从某种角度看,祖先类更加通用,人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。

抽象类不能被实例化。也就是说,如果将一个类声明为abstract,就不能创建这个类的对象。

5.1.7 受保护的访问

java用户控制可见性的4个访问修饰符:

1)仅对本类可见——private。

2)对所有类可见——public。

3)对本包和所有子类可见——protected。

4)对本包可见——默认(很遗憾),不需要修饰符。

5.2 Object:所有类的超类

Object类是Java中所有类的始祖,在java中每个类都是由它扩展而来的。

可以使用Object类型的变量引用任何类型的对象。

在java中,只有基本类型(primitive types)不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展与Object类。

5.2.1 equals方法

Object类中的equals方法用于检测一个对象是否等于另外一个对象。

5.2.2 相等测试与继承

java语言规范要求equals方法具有下面的特性:

1)自反性:对于任何非空引用x,x.equals(x)应该返回true。

2)对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。

3)传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。

4)一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。

5)对于任何非空引用x,x.equals(null)应该返回false。

不同的情况:

如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。

如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

下面给出编写一个完美的eauals方法的建议:

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 intanceof ClassName)) return false;

5)将otherObject转换为相应的类类型变量:

ClassName other = (ClassName) otherObject;

6)现在开始对所有需要比较的域进行比较。使用==比较基本数据类型,使用equals比较对象域。如果所有的域都匹配,就返回true;否则返回false。

5.2.3 hashCode方法

如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

不过,在java7中还可以做两个改进。首先最好使用null安全的方法Objects.hashCode。如果其参数为null,这个方法会返回0,否则返回对参数调用hashCode的结果。

eauals与hashCode的定义必须一致:如果x.eauals(y)返回true,那么x.hashCode()必须与y.hashCode()具有相同的值。

5.2.4 toString方法

用于返回表示对象值的字符串。

5.3 泛型数组列表

ArrayList

ArrayList<Employee> staff = new ArrayList<Employee>();

5.4 对象包装器与自动装箱

所有的基本类型都有一个与之对应的类。这些对象包装器拥有很鲜明的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个类派生于公共的超类Number)。对象包装器类是不可变的,类还是final。

自动装箱(基本类型自动转换成包装类),自动拆箱(包装类自动转换为基本类型)。

5.5 参数变量可变的方法

这里的省略号...是java代码的一部分,它表明这个方法可以接收任意数量的的对象。

允许将一个数组传递给可变参数方法的最后一个参数。

5.6枚举类

public enum Size{SMALL,MEDIUM,LARGE,EXTRA_LARGE}

实际上,这个声明定义的类型是一个类,他刚好有4个实例。

因此在比较两个枚举类型的值时,永远不需要调用equals方法,而直接使用"=="就可以了。

如果需要的话,可以在枚举类型中添加一些构造器、方法和域。当然、构造器只是在构造枚举常量的时候被调用。

5.7 反射

能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,在下面可以看到,反射机制可以用来:

在运行中分析类的能力。

在运行中查看对象。

实现通用的数组操作代码。

利用Method对象,这个对象很像C++中的函数指针。

5.7.1 Class类

Object类中的getClass()方法将会返回一个Class类的实例。

类名.class获得一个Class类的实例。

Class.forName加载一个类。

虚拟机为每个类管理一个Class对象。因此,可以利用==运算符实现两个类对象比较的操作。

5.7.2 捕获异常

5.7.3 利用反射分析类的能力

在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。

这三个类都有一个叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能够报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。例如,可以使用Modifier类中的isPubli、isPrivate或isFinal判断方法或构造器是否是public、private或final。另外,还可以利用Modifier.toString方法将修饰符打印出来。

Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

5.7.4 在运行是使用反射分析对象

5.7.5 使用反射编写泛型数组代码

5.7.6 调用任意方法

利用Method类中的invoke方法。

5.8 继承设计的技巧

1)将公共操作和域放在超类。

2)不要使用受保护的域。

3)使用继承实现"is-a"。

4)除非所有继承的方法都有意义,否则不要使用继承。

5)在覆盖方法时,不要改变预期的行为。

6)使用多态,而非类型信息。

无论什么时候,对于下面这种形式的代码

if(x is of type1)

    action1(x);

else if(x is of type2)

    action2(x);

都应该考虑使用多态性。

7)不要过多地使用反射。

 

 

 

原文地址:https://www.cnblogs.com/xuzhen97/p/7724366.html