5. 继承

5. 继承

利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。

反射是指在程序运行期间发现更多的类及其属性的能力。

5.1 类、超类和子类

5.1.1 定义子类

public class Student extends Person{
    //方法和域
}

关键字extends表名正在构造的新类派生于一个已存在的类。已存在的类成为超类、基类或父类;新类称为子类、派生类或孩子类。

5.1.2 覆盖方法

在子类中可以增加域、增加方法或覆盖超类的方法,但是绝对不能删除集成的任何域和方法。

在Java中使用关键字super调用超类的方法。

5.1.3 多态

多态:同一类的对象收到相同消息时,会得到不同的结果。而这个消息是不可预测的。多态,顾名思义,就是多种状态,也就是多种结果。

多态性是一种通过多种状态或阶段描述相同对象的编程方式。它的真正意义在于:实际开发中,只要关心一个接口或基类的编程,而不必关心一个对象所属于的具体类。

在Java程序设计语言中,对象变量是多态的。一个Student变量既可以引用一个Student类对象,也可以引用一个Student类的任何一个子类的对象。

在Java中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。

5.1.4 阻止继承:final类和方法

不允许扩展的类被称为final类。如果在定义类的时候使用了final修饰符就表名这个类是final类。

5.1.5 抽象类

如果自下而上在类的继承层次结构中上移,位于上层的类更具有通用性,甚至可能更加抽象。

使用abstract关键字实现抽象类。

建议尽量将通用的域和方法放在超类中。

扩展抽象类的两种选择:在抽象类中定义部分抽象类方法或不定义抽象类方法,这样就必须将子类也标记为抽象类;定义全部的抽象方法。

  • 类即使不含抽象方法,也可以将类声明为抽象类。
  • 抽象类不能被实例化。也就是说,如果将一个类声明为abstract,就不能创建这个类的对象。

5.1.6 受保护访问

  • 仅对本类可见--private
  • 对所有类可见--public
  • 对本包和所有子类可见--protected
  • 对本包可见--默认,不需要修饰符

5.2 Object:所有类的超类

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

如果没有明确地指出超类,Object就被认为是这个类的超类。

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

Object obj = new Employee("Hello World", 35000);

Object类型的变量只能用于作为各种值得通用持有者。要想对其中的内容进行具体的操作,还需要清楚对象的原始类型,并进行响应的类型转换:

Employee e = (Employee)obj;

所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类。

5.2.1 equals方法

Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用

在子类中定义equals方法时,首先调用超类的equals。如果检测失败,对象就不可能相等。如果超类中的域都相等,就需要比较子类中的实例域。

5.2.2 相等测试与继承

Java语言规范要求equals方法具有以下特性:

  • 自反性:对于任何非空引用x,x.equals(x)应该返回true
  • 对称型:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true
  • 传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true
  • 一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果
  • 对于任意非空引用x,x.equals(null)应该返回false

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

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

Java核心技术给出的equals方法建议:

  1. 显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量

  2. 检测this与otherObject是否引用同一个对象:if(this == otherObject) return true;

  3. 检测otherObject是否为null,如果为null,返回false。这项检测是很必要的。if(otherObject == null) return false;

  4. 比较thisotherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:if(getClass != otherObject.getClass()) return false;

    如果所有的子类都拥有统一的语义,就是用instanceof检测:if(!(otherObject instanceof ClassName)) return false;

  5. otherObject转换为相应的类类型变量:ClassName other = (ClassName)otherObject

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

如果在子类中重新定义equals,就要在其中包含调用super.equals(other)

5.2.3 hashCode方法

散列码(hash code)是由对象导出的一个整形值。散列码是没有规律的。如果x和y是两个不同的对象,x.hashCode()y.hashCode()基本上不会相同。

由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。

最好使用null安全的访法Objects.hashCode。如果其参数为null,这个方法会返回0,否则返回对参数调用hashCode的结果。另外,使用静态方法:Double.hashCode可以避免创建Double对象。

5.2.3 toString方法

用于返回表示对象值的字符串。toString方法也可以供子类调用。

5.3 对象包装器与自动装箱

包装器(wrapper):Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个派生于公共的超类Number)。对象包装器类是不可变的,一旦构造了包装器,就不允许更改包装在其中的值。同时包装器类还是final,因此不能定义它们的子类。

ArrayList<Integer> list = new ArrayList<>();
list.add(3); //  =>  list.add(Integer.valueOf(3))  自动装箱

int n = list.get(i); //  = > int n = list.get(i).intValue();   自动拆箱

image-20200902005901773

image-20200902005918160

5.4 枚举类

先看一个例子:

public enum Size {
    SMAL,MEDIUM,LARGE,EXTRA_LARGE;
}

这个声明定义的类型是一个类,有四个实例。

所有的枚举类型都是Enum类的子类。它们继承了Enum类的许多方法。包含toString,它能够返回枚举常量名。例如Size.SMALL.toString()将返回字符串"SMALL"toString的逆方法是静态方法valueOf,例如:Size s = Enum.valueOf(Size.class,"SMALL");

每个枚举类型都有一个静态的values方法,它将返回一个包含全部枚举值的数组。

image-20200902005828729

5.5 反射

能够分析类能力的程序称为反射。反射功能及其强大:

  • 在运行时分析类的能力
  • 在运行时查看对象
  • 实现通用的数组操作代码
  • 利用Method对象。

5.5.1 Class类

Object类中的getClass()方法将会返回一个Class类型的实例。Class用于保存类的信息。

可以调用静态方法forName获得类名对应的Class对象。

5.5.2

5.6 继承的设计技巧

  1. 将公共操作和域放在超类
  2. 不要使用受保护的域
  3. 使用继承实现“is-a”关系
  4. 除非所有继承的访法都有意义,否则不要使用继承
  5. 在覆盖方法时,不要改变预期的行为
  6. 使用多态,而非类型信息
  7. 不要过多的使用反射
都说酒是陈的香。
原文地址:https://www.cnblogs.com/yihangjou/p/13599648.html