Day12_2:继承和方法重写
继承和方法重写
任务:使用继承重新定义部门类
关键步骤如下。
➢收集类中的公共部分。
➢将公共部分抽象成新的类。
继承的基本概念
继承是面向对象的三大特性之一,继承可以解决编程中代码冗余的问题,是实现代码重用的重要手段之一。继承是软件可重用性的一种表现, 新类可以在不增加自身代码的情况下,通过从现有的类中继承其属性和方法,来充实自身内容,这种现象或行为就称为继承。此时新类称为子类,现有的类称为父类。继承最基本的作用就是使得代码可重用,增加软件的可扩充性。
Java中只支持单继承,即每个类只能有一个直接父类。
继承表达的是“XXis aXX”的关系,或者说是一种特殊和一般的关系,如Dog is a Pet。同样可以让“学生"继承“人”,让“苹果”继承“水果”,让“三角形”继承“几何图形”等。
继承的语法格式如下。
[访问修饰符] class <SubClass> extends <SuperClass>{
}
➢在Java中, 继承通过extends关键字实现,其中SubClass称为子类,SuperClass称为父类或基类。
➢访问修饰符如果是public,那么该类在整个项目中可见。
➢若不写访问修饰符,则该类只在当前包中可见。
➢在Java中,子类可以从父类中继承以下内容。
◆可以继承public 和protected修饰的属性和方法,不论子类和父类是否在同一个包里;
◆可以继承默认访问修饰符修饰的属性和方法, 但是子类和父类必须在同一个包里;
◆无法继承父类的构造方法。
继承的应用
若使用面向对象编写部门类,目前共有8个部门,需要定义8个类,各个部门有很多共同属性,导致很多代码都是一样的, 只有很少部分不一样, 如果使用继承,就可以对相同的代码实现重用,提高工作效率。
示例:请使用继承,将8个部门类中相同的代码抽取成一个 “部门类”
//父类为Department
public class Department {
private int ID;//部门编号
private String name="待定";//部门名称
private int amount=0;//部门人数
private String responsibility="待定";//部门职责
private String manager="无名氏";//部门经理
public Department(){
//无参构造方法
}
public Department(String name,String manager,String responsibility){
//带参构造方法
this.name= name;
this.manager=manager;
this.responsibility=responsibility;
}
public int getID(){
return ID;
}
public void setID(int id) {
this.ID= id;
}
//......省略其他setter/getter的代码
public void printDetail() {
System.out.printIn(" 部门:" + this.name + "\n经理: " + this.manager + "\n部门职责: "+this.responsibility+ "\*************);
}
}
示例的代码中将8个不同的部门子类的公共部分抽取成Department类,然后8个子类分别继承这个父类,就可以省去很多冗余的代码。至此,任务已基本完成。
任务 使用继承和重写完善类的结构
关键步骤如下。
➢使用extends关键字建立继承关系。
➢使用super关键字调用父类成员。
➢使用方法重写,写父类中的方法,输出子类自身的信息。
使用继承和重写实现部门类及子类
前面已经定义了Department 类,下面使用继承定义人事部类、研发部类。
1. 使用继承定义部门类及子类
示例:把人事部类、研发部类作为子类,继承Department类。
public class PersonelDept extends Department {
//人事部
private int count;//本月计划招聘人数
public PersonelDept(String name,String manager,String manager,String responsibility, int count){
super(name,manager, responsibility);
this.count=count;
}
public int getCount() {
return count;
}
public void setCount(int count){
this.count = count;
}
}
//以上代码为人事部类继承Department类,以下代码为研发部类继承Department类
public class ResearchDept extends Department
//研发部
private String speciality;//研发方向
public Research(String name,Sring manager,String responsibiiity,String speciality){
super(name,manager, responsibility);
this.speciality=speciality;
}
public ResearchDept(String speciality){
super(); //默认调用父类的无参构造方法
this.speciality=speciality;
}
public String getSpeciality() {
return speciality;
}
public void setSpeciality(String speciality) {
this.speciality = speciality;
}
}
通过示例可以看到,抽取父类Department后,子类中保留的代码都专属于该子类,和其他子类之间没有重复的内容。
2. 使用super关键字调用父类成员
当需要在子类中调用父类的构造方法时,可以如示例中的代码那样使用super关键字调用。
当函数参数或函数中的局部变量和成员变量同名时,成员变量会被屏蔽,此时若要访问成员变量则需要用“this. 成员变量名”的方式来引用成员变量。super 关键字和this关键字的作用类似,都是将被屏蔽了的成员变量、成员方法变得可见、可用,也就是说,用来引用被屏蔽的成员变量或成员方法。不过,super 是用在子类中,目的只有一个,就是访问直接父类中被屏蔽的内容,进一步提高代码的重用性和灵活性。super 关键字不仅可以访问父类的构造方法,还可以访问父类的成员,包括父类的属性、一般方法等。通过super访问父类成员的语法格式如下。
访问父类构造方法: super( 参数)
访问父类属性/方法: super.< 父类属性/方法>
➢super只能出现在子类(子类的方法和构造方法)中,而不是其他位置。
➢super 用于访问父类的成员,如父类的属性、方法、构造方法。
➢具有访问权限的限制,如无法通过super访问父类的private 成员。
示例:在人事部类中使用super关键字调用Department类中的方法。
分析如下。
抽取人事部类、研发部类的公共属性和方法等。
//父类: Department
public class Department {
public Department(String name,String manager,String responsibility){
this.name=name;
this.manager=manager;
this.responsibility=responsibility;
}
//....省略父类的属性、settr/getter()方法等代码
public void printDetail(){
System.out.printIn(" 部门:"+ this.name+"\n经理:"+this.manager+"\n部门职责:"+this.responsibility+"\n******");
}
}
//以上为父类Department部分代码,以下为人事部类部分代码
public class PersonelIDept extends Department {
private int count;//本月计划招聘人数
//...省略父类的属性、setter/getter( )方法等代码
public PrsonelDept(String name, String manager,String responsibility,
int count){
super(name,manager, responsibility); //super调用父类构造方法
this.count=count;
}
}
//以上为父类与子类部分代码,以下为调用类部分代码
public static void main(String[] args){
PersonelDept pd=new PersonelDept("人事部","王经理","负责公司的人才招聘和培训。",10);
pd.printDetail();
3.实例化子类对象
在java中一个类的构造方法在如下两种情况下总是会被执行。
➢创建该类的对象(实例化)。
➢创建该类的子类的对象(子类的实例化)。
因此子类在实例化时会首先执行执行其父类的构造方法,然后才执行子类的构造方法。换言之,当在Java语言中创建一个对象时,Jav!虚拟机会按照父类一 子类的顺序执行一系列的构造方法。子类继承父类时构造方法的调用规则如下。
(1)如果子类的构造方法中没有通过super 显式调用父类的有参构造方法, 也没有通过this 显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,是否写“super();”语句,效果是一样的。
(2)如果子类的构造方法中通过super 显式地调用了父类的有参构造方法,那么将执行父类相应的构造方法,而不执行父类无参构造方法。
(3)如果子类的构造方法中通过this显式地调用了自身的其他构造方法,在相应构造方法中遵循以上两条规则。
特别需要注意的是,如果存在多级继承关系,在创建一个子类对象时, 以上规则会多次向更高级父类传递,一 直到执行*父类 Objet类的无参构造方法为止。
下面通过一个存在多 级继承关系的示例,更深入地理解继承条件下构造方法的调用规则,即继承条件下创建子类对象时的系统执行过程。
示例:将人类作为父类,学生类继承人类,研究生类继承学生类。创建对象时调用不同的构造方法,观察输出结果。
//人类作为父类
class Person {
String name;//姓名
public Person() {
System.out.println("execute Person()");
}
public Person(String name) {
this.name = name;
System.out.println("execute Person(name)");
}
}
//学生类作为Person 的子类
class Student extends Person {
String school;//学校
public Student() {
System.out.println("execute Student() ");
}
public Student(String name, String school) {
super(name);//显式调用父类有参构造方法,将不执行无参 构造方法
this.school = school;
Systenm.out.println("execute Student(name,school)");
}
}
//研究生类作为Student的子类
class PostGraduate extends Student {
String guide;//导师
public PostGraduate(){
System.out.printIn("execute PostGraduate()");
}
public PostGraduate(String name,String school, String guide) {
super(name, school);
this.guide = guide;
System.out.println("execute PostGraduate(name,school,guide)");
}
}
//main( )方法程序的入口
class Test{
public static void main(String[] args) {
PostGraduate pgdt = null;
pgdt =new PostGraduate();
System.out.println();
pgdt new PostGraduate(" 刘致同","北京大学”,"王老师");
}
}
执行“pgdt= new PostGraduate();" 后,共创建了4个对象。按照创建顺序,依次是 Object, Person.,Student, PostGraduate 对象。在执行Person()时会调用它的直接父类Object的无参构造方法,该方法内容为空。
执行“pgdt = new PostGraduate(“刘致同”,”北京大学”,”王老师”);”后,也创建了4个对象,只是此次调用的构造方法不同,依次是Object()、public Person(String name)、public Student(String name, String school)、 public PostGraduate(String name, String school, String guide)。
4. Object 类
Object类是所有类的父类。在Java中,所有的Java类都直接或间接地继承了java.lang.Object 类。Object 类是所有Java类的祖先。在定义一个类时,没有使用extends关键字,也就是没有显式地继承某个类,那么这个类直接继承Object类。所有对象都继承这个类的方法。
示例:请编写代码,实现没有显式继承某类的类,Object 类是其直接父类。
public class Person{
//......省略类的内部代码}
}
//两种写法是等价的
public class Person extends Object{
//...省略类的内部代码
}
Object类中的equals()方法用来比较两个对象是否是同一对象, 若是,返回true,而字符串对象的equals()方法用来比较两个字符串的值是否相等,java.lang.String类重写了Object类中的equals()方法。
方法重写
在示例中,PersonelDept对象pd的输出内容是继承自父类Department的printDetail()方法的内容,所以不能显示PersonelDept的count信息,这显然不符合实际需求。
下面介绍如何使用方法重写来输出各部门的完整信息。
如果从父类继承的方法不能满足子类的需求,可以在子类中对父类的同名方法进行重写(覆盖),以符合需求。
示例:在PersoneIDept中重写父类的printDetail( )方法。
//父类为Department
public class Department
//.....省略父类的属性、setter/getter()方法等代码,完整代码请参考第一个示例
public void printDetail() {
System. out pintnln("部门:”+ this.name + "\n经理: "+this.managr
+"\n 部门职责: "+this.responsibiiity+"************")
}
}
//以上为父类Department部分代码,以下为“人事部”类部分代码
public class PersonelDept extends Department {
private int count;//本月计划招聘人数
//......省略父类的属性、setter/getter()方法等代码
public void printDetail(){
System. out.println("本月计划招聘人数:"+this.count+"\n");
//以上为父类与子类部分代码,以下为调用类部分代码
public static void main(String[] args){
PersonelDept pd=new PersonelDept("人事部”,"王经理","负责公司的人才招聘和培训。 ",10);
pd.printDetail();
}
从输出结果可以看出,pd.printDetail()调用的是相应子类的printDetail()方法,可以输出自身的count属性,符合需求,任务到这里已基本实现。
在子类中可以根据需求对从父类继承的方法进行重新编写,这称为方法的重写或方法的覆盖( Overriding)。
方法重写必须满足如下要求。
➢重写方法和被重写方法必须具有相同的方法名。
➢重写方法和被重写方法必须具有相同的参数列表。
➢重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类。
➢重写方法不能缩小被重写方法的访问权限。
请思考重载(Overloading)和重写(Overriding) 有什么区别和联系?
➢重载涉及同一个类中的同名方法,要求方法名相同,参数列表不同, 与返回值类型无关。
➢重写涉及的是子类和父类之间的同名方法,要求方法名相同、参数列表相同、返回值类型相同。
推荐阅读