继承is- a
5.1类、超类、子类
- extend:表明正在构造的新类派生于一个已存在的类。此处已存在的类称为超类(基类、父类),新类称为子类(派生类、孩子类)
- 覆盖:子类的方法与超类的方法同名但不同功能。覆盖方法时,要保证返回类型的兼容性,允许子类将覆盖方法的返回类型定义为原返回类型的子类型。一定要保证覆盖方法不低于被覆盖方法的可见性。
- super:要在子类覆盖超类的方法中调用超类的同名被覆盖方法,则要用super.func();还可以用super()放在子类构造器第一句,调用超类构造器。
- this:引用隐式参数(可以说成调用被局部变量同名覆盖的域);放在构造器的第一句调用该类的其他构造器
- 多态:一个对象变量可以指示多种实际类型
- 动态绑定:在运行时依赖于隐式参数的实际类型来选择调用哪个方法(Java中不需要将方法声明为虚拟方法,默认就是动态绑定的处理方式,可以通过标记方法为final阻止其动态绑定)
- 静态绑定:编译器在编译时就能确定调用哪个方法。private、static、final方法和构造器
- 继承层次:由一个公共超类派生出来的所有类的集合(Java不支持多继承)
- 继承链:从某个特定的类到其祖先的路径称为该类的继承链
- final:修饰不许被继承的类或不许被覆盖的方法。final类的所有方法自动成为final方法,但域不会自动变成final域
- 置换法则:出现超类对象的任何地方都可以用子类对象置换
- x.f(args) 方法调用过程:
- 编译器查看对象的声明类型和方法名
- 编译器查看调用方法时提供的参数类型
- 如果是private、static、final方法或构造器,则编译器可以确定该调用哪个方法(静态绑定)
- 当程序运行且采用动态绑定调用时,虚拟机一定调用与对象变量x实际所引对象类型最合适的类的方法。
- 类型转换:子类的引用可以赋给超类变量,但要将超类的引用赋给子类变量就需要进行类型转换。类型转换只能在继承层次内,并且超类转换成子类之前,应该用instanceof先检查。一般情况下要尽量少用类型转换。
- 抽象类:abstract修饰,不能new创建对象,但可以创建对象变量,引用子类对象。包含抽象方法的类一定是抽象类,但抽象类也可以包含具体方法和域。
- public:对所有类可见
- protected:对本包和所有子类可见
- 默认:对本包可见(没有修饰符)
- private:仅对本类可见
5.2 Object:所有类的超类
- Java中每个类都是由Object类扩展而来的,可以使用Object类型变量引用任何类型的变量
- Java中只有基本类型(数值、字符、布尔)不是对象,数组、字符串等都是对象
Object类的方法:hashCode()、toString()、equals()
- hashCode():返回对象哈希值。相等的对象,其哈希值也相等。
- toString():返回表示对象值的字符串(类名:[域值])。可用于调试。对象与字符串通过操作符“+”连接时,Java编译器自动调用toString()方法
- equals():两个对象状态相等
- Objects.equals(a,b):若a,b均为null,返回true;如果仅其中一个为null,返回false;否则调用a.equals(b)
- 在子类中定义覆盖超类的equals()方法时,先调用超类equals(),如果超类中的域都相当,就需要比较子类中的实例域(这时候需要把Object类型的形参强制转换为子类类型)
public class Employee {
...
@ Override public boolean equals(Object otherObject){
if(this==otherObject) return true;
if(otherObject==null) return false;
if(getClass()!=otherObject.getClass()) return false;
Employee other=(Employee) otherObject;
return Objects.equals(name,other.name)&&salary==other.salary&&Objects.equals(hireDay,other.hireDay);
...
}
public class Manager extends Employee {
...
public boolean equals(Object otherObject){
if(!super.equals(otherObject)) return false;
Manager other=(Manager) otherObject;
return bonus==other.bonus;
}
...
}
5.3 泛型数组列表Generic Array List
- Java允许在运行时确定数组的大小
- ArrayList是一个采用类型参数的泛型类,需要用“<类名>”指定保存的元素类型(基本类型要先装箱/包装才能建立ArrayList)
- 用get()、set()实现访问、改变数组元素,不用“[]”语法格式:staff.set(i,harry);将第i个元素值设置为harry,0<= i < staff.size()
- 用add()添加新元素,set()只能替换已经存在的元素的值
5.4 对象包装器与自动装箱
- 包装器wrapper:与基本类型对应的类Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean
- 对象包装器类不可变,一旦构造了包装器,包含在包装器中的内容就不会改变,不能使用这些包装器类创建修改数值参数的方法,如果选编写一个修改数值参数的方法,需要使用在org.omg.CORBA包中定义的holder类型(IntHolder,BooleanHolder…),每个holder类型都包含一个public域,通过这个public域可以访问存储在其中的值
- 包装器类是final类,不能被继承
- 关于基本类型和包装类拆箱、装箱以及判断相等的理解见博客
- 装箱和拆箱是编译器认可的,编译器在生成类的字节码时,插入必要的方法调用,虚拟机只是执行这些字节码
5.5 参数个数可变的方法 varargs methods:funcname(type… paras){ }
- 省略号 ”…” 是Java代码的一部分,表明这个方法可以接收任意数量的对象(Object[])
- 允许将一个数组传递给可变参数方法的最后一个参数,还可以将已经存在且最后一个参数是数组的方法重新定义为可变参数的方法(main(String[] args) -> main(String… args))