关于JAVA核心技术(卷一)读后的思考(继承中的类、子类和超类)
前面说过类之间的关系有一种便是继承,其中“is-a”关系是继承的一个明显特征。举个例子有一个类是Employee,还有一个类是Manager类,Employee表示的是员工,而Manager表示的是老板,即管理层,而作为管理层也是员工,所以可以说这两者之间具有"is-a"这种关系。也就是说Manager是Employee的子类。
定义子类:
这里要用到一个关键字extends来表示继承。
用上面的Employee例子就是:
public class Manager extends Employee{
……
}
extends表明正在构造的新类派生于一个已存在的类。已存在的类成为超类;新类称为子类。
在Manager类中有一个奖金信息的方法,具体为:
public class Manager extends Employee {
private double bonous;
public void setBonous(double b) {
bonous =b;
}
}
即这个方法,在超类中并不存在,而在子类中存在,而在子类中并未显式地定义部分超类的方法,但仍然可以使用,因为Manager自动继承了超类Employee的方法。
而对于构造函数有这样一个要求:
因为考虑到这些实例域都是private定义的,所以后面又把重新定义一边仍然报错,所以从侧面证明了这个原理
方法覆盖
超类的有些方法子类并不适用,所以这个时候就会进行提供一个新的方法来覆盖。
子类的方法是不允许直接放为超类的私有域的,也就是说,尽管Manager对象都拥有一个名叫salary的域,但在Manager类的getSalary方法并不能直接访问salary域,必须调用超类中的公有的接口,才能使用通过。使用时,要加上super.getSalary,否则会无限调用自己,造成出错。
子类可以增加域、增加方法或者覆盖超类的方法,却不能删除继承的任何域和方法
子类构造器
代码分析:
public Manager(String name,double salary,int year,int month,int day) {
super(name,salary,year,month,day);
bonous=0;
}
其中的 super(name,salary,year,month,day);是调用超类的含对应参数的构造器。
对该部分私有域进行初始化。我们用super实现对超类构造器的调用
super调用构造器的语句必须是子类构造器的第一句
如果子类的构造器没有显示地调用超类的构造器,则将自动地调用超类默认的构造器,若超类无无参数构造器,则会报错。
package com.java.inheritance;
public class ManagerTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Manager boss =new Manager("Carl Cracker",80000,1987,12,15);
boss.setBonous(5000);
Employee[] staff = new Employee[3];
staff[0]=boss;
staff[1]=new Employee("Harry Hacker",50000,1989,10,1);
staff[2]=new Employee("Tommy Teter",40000,1990,3,15);
for(Employee e:staff)
System.out.println("name="+e.getName()+",salary="+e.getSalary());
}
}
package com.java.inheritance;
import java.time.*;
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name,double salary,int year,int month,int day)
{
this.name=name;
this.salary=salary;
hireDay=LocalDate.of(year, month, day);
}
/* public Employee() {
}*/
public String getName(){
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent) {
double raise =salary*byPercent/100;
salary+=raise;
}
}
package com.java.inheritance;
import java.time.LocalDate;
public class Manager extends Employee {
private double bonous;
public Manager(String name,double salary,int year,int month,int day) {
super(name,salary,year,month,day);
bonous=0;
}
public double getSalary() {
double baseSalary=super.getSalary();
return baseSalary+bonous;
}
public void setBonous(double b) {
bonous =b;
}
}
其中循环中有e.gatSalary()
调用能够确定应该执行那个getSalary方法。尽管e声明为Employee类型,但实际是e技能引用Employee类型的对象,也可以引用Manager类型的对象。引用的是什么对象就是调用的是什么方法。
一个对象变量可以指示多种实际类型的现象称为多态,在运行时能够自动的选择调用哪个方法的现象称为动态绑定。
继承层次
继承并非限于一个层次。eg:Manager类派生Executive类。由一个公共超类派生出来的的所有类的集合称为继承层次,其中不管派生了几次都称为该类的继承层次。
在继承层次中,由某个特定的类到祖先的路径称为该类的继承链。
多态
"is-a"规则是判定是否应该设计为继承关系的简单规则,即每个子类对象都是超类的对象。
"is-a"规则是另一种表述法是置换法则,即两个对象进行置换观察是否任然满足条件。
对象变量是多态的,一个Employee变量既可以引用Employee对象,也可以引用一个Employee的任何一个子类的对象。
Employee[] staff = new Employee[3];
staff[0]=boss;
这是允许的,但编译器将staff[0]堪称Employee对象,
意味着可以这样调用:
boss.setBonous(5000);
但不能
staff[0].setBonus(5000);
因为staff[0]声明类型是Employee,故不能调用子类方法。
同时不允许将超类的引用赋值给子类变量。
阻止继承:final类和方法
不允许扩展的类称为final类。
子类不允许覆盖的方法在前加final
强制类型转换
基本数据类型转换:
低——>高:直接转换
高——>低:强制类型转换
方式:(类型名)变量名
类:子类类型 变量名=(超类类型) 变量名
超累类型 变量名=子类变量名 即:直接转换
抽象类
关键字:abstract
即在超类中增加一个方法加上abstract,即可以不用实现这个方法
而包含一个或多个抽象方法的类必须被声明为抽象的
抽象方法充当着占位的角色,它的具体实现在子类中,扩展抽象类可以有两种选择,一种是在抽象类中定义部分抽象类方法或不定义抽象类方法,这样必须将子类也标记为抽象类。
另一种是定义全部的抽象类方法
类即使不含抽象类方法,也可以将类声明为抽象类。
抽象类不能实例化,即不能创建对象,但可以创建子类变量
受保护访问
关键字:protect
使用这个关键字声明的实例域和方法,可以被该类的子类直接调用。
不提倡使用
访问修饰符总结总结
(1)仅本类可以见——private
(2)对所有类可见——public
(3)对本包和所有子类可见——protect
(4)对本包可见——默认(无修饰符)