Java核心技术第一卷学习笔记-第五章-继承

161 阅读5分钟

第五章 继承

类、超类和子类

继承使用extends关键字,如下所示。

public class Manager extends Employee{
    ...
}

Manager继承了Employee,其中,Manager被称为子类,Employee被称为超类。在Java中,所有类的继承都是公有继承,且没有多继承。子类继承了超类的所有域和方法,但是子类无法访问超类的私有域(private关键字修饰的域)。

子类调用父类的方法使用super,如下所示。

super.getSalary();

在Manager的方法中写上述语句则调用Employee的getSalary方法。super不同于this,super的作用是告诉编译器调用超类的方法。在子类中定义相同函数签名的函数即可实现对父类函数的覆盖,同时可以加上@override来显式指明被覆盖的超类函数。

在子类的构造器中,使用形如super(11.3,"Nelson");可以初始化超类,若在子类的构造器中不给出上述语句,则编译器自动调用超类默认构造器,即没有参数的构造器。

超类的对象变量可以引用子类的对象,编译器知道超类的对象变量引用的是超类的还是子类的对象,从而确定了使用超类的对象变量调用的函数是超类的函数还是子类的函数,实现了多态和动态绑定。也就是说,如果一个超类的对象变量引用了子类的对象,那么使用这个对象变量调用的函数就是子类的函数。同时,调用的函数也必须是超类已经声明的函数。

对象调用方法的过程如下:

  • 查看对象的声明类型和方法
  • 重载解析
  • 若是private,static,final,静态绑定
  • 动态绑定,调用对象变量最合适的方法。

阻止继承可以使用final关键字,如下所示。

public final class Manager extends Employee {
    ...
}

阻止方法被覆盖,也是使用final关键字修饰。

public final String getName() {
    ...
}

final类的所有方法均为final,但域不一定是final。

在类转换前,我们需要使用instanceof测试一下是否能够成功地转换。

if(staff instanceof Manager) {
    ...
} else {
    ...
}

abstract关键词可用于定义抽象类和抽象方法。

抽象方法可以不实现。

public abstract String getDescription();

但是,abstract的方法必须位于abstract内,使用abstract修饰对象即可使对象成为抽象类。

public abstract class Person {
    ...
}

抽象类不可以被实例化。

Java有3个用于控制类的域可见性的修饰符。

  1. 仅对本类可见:private
  2. 对所有类可见:public
  3. 对本包和所有子类可见:protected
  4. 对本包可见:默认,不需要修饰符

Object:所有类的超类

Java中的每一个类都由Object扩展而来,通常缺省,不需要显式写出继承自Object,如下所示。

public class Employee extends Object{
    ...
}

和下面的语句是等效的。

public class Employee {
    ...
}

在Java中,只有基本类型不是对象,基本类型包括数值,字符,布尔类型。注意,所有的数组都是类,如int []

equals

Object.equals(Object otherObject)通常用于判断两个类是否相当,在子类中应该对其覆盖。equals应该具有下列4种性质。

  1. 自反性。x.equals(x)返回true。
  2. 对称性。x.equals(y) == true当且仅当y.equals(x)
  3. 传递性。若x.equals(y) == true, y.equals(z) == true,则x.equals(z) == true
  4. 对于任何非空对象变量x,x.equals(null) == false

equals的实现方法一般如下。

  1. 判断this和otherObject。
  2. 判断otherObject和null。
  3. 若子类有自己相等的语义,则判断getClass()和otherObject.getClass(),即判断是否是同一个类。若子类的相等语义遵循超类的语义,那么使用instanceof判断otherObject的类型。
  4. 将otherObject转换为对应的类型,通过Objects.equals(a,b)判断this和otherObject所有域的关系。使用Objects.equals(a,b)的目的是为了null安全。

数组的比较可使用Array.equals(a,b)

hashCode

hashCode计算由对象导出的整数值,equals返回true,则hashCode必须相等。

toString

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

toString的格式:"类名[域的值]",类名=getClass().getName()

当对象与一个字符串通过”+“连接时,Java自动调用toString方法。

数组的toString并不是打印其中的内容。

泛型数组ArrayList

ArrayList的容量可以自增长。

void set(int index, E obj)
E get(int index)
void add(int index, E obj)
void add(E obj)
E remove(int index)

对象包装器

对象包装器是将基本类型转换为对象,有Integer,Long,Float,Double,Short,Byte,Character,Void,Boolean。对象包装器是不可变的,一旦构造包装器,就不可以改变其中的值。但是对象包装器可以参与运算,由运算改变其中的值。

<>中的类型不可以是基本类型。ArrayList<int>错误,ArrayList<Integer>正确。

假设定义了一个Integer的Arraylist,变量名是list。

  • list.add(1)实际上进行自动装箱,编译器将该语句转换为list.add(Integer.valueof(1))

  • int n = list.get(0))实际上进行了自动拆箱,编译器将该语句转换为list.get(0).intValue()

装箱和拆箱是编译器自动执行的,其达到的效果是Integer,Double等可以像int,double那样直接运算。

可变参数

使用变量名... args即可定义可变参数,args是一个数组,如printf的定义。

public PrintStream printf(string fmt, Object...args)

枚举类

待整理。

反射

看不懂。