抽象类和接口的区别
1 抽象类
1.1 为什么定义抽象类
把一组小猫抽象为Cat类,把一组小狗抽象为Dog类, 把人抽象为Person类, 把小狗/小猫/人可以进一步抽象为动物类
就像动物类/交通工具类/食物类等等这些类没法具体化, 这些类就可以定义为抽象类, 对事物进行更高层级的抽象可以形成更抽象的类
1.2 如何定义抽象类
使用abstract修饰的类就是抽象类
1.3 抽象类的特点
1)抽象类不能实例化对象, 抽象类引用需要赋值子类对象
2)含有抽象方法的类必须定义为抽象类, 但是抽象类中不一定含有抽象方法
3)子类继承了抽象类后, 需要重写抽象类的所有抽象方法,如果子类没有重写所有的抽象方法,子类也需要定义为抽象类
4)abstract与final 不可以共存
5)抽象类的引用可以赋值匿名内部类对象
1.4 为什么需要定义抽象方法
当一个类的某个操作不能具体实现时, 在不同子类中有不同的实现方式,这个操作就可以定义为抽象方法
1.5 如何定义抽象方法
使用abstract修饰方法, 只有方法的声明部分,没有方法体
abstract class Animal{
public abstract void move();
}
/**
* 定义一个宠物类Pet, 宠物会卖萌sellMeng()
* 不同各类的宠物,卖萌的方式不同, 宠物类有卖萌的行为不能具体实现
* ,这个行为可以定义为抽象方法
*
*/
public abstract class Pet {
public abstract void sellMeng();
//使用abstract修饰, 只有方法的声明,没有方法体
}
//定义小猫类
class Cat extends Pet {
@Override
public void sellMeng() {
System.out.println("小猫喵喵喵~~~~");
}
}
//定义小狗Dog类继承Pet类, 重写卖萌方法
class Dog extends Pet {
@Override
public void sellMeng() {
System.out.println("小狗旺旺~~~~~");
}
}
//定义主人类Master,
//主人有一个喂养宠物的方法,主人喂宠物时,宠物就卖萌
public class Master {
public void feed( Pet pet) {
pet.sellMeng();
}
}
/**
* 编写测试类, 主人喂狗, 主人喂猫
*/
public class Test {
public static void main(String[] args) {
Master lisi = new Master();
Dog dahuang = new Dog();
//feed(Pet)形参是父类的引用, 在调用方法时,传递子类对象
lisi.feed(dahuang);
lisi.feed( new Cat() );
//feed()方法的形参是抽象类的引用, 还可以传递匿名内部类对象
lisi.feed( new Pet() {
//在匿名内部类中重写抽象类的抽象方法
@Override
public void sellMeng() {
System.out.println("哼哼哼~~~~~");
}
});
}
}
2 接口
2.1 接口是什么
电脑上的接口有哪些?
USB接口 , 网线接口, 耳机接口, VGA接口, HDMI高清接口, 电源接口, Thunder接口
电脑通过这些接口连接更多的外设,扩展了电脑的功能
Java中的接口就是功能的封装
Java中的类通过接口可以扩展类的功能
2.1 如何定义接口
语法:
[修饰符] interface 接口名 {
功能,使用方法描述, 接口中的 方法默认使用public abstract修饰
}
2.3 如何使用接口
class 类名 implements 接口名 {
类需要重写接口中的抽象方法
}
2.4 接口的内容
/**
* 接口的内容
*
*/
public interface MyInterface {
void m1(); //默认使用public abstract修饰, 需要在实现类中重写
int xx = 666; //字段默认使用public static final修饰, 直接通过接口名访问
public static void sm() {
System.out.println("接口 中可以定义静态方法, 直接使用接口名调用");
}
public default void m2() {
System.out.println("在接口中使用default修饰方法, 表示该方法可以有默认的方法体"
+ "该方法在实现类中可以重写,也可以不重写");
}
}
2.5 接口的特点
1)类实现了接口,需要重写接口中抽象方法,如果没有重写接口所有的抽象方法,那么这个类需要定义为抽象类
2)接口是一种引用数据类型,可以定义变量,但是接口不能实例化对象, 接口的引用可以赋值实现类对象, 通过接口引用调用抽象方法,实际上就是执行实现类对象的方法,称为接口多态 . 接口就是为了实现多态的
3)接口的内容: 接口中方法默认使用 public abstract 修饰, 接口中的字段默认使用public static final 修饰, 接口中还可以定义 public static 方法, 还可以定义 public default 方法
4)一个类在继承父类的同时,可以实现多个接口, 接口支持多实现, 需要重写所有接口的所有抽象方法
5)接口也可以继承, 并且支持多继承
6) 接口的引用可以赋值匿名内部类对象
/**
* 定义一个飞行接口Flyable
*/
public interface Flyable {
//封装飞行功能
void fly(); //接口中的方法默认使用public abstract修饰
}
/**
* 定义Bird鸟类,
* 实现了Flyable接口, 需要重写接口的抽象方法
*
*/
public class Bird implements Flyable {
//Bird类重写了抽象方法,具有了接口中指定的功能
@Override
public void fly() {
System.out.println("小鸟使用小翅膀可以在天上飞");
}
}
public class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机也能飞");
}
}
/**
* 测试接口多态
*/
public class Test01 {
public static void main(String[] args) {
//1) 接口是一种引用数据类型, 接口可以定义变量
Flyable f;
//2)接口不能实例化对象
// f = new Flyable();
//3)给接口引用赋值实现类对象
f = new Bird();
//4) 通过接口引用调用抽象方法,实际上执行的是实现类对象的方法, 接口多态
f.fly();
//接口就是为了实现多态
f = new Plane();
f.fly();
//5)接口引用可以赋值匿名内部类对象
f = new Flyable() {
//在匿名内部类中重写接口的抽象方法
@Override
public void fly() {
System.out.println("蝴蝶也能飞");
}
};
f.fly();
}
}
接口支持多继承
//定义吃饭的接口
public interface Eatable {
void eat();
}
//定义睡觉的接口
public interface Sleepable {
void sleep();
}
//接口支持多继承
public interface Supermanble extends Flyable, Eatable, Sleepable {
/*
* 现在Supermanble接口从Flyable接口中继承了fly()方法,
* 从Eatable接口中继承了eat()方法
* 从Sleepable接口中继承了sleep()方法
* 在子接口中,可以添加特有的方法
*/
void fight();
}
/**
* 一个类,可以实现多个接口, 接口名之间使用逗号分隔
* 类实现了 多个接口, 需要重写所有接口的所有抽象方法
*
*/
public class Person implements Eatable, Sleepable, Flyable {
@Override
public void fly() {
}
@Override
public void sleep() {
}
@Override
public void eat() {
}
}
3 接口与抽象类的异同点
相同点:
1)都可以定义抽象方法
2)都不能实例化对象
3)抽象方法都需要被重写(覆盖)
不同点:
1)意义不同
接口是功能的封装
抽象类是事物更高层级的抽象
2)定义方式不同
接口使用interface
抽象类使用 abstract class
3)内容不同
接口中内容有四部分(JDK8): 默认的public abstract方法, 默认的public static final常量, 还可以定义public static 方法, public default方法
抽象类中除了抽象方法外, 普通类有的所有成员都可以在抽象类中定义, 如实例方法/实例变量/ 静态方法/静态变量/构造方法
4)使用方式不同
接口需要被类implements实现
抽象类需要被类extends继承
5)接口支持多继承, 类只支持单继承
6)应用场景不同
如果仅仅是为了扩展类的功能, 优先选择接口
如果还要体现类与类之间的关系,选择抽象类; 如果除了功能外,还需要保存各种数据,只能选择抽象类; 抽象类的抽象方法需要被子类重写,约束所有的子类都具有某个功能.
4 接口的应用
//定义Usb接口, 有连接设备, 断开连接两个操作
public interface Usb {
void connect(); //连接
void disconnect(); //断开连接
}
// 定义键盘Keyboard类, 实现了Usb接口
class Keyboard implements Usb {
@Override
public void connect() {
System.out.println("键盘已连接, 可以打字了");
}
@Override
public void disconnect() {
System.out.println("键盘已拔出");
}
}
//定义鼠标Mouse类,实现了Usb接口
class Mouse implements Usb {
@Override
public void connect() {
System.out.println("鼠标已连接,可以使用了");
}
@Override
public void disconnect() {
System.out.println("鼠标已拔出");
}
}
/**
* * 4) 定义计算机类Computer类, 计算机有一个Usb插槽
* 可以把Usb设备插入到Usb插槽中
* 可以把Usb插槽上的设备拔下来
*/
public class Computer {
//Usb插槽可以连接各种Usb设备
Usb usbSlot; //模拟USB插槽
//模拟把USB设备插入到USB插槽中
public void insert( Usb usb) {
usbSlot = usb ; //模拟把USB设备插入到USB插槽
usb.connect();
}
//模拟把USB设备从插槽中拔出
public void checkout() {
if ( usbSlot == null ) {
return ; //表示USB插槽上没有设备
}
usbSlot.disconnect(); //usb插槽上的设备断开连接
usbSlot = null; //usb插槽上没有设备了
}
}
/**
* 编写测试类, 测试往电脑上插入键盘, 拔出键盘后再插入鼠标
*
*/
public class Test {
public static void main(String[] args) {
Computer lenovo = new Computer();
Mouse mouse = new Mouse();
lenovo.insert(mouse);
lenovo.checkout();
lenovo.insert( new Keyboard() );
lenovo.checkout();
lenovo.insert(new Usb() {
@Override
public void disconnect() {
System.out.println("USB打印机断开连接");
}
@Override
public void connect() {
System.out.println("USB打印机连接到电脑");
}
});
lenovo.checkout();
lenovo.checkout();
}
}
鼠标已连接,可以使用了
鼠标已拔出
键盘已连接, 可以打字了
键盘已拔出
USB打印机连接到电脑
USB打印机断开连接
Process finished with exit code 0
5 提倡面向接口的编程
提倡面向接口的编程
1)接口使用比较灵活, 一个类在继承父类的同时, 可以实现若干接口 计算机实现了很多的接口
2)接口比较容易扩展, 接口的引用可以赋值各种实现类对象, 经常把接口的引用作为方法的形参, 在调用方法时,可以传递各种实现类对象 可以在计算机USB插槽上插入/拔出各种USB设备
3)接口可以使项目分层
就像有厂商负责生产计算机,有的厂商负责生产键盘, 有的厂商负责生产鼠标一样