第十三章 类的继承
提纲
- 13.1 概念
- 13.2 Java中的继承
- 13.2.1 父类和子类
- 13.2.2 定义继承的语法
- 13.2.3 举例
- 13.2.4 继承的好处
- 13.4.5 继承需要注意的地方
- 13.3 super关键字
- 13.3.1 super关键字的作用
- 13.3.2 如何使用构造函数给父类传参
- 13.3.3 总结
- 13.4 方法的重写
- 13.4.1 什么是方法重写
- 13.4.2 重写的特点
- 13.4.3 重写举例
- 13.5 继承与访问权限的关系
- 13.3 作业
13.1 概念
-
继承的含义:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
-
生活中的继承:
动物 食草动物 肉食动物 兔子 牛 羊.... 老虎 狮子 猎豹
解释:兔子、牛、羊属于食草动物类,老虎、狮子、猎豹于食肉动物类。食草动物和食肉动物又是属于动物类。父类更通用,子类更具体。虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
3. 为什么使用继承
- 从已有的类派生出新的类,称为继承。
- 在不同的类中也可能会有共同的特征和动作,可以把这些共同的特征和动作放在一个类中,让其它类共享。 因此可以定义一个通用类,然后将其扩展为其它多个特定类,这些特定类继承通用类中的特征和动作。
- 继承是 Java 中实现软件重用的重要手段,避免重复,易于维护,易于理解。
13.2 Java中如何继承
-
13.2.1 父类和子类:
- 如果类 B 从类 A 派生,或者说类 B 扩展自类 A,或者说类 B 继承类 A
- 则称类 A 为"父类",又称超类、基类
- 称类 B 为"子类",又叫派生类、次类、扩展类
-
13.2.2 定义继承的语法:修饰符 class 子类名 extends 父类名{}
-
13.2.3 举例:
//定义一个动物类Animal public class Animal { } //定义一个老虎类Tiger,因为老虎是属于动物的,所以老虎是子类,动物是父类
public class Tiger extends Animal{
} //还可以定义多个子类
public class Rabbit extends Animal{
} ...
-
13.2.4 继承的好处:现有大儿子和小儿子两个类,类中有属性姓名、性别和年龄,类中有方法吃,内容输出为:XXX正在吃,睡,内容输出为:XXX正在睡,自我介绍,内容为大家好!我是XXX,性别是XXX,今年XX岁了。
//非继承状态下有两个类,小儿子和大儿子 //大儿子类 public class BigSon { String name; String sex; int age; public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是:"+ name + ",性别是:" + sex + ",今年"+age+"岁"); } } //小儿子类 public class SmallSon { String name; String sex; int age; public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是:"+ name + ",性别是:" + sex + ",今年"+age+"岁"); } }
结论:功能虽然已经实现,但是代码量大且臃肿,两个类中是完全一模一样的代码,如果出现三儿子、四儿子…那代码量可想而知。
//经继承优化之后的代码 //首先,定义一个父亲类,因为大儿子和小儿子都是父亲的子类 public class Father { String name; String sex; int age; public void eat(){ System.out.println(name+"正在吃"); } public void sleep(){ System.out.println(name+"正在睡"); } public void introduction() { System.out.println("大家好!我是:"+ name + "性别是:" + sex + ",今年"+age+"岁"); } } //大儿子继承父亲 public class BigSon extends Father{ } //小儿子继承父亲 public class SmallSon extends Father{ } //创建测试类测试 public class Test { public static void main(String[] args) { SmallSon ss = new SmallSon(); ss.name = "张三"; ss.age = 18; ss.sex = "男"; ss.eat(); ss.sleep(); ss.introduction(); BigSon bs = new BigSon(); bs.name = "李四"; bs.age = 14; bs.sex = "女"; bs.eat(); bs.sleep(); bs.introduction(); } } //执行结果 张三正在吃 张三正在睡 大家好!我是:张三性别是:男,今年18岁 李四正在吃 李四正在睡 大家好!我是:李四性别是:女,今年14岁
结论:使用继承之后,可以发现这个Father类就可以作为一个父类,然后小儿子类和大儿子类继承这个类之后,就具有父类当中的属性和方法,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码)。
-
13.4.5 继承需要注意的地方
-
在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
-
子类不是父类的子集,子类一般比父类包含更多的数据域和方法。
-
父类中的 private 数据域在子类中是不可见的,因此在子类中不能直接使用它们。
//继续4中的例子 //在Father中加入私有属性小三和私有方法按摩 private otherWomen = "小莉"; private void massage(){ System.out.println("去按摩"); } //在子类中,无法调用并使用该属性,除非父类中提供有getter和setter方法才可以对其进行调用和修改 //也无法调用该massage方法
-
如果要继承,父类与子类之间就必须存在继承关系。
-
13.3 super关键字
-
13.3.1 super关键字的作用
- 调用父类构造方法语法:super();或者super(参数列表);如果父类没有空的构造方法,子类一定要重写父类的构造方法。
- 调用父类的成员变量的语法:super.变量名
- 调用父类的方法语法:super.方法名(参数列表);
-
13.3.2 如何使用构造函数给父类传参。
//这里只写添加的代码。 //首先,父类中编写传递参数的构造函数 public Father(String name, String sex, int age) { this.name = name; this.sex = sex; this.age = age; } //大儿子类中 String name;//大儿子自己的name属性 public BigSon(String name, String sex, int age,String name) { super(name, sex, age);//父亲名字 this.name = name;//大儿子名字 } public void display(){ System.out.println("大儿子的名字是:"+this.name+",父亲的名字是:"+super.name); } //在Test类的main方法中 BigSon bs = new BigSon("Father", "男", 55, "BigSon"); bs.display(); //执行结果 大儿子的名字是:BigSon,父亲的名字是:Father
-
13.3.3 总结
- super 语句必须是子类构造方法的第一条语句。
- 不能在子类中使用父类构造方法名来调用父类构造方法,必须用super()来调用。
- 父类的构造方法不被子类继承。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super(),作为子类构造方法的第一条语句,如果父类中没有空的构造方法,则子类必须使用super()给父类的构造方法传参,否则会编译错误。super();
- 静态方法中不能使用 super 关键字。
- 使用this和super来区分是本类的还是父类的属性与方法。
13.4 方法的重写
-
13.4.1 什么是方法重写:
- 如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。
- 但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。
- 方法重写又称方法覆盖。
-
13.4.2 重写的特点:
- 若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
- 重写的方法中子类函数的访问修饰权限不能少于父类的。
-
13.4.3 重写举例:
//汽车类 public class Car { //开车的方法 public void drive(){ System.out.println("开汽车"); } } //本田车类属于汽车类 public class Honda extends Car{ @Override//Override覆盖、重写的意思 public void drive() { //super.drive();//调用父类的开车方法 System.out.println("开本田牌汽车"); } } //宝马车类属于汽车类 public class BMW extends Car{ @Override public void drive() { System.out.println("开宝马牌汽车"); } } //保时捷车类属于汽车类 public class Porsche extends Car{ @Override public void drive() { System.out.println("开保时捷牌汽车"); } } //不管什么车子都必须由人来开,所以定义一个人类,并且定义一个开车的方法 public class Person { //定义开车方法并不知道开的是什么车,所以定义他们所有车的父类 public void drive(Car car){ car.drive(); } } //定义一个测试类 public class Test { public static void main(String[] args) { Person person = new Person(); //直接传入子类对象,得到对应的结果 person.drive(new BMW()); person.drive(new Honda()); } }
13.5 继承与访问权限的关系
//在上述例子中的Father加入两个属性
public String face = "超级帅";//相貌
protected double money = 500000;//钱
//加入受保护的方法
protected void run() {
System.out.println("在跑步");
}
//加入默认的方法
void makeMoney(){
System.out.println("老爸赚钱很厉害");
}
//在com.zxjy.test包中定义一个三儿子
public class ThreeSon extends Father{
public void display(){
//在这里,调用的属性只有face和money,调用的方法为eat()、sleep()、introduction()、run(),无法调用私有方法和默认方法
System.out.println("长的有老爸那么:"+super.face+",拿着老爸的钱:"+super.money);
}
}
//Test类测试
ThreeSon ts = new ThreeSon();
ts.display();
ts.run();
//执行结果
长的有老爸那么:超级帅,拿着老爸的钱:500000.0
在跑步
三、作业
- 请用继承的思想,改写下面的类:
-
现在有笛子类与钢琴类如下:问:请用继承的思想,更改下面的类。
/** * 乐器类 */ Public abstract class Instrument(){ } /** * 钢琴类 */ public class Piano { public void play() { System.out.println("钢琴是弹的"); } } /** * 笛子类 */ public class Flute { public void play() { System.out.println("笛子是吹的"); } }
-
现在有小学生类与大学生类:问:请用继承的思想,改写下面的类。要求性别属性在构造函数中初始化。
/**
public class student{
public String sex;
public student(String sex){
this.sex=sex;
}
}
* 小学生 */ public class SmallStudent { public String sex; public void study() { System.out.println("学习小学课本"); } public String getSex() { return this.sex; } } /** * 大学生类 */ public class BigStudent { public String sex; public void study() { System.out.println("学习大学课本"); } public String getSex() { return this.sex; } }
-
现在有白猪类和黑猪类如下:问:请用继承的思想,改写下面的类。要求性别属性在构造函数中初始化。
/** * 黑猪类 */ public class BlackPig { private String color; public BlackPig() { this.color = "黑"; } public String getColor() { return this.color; } public int attack() { return 50; } } /** * 白猪类 */ public class WritePig { private String color; public WritePig() { this.color = "白"; } public String getColor() { return this.color; } public int attack() { return 100; } }
-
- 看代码,说出结果和原因:
-
第一题:
import java.util.Arrays; public class A { public int i; public float j; public String s; public int[] arr; public char c; public static void main(String[] args) { A a = new A(); System.out.println(a.i); System.out.println(a.j); System.out.println(a.s); System.out.println(a.c); //Arrays.toString方法用来将数组内容打印出来 System.out.println(Arrays.toString(a.arr)); } }
-
第二题:
public class A { public String i; public A() { System.out.println(this.i); } public static void main(String[] args) { A a = new A(); } }
-
第三题:
public class A { public B b = new B(); public A() { System.out.println("A"); } public static void main(String[] args) { A a = new A(); } } class B { public B() { System.out.println("B"); } }
-
第四题:
class C { public C() { System.out.println("C"); } } class A extends C{ public A() { System.out.println("A"); } public static void main(String[] args) { A a = new A(); } }
-
第五题:
class C { public C() { System.out.println("C"); } } class B extends C{ public B() { System.out.println("B"); } } class A extends B{ public A() { System.out.println("A"); } public static void main(String[] args) { A a = new A(); } }
-
第六题:
class C { public C() { System.out.println("C"); } } class B { public B() { System.out.println("B"); } } class A extends C{ public B b = new B(); int num=10; public A() { num=100; System.out.println("A"); } public static void main(String[] args) { A a = new A(); } }
-
- 继承作业练习:
- 练习一:
- 设计一个学生类Student,其属性有name(姓名)、age(年龄)和degree(学位)。Show()方法显示信息和构造函数。由Student类派生出本科类与研究生类。
- 本科生类Undergraduate和研究生类Graduate,本科生类Undergraduate增加属性specialty(专业),添加方法UndergraduateShow()显示信息。
- 研究生类Graduate增加属性direction(研究方向)。添加GraduateShow()方法,用于输出对象的信息。
- 本科生类Undergraduate和研究生类Graduate定义构造函数初始化属性(要求使用super调用父类的构造函数)。
- 练习二:
- 创建一个武器类Weapen,具有攻击力Power(public ),速度speed(public )字段,并实现构造方法。
- 创建一个Tank类,从Weapen类继承,具有私有的方向字段dir(上下左右),并用属性封装。定义攻击方法TankAttack(),打印"我是坦克,向**方向运动,速度***,攻击力***"。
- 创建一个子弹类Bullen,从Weapen类继承,具有私有的type字段(表示子弹类型,如:机枪子弹,步枪子弹),用属性封装。定义攻击方法BullenAttack(),打印"我是子弹***,速度***,攻击力***"。
- 为Tank类和Bullen类定义构造函数初始化属性(要求使用super调用父类的构造函数)。
- 创建一只Tank对象,调用其方法;创建一只Bullen,调用其方法。
- 练习三:使用继承机制实现动物世界:
- 创建控制台应用程序
- 创建所有动物的基类Animal类,定义姓名(name),体重(weight)属性,并创建构造函数初始化属性,定义吃(Eat)的方法,输出“真好吃”。
- 创建狗类(Dog),青蛙类(Frog)继承至动物类。
- 为Dog添加咬人的方法,输出“惹我罗,咬死你!”;为Frog添加游泳的方法,输出“洗个澡,真凉快!”。
- 为狗类和青蛙类定义构造函数初始化属性(要求使用super调用父类的构造函数)。
- 创建一只狗对象,调用其方法;创建一只青蛙,调用其方法。
- 练习四:
-
创建一个动物类:Animal:字段:name,sex~~访问修饰符均为public。Amimal:构造函数:无参,带两个参的分别为name和sex赋值
-
创建一个鸡类Ji继承于Animal:Ji:私有字段:chiBang,用属性封装。Ji:构造函数:带三个参的分别为name和sex和chiBang赋值,name和sex在父类赋值。Ji :JiToString()方法,用来作自我介绍,返回:我是XXX,我是XXX,我有XXX。
-
创建一个狗类Dog继承于Animal:Dog:私有字段:weiBa,用属性封装。Dog:构造函数:带三个参数分别为name,sex,weiBa赋值,name和sex在父类赋值。Dog: DogToString()方法,用来作自我介绍,返回:我是XXX,我是XXX,我有XXX。
-
在测试类Main方法内:定义二个鸡对象:
花花,母,一对金黄色的翅膀 草草,公,一对银黄色的翅膀 定义二个狗对象: 小白,公,一条金白色的尾巴 小黄,母,一条银白色的尾巴 生成4个对象,分别调用相应的方法。
-
- 练习一:
,调用其方法。
- 练习四:
1. 创建一个动物类:Animal:字段:name,sex~~访问修饰符均为public。Amimal:构造函数:无参,带两个参的分别为name和sex赋值
2. 创建一个鸡类Ji继承于Animal:Ji:私有字段:chiBang,用属性封装。Ji:构造函数:带三个参的分别为name和sex和chiBang赋值,name和sex在父类赋值。Ji :JiToString()方法,用来作自我介绍,返回:我是XXX,我是XXX,我有XXX。
3. 创建一个狗类Dog继承于Animal:Dog:私有字段:weiBa,用属性封装。Dog:构造函数:带三个参数分别为name,sex,weiBa赋值,name和sex在父类赋值。Dog: DogToString()方法,用来作自我介绍,返回:我是XXX,我是XXX,我有XXX。
4. 在测试类Main方法内:定义二个鸡对象:
花花,母,一对金黄色的翅膀
草草,公,一对银黄色的翅膀
定义二个狗对象:
小白,公,一条金白色的尾巴
小黄,母,一条银白色的尾巴
生成4个对象,分别调用相应的方法。