Day11
继承
继承概述
- 继承是面向对象三大特征(封装、继承、多态)之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。
- 格式:public class 子类名 extends 父类名{}
- 范例:public class Zi extends Fu{}
- Fu:是父类,也被称为基类、超类
- Zi:是子类,也被称为派生类
继承的特点
- java只支持单继承,不支持多继承,但支持多层继承。
继承中子类的特点:
- 子类可以有父类的内容
- 子类还可以有自己的特有的内容
继承的好处和弊端
继承的好处
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
继承弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时,子类实现也不得不跟着变化,削弱了子类的独立性
什么时候用继承
- 继承体现的关系:is a
- 假设法:有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承来体现,否则就不能滥用继承
继承中成员变量的访问特点
- 在子类中访问一个变量 的优先级
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果没有就报错(不考虑父亲的父亲…)
- **注意:**如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的,可以通过super关键字,进行区分。
继承中成员方法访问的特点
- 通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
super
super关键字的用法和this关键字的用法相似
- this:代表本类对象的引用(this关键字指向调用该方法的对象,一般我们是在当前类中使用this关键字,所以我们常说this代表本类对象的引用)
- super:代表父类存储空间的标识(可以理解为父类对象引用)
方法重写
-
概述:在继承体系中,子类出现了和父类中一模一样的方法声明
-
应用场景
-
当子类需要父类的功能,而功能主体子类有自己特有内容,可以重写父类中的方法,这样,即沿袭了父类的功能,有定义了子类特有的内容
-
示例:
-
父类 public class OldPhone { public void call(String name){ System.out.println("给"+name+"打电话"); } public void speak(){ System.out.println("说英文"); } } 子类 public class NewPhone extends OldPhone { public void speak(){ super.speak(); System.out.println("说中文"); } } 测试类 public class Test { public static void main(String[] args) { NewPhone newPhone = new NewPhone(); newPhone.speak(); newPhone.call("澜"); } }
-
-
注意:
- 方法重写:在继承体系中,子类出现了和父类一模一样的方法声明(方法名、参数列表、返回值类型)
- 方法重载:在同一个类中,方法名相同,参数列表不同,与返回值无关。
方法重写注意事项
- 父类中私有方法不能被重写
- 父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写
- 注意:静态方法不能被重写,如果子类中,也存在一个方法声明一模一样的方法,可以理解为,子类将父类中同名方法,隐藏了起来,并非是方法重写!
- 子类重写父类方法时,访问权限必须大于等于父类
- 权限修饰符
继承中构造方法的访问特点
继承中所有的构造方法默认都会访问父类中无参的构造方法
为什么?
- 子类在初始化的时候,有可能会使用到父类中的数据,如果没有完成初始化,子类将无法使用父类的数据。 子类初始化之前,一定要先完成父类初始化。
怎么初始化?
- 构造方法的第一条语句默认都是:super()
**注意:**如果我们编写的类,没有手动指定父类,系统也会自动继承Object(Java继承体系中的最顶层父类)
示例:
父类Person.java
package gouzao;
public class Student extends Person{
private int score;
public Student(){
//子类初始化之前,一定要先完成父类数据的初始化
//子类初始化之前,一定要先访问到父类的构造方法,完成父类数据的初始化
//系统在每一个构造方法中,默认隐藏一句代码super();
System.out.println("我是子类的无参构造");
}
public Student(int score){
this.score=score;
System.out.println("我是子类的带参构造");
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
子类Student.java
package gouzao;
public class Student extends Person{
private int score;
public Student(){
//子类初始化之前,一定要先完成父类数据的初始化
//子类初始化之前,一定要先访问到父类的构造方法,完成父类数据的初始化
//系统在每一个构造方法中,默认隐藏一句代码super();
System.out.println("我是子类的无参构造");
}
public Student(int score){
this.score=score;
System.out.println("我是子类的带参构造");
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
测试类Test.java
package gouzao;
public class Test {
public static void main(String[] args) {
Student stu = new Student();
Student stu1 = new Student(100);
}
}
运行结果:
如果父类中没有空参构造方法,只有带参构造方法,会出错,解决办法:
- 子类通过super,手动调用父类的带参的构造方法
- 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参构造方法
- 注意:this()super()必须放在构造方法的第一行有效语句,并且二者不能共存
抽象类
概述
- 抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法;
- 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类;
作用
- 抽象类可以作为一些设计模式使用;
- 抽象类中可以写抽象方法。
定义抽象方法的作用
- 抽象方法可以强制子类必须重写父类抽象方法;
案例:猫和狗
需求:定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…)
分析:
- 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
- 父类Animal中,无法将eat方法具体实现描述清楚,所以定义为抽象方法
- 抽象方法需要存活在抽象类中,将Animal定义为抽象类
- 让Cat和Dog分别继承Animal,重写eat方法
- 测试类中创建Cat和Dog对象,调用方法测试
Animal类:
package chouxiang;
public abstract class Animal {
public void drink(){
System.out.println("喝水");
}
public abstract void eat();
}
Cat类:
package chouxiang;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
Dog类:
package chouxiang;
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
测试类Test:
package chouxiang;
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
d.drink();
Cat c = new Cat();
c.eat();
c.drink();
}
}
运行结果:
- 抽象方法的定义格式:
- public abstract 返回值类型 方法名(参数列表);
- 抽象类的定义格式:
- public abstract class 类名{}
抽象类的注意事项
- 抽象类不能创建对象(不能实例化)
- 抽象类中有构造方法
- 抽象类的子类
- 必须要重写父类中所有的抽象方法
- 可以将自己也变成一个抽象类
- 抽象类中的方法
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定是抽象类
模板设计模式
- 设计模式是一套反复被使用、多数人只晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
- 模板设计模式:把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法,让使用模板的类(继承抽象类的类)去重写抽象方法实现需求;
- 小结:模板设计模式的优势,模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可。
final关键字
final修饰的特点
- 修饰方法:表明该方法是最终方法,不能被重写
- 修饰变量:表明该变量是常量,不能再次被赋值
- 基本数据类型变量:其值不能被更改
- 引用数据类型变量:地址值不能被更改,但可以更改对象的属性值
- 修饰类:表明该类是最终类,不能被继承
final修饰成员变量,初始化时机
- 在创建的时候直接给值
- 在构造方法结束之前,完成赋值
代码块
代码块概述与分类
在java中,使用{}括起来的代码块被称为代码块
分类:
- 局部代码块
- 位置:方法中定义
- 作用:限定变量的生命周期,及早释放,提高内存利用率
- 构造代码块
- 位置:类中方法外定义
- 特点:每次构造方法执行时,都会执行该代码块中的代码,并且在构造方法执行前执行
- 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
- 静态代码块
- 位置:类中方法外定义
- 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
- 作用:在类加载的时候做一些数据初始化的操作
练习1
我们计划为一个电器销售公司制作一套系统,公司的主要业务是销售一些家用电器,例如:电冰箱、洗衣机等。
-
冰箱类:
- 属性:品牌、型号、颜色、售价、门款式、制冷方式
-
洗衣机类:
- 属性:品牌、型号、颜色、售价、电机类型、洗涤容量
请根据上述类的设计,抽取父类“电器类”,并代码实现父类“电器类”、子类“冰箱类”,“洗衣机类”;
父类Dianqi.java
package lian1;
public class Dianqi {
private String brand;
private String version;
private String color;
private double price;
public Dianqi() {
}
public Dianqi(String brand, String version, String color, double price) {
this.brand = brand;
this.version = version;
this.color = color;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
子类Refrigerator.java
package lian1;
public class Refrigerator extends Dianqi{
private String doorStyle;
private String wayRefrigeration;
public Refrigerator(){
}
public Refrigerator(String brand, String version, String color, double price, String doorStyle, String wayRefrigeration) {
super(brand, version, color, price);
this.doorStyle = doorStyle;
this.wayRefrigeration = wayRefrigeration;
}
public String getDoorStyle() {
return doorStyle;
}
public void setDoorStyle(String doorStyle) {
this.doorStyle = doorStyle;
}
public String getWayRefrigeration() {
return wayRefrigeration;
}
public void setWayRefrigeration(String wayRefrigeration) {
this.wayRefrigeration = wayRefrigeration;
}
}
子类Washer.java
package lian1;
public class Washer extends Dianqi {
private String powerStyle;
private int volume;
public Washer(String brand, String version, String color, double price, String powerStyle, int volume) {
super(brand, version, color, price);
this.powerStyle = powerStyle;
this.volume = volume;
}
public Washer(){
}
public String getPowerStyle() {
return powerStyle;
}
public void setPowerStyle(String powerStyle) {
this.powerStyle = powerStyle;
}
public int getVolume() {
return volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
}
测试类
package lian1;
public class DianqiTest {
public static void main(String[] args) {
Refrigerator rg = new Refrigerator("格力","暂无","灰色",1999.99,
"单门","不详");
Washer ws = new Washer("海尔","暂无","白色",999.99,"不详",50);
System.out.println(rg.getBrand()+" "+rg.getVersion()+" "+rg.getColor()+" "+rg.getPrice()
+" "+rg.getDoorStyle()+" "+rg.getWayRefrigeration());
System.out.println(ws.getBrand()+" "+ws.getVersion()+" "+ws.getColor()+" "+ws.getPrice()+" "
+ws.getPowerStyle()+" "+ws.getVolume());
}
}
运行结果:
练习2
我们计划为一所体育学校设计一套系统,需要记录以下人员的信息:
-
教练员:
- 属性:员工编号、姓名、性别、年龄
- 行为:吃饭(吃工作餐)
-
运动员:
- 属性:学员编号、姓名、性别、年龄、所在班级
- 行为:吃饭(吃营养餐)
请根据需求,设计、并编码实现:父类“人员类”,子类“教练员类”、子类“运动员类”,并包含共有的属性和行为的定义,由于运动员和教练员的吃饭的内容不同,所以需要设计为“抽象方法”。
父类Person.java
package lian3;
public abstract class Person {
private String name;
private String gent;
private int age;
public abstract void eat();
public Person(String name, String gent, int age) {
this.name = name;
this.gent = gent;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGent() {
return gent;
}
public void setGent(String gent) {
this.gent = gent;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类Trainer.java
package lian3;
public class Trainer extends Person{
private String yid;
public Trainer(String name, String gent, int age, String yid) {
super(name, gent, age);
this.yid = yid;
}
public String getYid() {
return yid;
}
public void setYid(String yid) {
this.yid = yid;
}
@Override
public void eat() {
System.out.println("教练"+getName()+"正在吃工作餐,他的员工编号为"+getYid()+",年龄为"+getAge());
}
}
子类Sportsman.java
package lian3;
public class Sportsman extends Person {
private String xid;
public Sportsman(String name, String gent, int age, String xid) {
super(name, gent, age);
this.xid = xid;
}
public String getXid() {
return xid;
}
public void setXid(String xid) {
this.xid = xid;
}
@Override
public void eat() {
System.out.println("运动员"+getName()+"正在吃营养餐,他的学员编号为"+getXid()+",年龄为"+getAge());
}
}
测试类
package lian3;
import jicheng.Tager;
public class PersonTest {
public static void main(String[] args) {
Trainer t = new Trainer("老王","男",36,"20201467");
t.eat();
Sportsman s = new Sportsman("小王","男",22,"20200123");
s.eat();
}
}
运行结果:
练习3
某公司的系统中需要记录两类员工:
-
员工类:
- 属性:工号、姓名、年龄
- 行为:发布通知
-
经理类:
- 属性:工号、姓名、年龄、年终奖额
- 行为:发布通知
任何员工发布“通知”都统一使用以下格式:
通知:
xxxxxxxxx
xxxxxxxxx
必胜环球科技有限公司
父类tongzhi.java
package lian4;
public abstract class Tongzhi {
private String id;
private String name;
private int age;
public void tongzhi(){
System.out.println("通知:");
body();
System.out.println("\t\t必胜环球科技有限公司");
}
public abstract void body();
public Tongzhi(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类Jingli.java
package lian4;
public class Jingli extends Tongzhi{
private int jine;
public Jingli(String id, String name, int age, int jine) {
super(id, name, age);
this.jine = jine;
}
public int getJine() {
return jine;
}
public void setJine(int jine) {
this.jine = jine;
}
@Override
public void body() {
System.out.println("编号为:"+getId()+"的"+getAge()+"岁的经理"+getName()+"拿着"+jine+"元奖金发布了通知...");
}
}
子类Yuangong.java
package lian4;
public class Yuangong extends Tongzhi {
public Yuangong(String id, String name, int age) {
super(id, name, age);
}
@Override
public void body() {
System.out.println("编号为:"+getId()+"的"+getAge()+"岁的员工"+getName()+"发布了通知...");
}
}
测试类Test.java
package lian4;
public class Test {
public static void main(String[] args) {
Jingli j = new Jingli("20201216","老王",32,10000);
j.body();
Yuangong y = new Yuangong("20201612","小王",25);
y.body();
}
}
运行结果: