抽象类
概述
是什么: 在Java中abstract
是抽象的意思,可以修饰类、方法。
//抽象类
public abstract class Animal{
//抽象方法
public abstract void run();
}
注意事项:
- 抽象方法只有方法签名,不能声明方法体。
- 一个类中如果定义了抽象方法那么这个类必须声明成抽象类,否则报错!
使用场景:
抽象类可以理解为不完整的设计图,一般作为父类,让子类来继承。
父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成,此时这个类就可以声明成抽象类。
特征
- 类有的成员(变量,方法,构造器)抽象类都具有。
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能有abstract去修饰变量,代码块,构造器。
- 抽象类得到了方法,失去了创建对象的能力。(最重要)
解释:假如抽象类可以创建对象,那么调用抽象类的抽象方法,缺少方法体并不能运行,因此不能创建对象(反证法)。哪怕抽象类中存在着非抽象方法,也是不可以创建对象的!
如果抽象类试图创建按对象,会报错:
class is abstract; cannot be instantiated(实例化)
final和abstract的关系:互斥关系
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承!
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写!
抽象类的应用知识:模板方法模式
使用场景:当系统中出现同一个功能多出在开发,而功能中大部分的代码时一样的,只有其中部分可能不同的时候。
实现步骤:
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只能定义通用并且能确定的代码。
- 模板方法中不能决定的功能定义成抽象方法让具体子类实现。
案例:
- 现在有两类学生,中学生与小学生,都要完成《我的爸爸》这篇作文。
- 要求每种类型的学生,标题第一段和最后一段内容必须一样,正文部分自由发挥。
- 请选择最优的面向对象方案进行设计。
public abstract class StudentDemo {
public void Write(){
//标题+开头
System.out.println("\t\t《我的爸爸》");
System.out.println("\t我来说说我和我爸爸的事");
//正文
System.out.println("\t"+WriteMain());
//结尾
System.out.println("\t我的爸爸非常好");
}
public abstract String WriteMain();
}
public class secondary extends StudentDemo {
@Override
public String WriteMain() {
return "中学生的正文~";
}
}
public class pupil extends StudentDemo{
@Override
public String WriteMain() {
return "小学生的正文~";
}
}
public class test {
public static void main(String[] args) {
secondary s = new secondary();
s.Write();
pupil p = new pupil();
p.Write();
}
}
最终结果:
模板方法最好用final修饰,这样子这个模板就不能重写了,使代码更安全、专业。
接口
概述
是什么: 接口是一种规范,约束对方只能做什么事。
**格式:**接口用关键字interface
来定义
public interface 接口名{
//常量
//抽象方法
}
注意事项:
- 由于接口体现规范思想,一定是公开的,所以说在代码层面,可以不写public static final和public abstract,写全或少写都没事,反正会被忽略~
- JDK 8之前接口中只能有抽象方法和常量!
接口的基本使用:被实现
- 接口是用来被类实现(implements)的,实现接口的类称为实现类,实现类可以理解为所谓的子类。
格式:修饰符 class 实现类 implements 接口1,接口2,接口3……{}
- 接口可以被类单实现,也可以被类多实现,但是要将抽象方法全部重写。
接口与接口的关系:多继承
多继承: 一个接口可以同时继承多个接口。
作用: 规范合并,整合多个接口为同一个接口,便于子类实现。
JDK 8开始接口新增方法
JDK 8后允许接口中直接定义带有方法体的方法。
种类:
- 默认方法
格式:default void run(){}
- 类似之前写的普通实例方法,但是用default修饰。
- 默认会public修饰,需要用接口的实现类的对象来调用。
- 静态方法
格式:static void inAddr(){}
- 默认会public修饰,必须用static修饰。
- 接口的静态方法必须用本身的接口名调用。
- 私有方法
格式:private void go(){}
- 私有的实例方法,必须用private修饰,从JDK1.9开始才有的。
- 只能在本类中其他默认方法或者私有方法访问。
新增的三种方法我们在开发的时候很少遇到,通常是在Java源码中涉及到,现阶段需要理解、识别语法,明白调用关系即可~
接口的注意事项
- 接口不能创建对象(更加彻底的抽象)。
- 一个类可以实现多个接口,多个接口中有同样的静态方法不冲突(接口静态方法调用方式决定的)。
- 一个类继承了父类,同时又实现了接口,父类和接口中又同名方法,默认使用父类的。
格式:class A extends B implements C{}
- 一个类实现了多个接口,多个接口中 存在同名方法,不冲突,这个类重写该方法就行,但此时就永远无法调用接口中的同名方法了。
- 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能继承。
多态
概述
是什么: 同类型的对象,执行同一个行为,会表现出不同的行为特征。
常见形式:
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态中成员访问特点:(以上面格式第一行为例)
- 方法调用:编译看左边,运行看右边。
- 变量调用,编译看左边,运行也看左边。(多态侧重行为多态)
使用前提:
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法重写
优势
- 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Animals a = new Dogs();//改dog就可以改掉后面的方法,不需要改后续代码。
a.run();
- 定义方法的时候,使用父类型作为参数,该方法可以接收这父类的所有子类对象,体现出多态的扩展性和便利。
使用多态会产生一个问题:不能使用子类独有功能,要解决这个问题,得使用类型转换。
多态下引用数据类型的类型转换
自动类型转换(由子到父)
子类对象赋值给父类类型的变量指向。
强制类型转换(由父到子)
格式: 子类 对象名 = (子类)父类类型变量
作用: 可以解决多态存在的问题。
注意事项:
- 强转在编译阶段(有继承或实现关系的在编译阶段可以强转)不会报错,但是在运行时可能出错!
- 如果转型后的类型和对象的真实类型不是同一种类型,那么会报错:
ClassCastException
Java建议强制类型转换前使用`instanceof`判断当前对象的真实类型,再进行强制转换。
每日一练:练习多态的使用
案例:
- 使用面向对象编程模拟:设计一个电脑对象,可以安装两个USB设备(不是真的实现这几个功能~)
- 鼠标:被安装时可以完成接入、调用点击功能、拔出。
- 键盘:被安装时可以完成接入、调用打字功能、拔出。
分析:
- 定义一个USB接口,实现接入拔出。
- 提供两个实现类。
- 创建电脑对象。
最终代码:
public class TestDemo {
public static void main(String[] args) {
computer c = new computer("外星人");
c.start();
//创建鼠标对象
USB u = new Mouse("罗技鼠标");
c.installUSB(u);
//创建键盘对象
USB u1 = new keyboard("双飞燕");
c.installUSB(u1);
}
}
public interface USB {
//接入拔出
void connect();
void unconnect();
}
public class computer {
private String name;
//提供安装USB设备的入口
public void installUSB(USB usb){
usb.connect();
if (usb instanceof keyboard){
keyboard k =(keyboard) usb;
k.keyDown();
}else {
Mouse m = (Mouse) usb;
m.dbDown();
}
usb.unconnect();
}
public void start(){
System.out.println(name + "开机了~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public computer(String name) {
this.name = name;
}
}
public class keyboard implements USB{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public keyboard(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name+"成功连接电脑~");
}
@Override
public void unconnect() {
System.out.println(name+"成功断开连接~");
}
public void keyDown(){
System.out.println(name+"敲击了:emm");
}
}
public class Mouse implements USB{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Mouse(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name+"成功连接电脑~");
}
@Override
public void unconnect() {
System.out.println(name+"成功断开连接~");
}
public void dbDown(){
System.out.println(name+"双击了");
}
}