目录
抽象类
概述
abstract是抽象的意思,可以修饰类、成员方法➡抽象类、抽象方法
比如我们定义了一个父类Animal,里面有个run方法,但是每种动物跑的不一样,我们就可以把它定义成抽象方法,等子类自己去实现
注意事项:
- 抽象方法只有方法名,不能写方法体
- 一个类中如果定义了抽象方法,这个类必须声明成抽象类,不然会报错
使用场景:
- 抽象类可以理解为不完整的设计图,一般作为父类,让子类继承
- 当父类知道子类一定要完成某些方法,但是每个子类的行为又不同时,父类可以把该方法定义成抽象方法,具体交给子类实现,此时这个类就是抽象类。子类实现时必须要重写
案例
需求:某加油站推出了2种支付卡,一种是预存10000的金卡,后续加油享受8折优惠,另一种是预存5000的银卡,后续加油享受8.5折优惠。
实现2种卡片进入收银系统的逻辑,卡片需要包括主人姓名、余额、支付功能。
分析:
- 创建一张卡片父类:定义属性包括姓名、余额、支付功能(具体交给子类实现)
- 创建一张金卡:重写支付功能,按照原价8折计算
- 创建一张银卡:重写支付功能,按照原价8.5折计算
代码参考
public abstract class Card {//父类
private String userName;
private double money;
//定义一个支付功能
public abstract void pay(double money2);
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
public class goldCard extends Card{//子类金卡
@Override
public void pay(double money2) {
System.out.println("您当前消费:"+money2);
System.out.println("您的金卡当前余额:"+getMoney());
//优惠计算:8折
double rs = money2*0.8;
System.out.println(getUserName()+"您实际支付:"+rs);
//更新余额
setMoney(getMoney()-rs);
}
}
public class silverCard extends Card{//银卡
@Override
public void pay(double money2) {
System.out.println("您当前消费:"+money2);
System.out.println("您的银卡当前余额:"+getMoney());
//优惠计算:8.5折
double rs = money2*0.85;
System.out.println(getUserName()+"您实际支付:"+rs);
//更新余额
setMoney(getMoney()-rs);
}
}
public class Test {
public static void main(String[] args) {
goldCard c = new goldCard();
c.setMoney(10000);//金卡
c.setUserName("gst");
c.pay(300);//消费300
System.out.println("余额:"+ c.getMoney());
System.out.println("--------------------");
silverCard s = new silverCard();
s.setMoney(5000);
s.setUserName("zcy");
s.pay(100);
System.out.println("余额:"+s.getMoney());
}
}
特征
- 类有的成员,抽象类都具备,比如成员变量、方法构造器
- 抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
- 一个类继承了抽象类,必须重写完抽象类中的全部抽象方法,否则这个类也必须定义成抽象类
- 不能用abstract修饰变量、代码块、构造器
- 最重要的特征:抽象类不能创建对象
注意:final和abstract是互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写
抽象类应用知识:模板方法模式
模板方法模式是一种设计模式
使用场景:当系统中出现同一个功能多处在开发,而该功能的大部分代码是一样的,只有其中部分可能不同的时候,模板方法提高了代码的复用性,使得开发效率更高
实现步骤:
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码
- 模板方法中不能确定的功能定义成抽象方法让子类去实现
案例
需求:
有两类学生,中学生和小学生,他们都要写一篇作文叫《我的爸爸》,要求他们的作文标题第一段和最后一段内容必须是一样的,正文部分自己发挥。
public abstract class Student {//父类
//声明模板方法
public final void write(){
System.out.println("《我的爸爸》");
System.out.println("描述你的爸爸...");
//正文部分(每个子类不一样)
//所以定义成抽象方法交给子类来完成
System.out.println(writeMain());
System.out.println("我爱我的爸爸");
}
//抽象方法
public abstract String writeMain();
}
public class primaryS extends Student{//子类
@Override
public String writeMain() {
return "我的爸爸鼻子高高的";
}//小学生
}
public class middleS extends Student{//子类
@Override
public String writeMain() {
return "我的爸爸太牛啦!";
}//中学生
public class Test {
public static void main(String[] args) {
middleS m = new middleS();
m.write();//调用父类的方法
System.out.println("---------------");
primaryS p = new primaryS();
p.write();
}
}
注意:模板方法最好使用final修饰,显得更专业,因为要用这个模板,不能把模板重写呀,不然这个模板不是白弄了嘛,加了final就可以防止子类重写这个模板方法,更安全。
接口
概述
- 接口用interface关键字来定义
- 在JDK8之前,接口中只能有常量和抽象方法
- 接口是一种规范,约束别人必须要干某一件事情,而规范默认都是公开的,所以代码层面,常量和抽象方法中的public、static、final、abstract可以省略不写
基本使用:被实现
- 用法:接口是用来被类实现(implements)的,实现接口的类叫实现类,实现类可以理解为所谓的子类
- 案例:把运动员定义成接口,将乒乓运动员定义成实现类去实现运动员那个接口,接口是一种规范,运动员这个接口约束着所有乒乓运动员、篮球运动员等都要跑步训练和比赛
- 接口可以单实现也可以多实现
注意事项:一个类实现接口必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
接口与接口的关系:多继承
基本小结
- 类与类:单继承
- 类与接口:多实现
- 接口与接口:多继承,一个接口可以同时继承多个接口
一个类实现三个接口,如果有十几个接口会很繁琐,可以将几个接口继承到一个接口上,实现接口的多继承⬇
接口多继承的作用:规范合并,整合多个接口为一个接口,便于子类实现
JDK 8开始:接口新增方法
原因:一个系统新开发了升级版本,新版本需要对接口丰富,加入很多个抽象方法,此时改了接口,所有实现类就要全部重写这些新的抽象方法,牵一发而动全身,很繁琐
所以:Java升级技术,允许接口中直接定义带有方法体的方法
第一种:默认方法
类似之前写的实例方法,但是必须用default来修饰(不写的话默认会用public),需要用接口的实现类的对象来调用
第二种:静态方法
必须用static修饰(不写默认用public),接口的静态方法必须用本身的接口名来调用
第三种:私有方法
就是私有的实例方法,必须使用private修饰,从JDK 9才开始有的,只能在本类中被其他默认方法或者私有方法访问
我这边是jdk8所以用不了这个,报错
如果要切换jdk版本这样操作⬇
注:这三种方法在开发时其实很少使用,但是可以帮助我们理解语法
注意事项
- 接口不能创建对象
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突
接口的静态方法不能用实现类调用,只能接口自己名字调用,所以上述代码中尽管A和B有相同的静态方法也不冲突
- 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的
Cat这个类继承了Animal父类又实现了Food接口,我们看输出结果默认用了Animal父类的
- 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可
CC只能自己重写一个go方法,如果一定要用AA或者BB的呢?不好意思,永远用不了!
- 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承
没问题,而且默认继承一个run就行了,合并起来了
但是这里两个run返回值不一样了,就算冲突报错了