本文首发于个人博客——然Coder
在之前讨论过接口这种面向对象的特性,这里我们来看看另一个面向对象的特性——继承。
继承,是在现有的类的基础上派生出另一个类,他可以从现有类中继承得到(或者叫重用)方法,并且还可以再原有的基础上进行适当扩展方法和域。
我们定义一个 Employee
类,将其作为公司中所有员工的父类:该类是其他所有员工的基础信息类,不同员工有不同的属性和行为。
public class Employee {
public String ID;
public String name;
public Date enterTime;
public int status;
public Employee() {}
public Employee(String ID, String name, Date enterTime, int status) {
this.ID = ID;
this.name = name;
this.enterTime = enterTime;
this.status = status;
}
public boolean checkedIn() {
System.out.println("员工打卡上班。");
return true;
}
public boolean checkedOut() {
System.out.println("员工打卡下班。");
return true;
}
}
现在我们建立一个经理类 Manager
类,让其继承 Employee
类,该类就叫做 Employee
的子类。
import java.util.Date;
public class Manager extends Employee {
public String departmentID;
public int level;
public Manager(String ID, String name, Date enterTime, int status) {
super(ID, name, enterTime, status);
}
public void setDepartmentID(String departmentID) {
this.departmentID = departmentID;
}
public void setLevel(int level) {
this.level = level;
}
public static void main(String[] args) {
Manager boss = new Manager("1","Jason",new Date(),1);
System.out.println(boss.ID);
}
}
从上述的代码可以清晰地看出,在子类 Manager
中我们添加了两个其他的字段 : departmentID
,level
,用来存储经理特有的属性,同样我们也可以增加 Manager
特有的行为,比如安排工作等:
public void arrangeWork(Employee employee) {
System.out.println("为" + employee.name + "安排工作。");
}
另外,他本身也还是具有父类 Employee
中的非私有方法的,比如上下班打卡的方法,子类可以选择继承继续使用父类的实现,也可以自己重写覆盖父类的方法。
@Override
public boolean checkedIn() {
System.out.println("经理上班打卡。");
return true;
}
@Override
public boolean checkedOut() {
System.out.println("经理下班打卡。");
return true;
//or super.checkedOut();
}
在覆盖重写父类的方法时,也可以通过 super
关键字来调用父类的方法来辅助实现。
不同于
this
,super
不是对象的引用,而是绕过动态查找方法并调用特定方法的指令。
在进行父类方法覆盖的时候,应当注意方法名以及参数列表是不能更改的,返回值类型,可以用父类返回值的子类代替。
注意
覆盖一个方法的时候,子类方法与父类方法的可见性应当是至少一致的,当父类的方法声明为
public
时,在子类中如果要覆盖该方法,就需要也将方法 声明为public
,如果漏掉,编译器会提示访问权限变弱。
在之前的代码中,我们为 Manager
类提供了一个构造方法,用于构造一个 Manager
对象:
public String departmentID;
public int level;
public Manager(String ID, String name, Date enterTime, int status) {
super(ID, name, enterTime, status);
}
这里我调用了父类的构造方法,注意到, Manager
本身还有其他 Employee
不具有的属性,如果我们需要将该系列的属性赋初值,只需要在调用了父类的构造方法后,继续赋值就可以了:
父类的构造方法调用必须在构造方法中的第一条语句。
public Manager(String ID, String name, Date enterTime, int status, String departmentID, int level) {
super(ID, name, enterTime, status);
this.departmentID = departmentID;
this.level = level;
}
在 Java
中对一个父类赋予一个子类对象是合法的。
Employee employee = new Manager();
此时我们来调用 checkedIn()
方法:employee.checkedIn()
,是调用父类的方法还是子类的方法?
事实上,是会调用子类的方法。原因是 JVM
的动态方法寻找。
在调用方法时,
JVM
会查看对象的实际类型,并且定位方法的版本,这个过程叫做动态方法寻找
。
final
即为最终的意思,就是说它是最终的版本,不允许修改。
当一个类被声明为 final
后,将不允许有继承他;同样,一个类的方法被声明为 final
后也将不允许子类修改覆盖它。
一个类可以定义没有实现的方法,并且啊,强迫子类去实现,这样的方法和类就属于抽象方法和抽象类,,必须用 abstract
关键字修饰。
任何继承自抽象类的子类要么实现所有没有实现的方法,要么自己也声明为抽象类。
不同于接口,抽象类可以拥有实例变量和构造函数
和接口类似,抽象类不可能拥有本身的实例对象
个人微信公众号【然Coder】
本文首发于个人博客