第十六章 抽象类和接口以及抽象类和接口的区别

1 抽象类

1.1 抽象类的特点

抽象类:
1、什么是抽象类
类和类之间具有共同特征,将这些共同特征提取出来,形成的就是抽象类。
类本身是不存在的,所以抽象类无法实例化。

2、抽象类属于什么类型?
抽象类也属于引用数据类型。

3、抽象类怎么定义?
语法:
[修饰符列表] abstract class 类名{
类体;
}

4、抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。

5、final和abstract不能联合使用,这两个关键字是对立的。

6、抽象类的子类可以是抽象类

7、抽象类虽然无法实例化,但抽象类有构造方法,这个构造方法是供子类使用的。

8、抽象类关联到一个概念:抽象方法,什么是抽象方法呢?
抽象方法表示没有实现的方法,没有方法体的方法。
public abstract void doSome();
抽象方法特点是:
特点1:没有方法体,以分号结尾
特点2:前面修饰符列表中有abstract关键字

9、抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中


public class AbstractTest01{
public static void main(String[] args){
// 错误:Account是抽象的,无法实例化。
new Account();
}
}

// 银行账户类
// 错误:非法的修饰符组合:abstract和final
/*
final abstract class Account{

}
*/

abstract class Account{
/*
public Account(){

}
public Account(String s){

}
*/

// 非抽象方法
public void odOther(){

}

// 抽象方法
public abstract void withdraw();
}

// 子类继承抽象类,子类可以实例化对象
/*
class CreditAccount extends Account{

}
*/

// 抽象类的子类可以是抽象类吗?可以
/*
abstract class CreditAccount extends Account{

}
*/

 

1.2 非抽象类继承抽象类必须将抽象方法实现

抽象类
1、抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中

2、重要结论:
一个非抽象的类继承抽象类,必须将抽象类中的抽象方法实现了。
这是java语法上规定的,必须的,不然编译器就报错了。

这里的覆盖或者重写,也可以叫做“实现”


public class AbstractTest02{
public static void main(String[] args){
// 能不能使用多态?
// 父类型引用指向子类型对象.
// 这就是面向抽象编程
// a的类型是Animal,Animal是抽象的
// 面向抽象编程,不要面向具体编程,降低程序的耦合度,提高程序的扩展力。
// 这种编程思想符合OCP原则。
Animal a = new Bird();  
/*
分析:
编译的时候这个move()方法是谁的?
运行的时候这个move()方法是谁的?
*/
a.move();

Animal a2 = new Cat();
a2.move();

}
}

// 动物类
abstract class Animal{
// 抽象方法
public abstract void move();
}

// 子类(非抽象的)
// 错误: Bird不是抽象的, 并且未覆盖Animal中的抽象方法move()
class Bird extends Animal{
// 需要将从父类中继承过来的抽象方法进行覆盖/重写,或者也可以叫做“实现”。
// 把抽象的方法实现了
// public void move(){}

public void move(){
System.out.println("鸟儿在飞翔");
}
}

class Cat extends Animal{
public void move(){
System.out.println("猫在走猫步");
}
}

// 如果Bird是抽象类的话,那么这个Animal中继承过来的方法可以不去“实现”
/*
abstract class Bird extends Animal{

}
*/

/*
分析:
Animal是父类,并且是抽象的。
Animal这个抽象类中有一个抽象方法move.

Bird是子类,并且是非抽象的。
Bird继承Animal之后,会将抽象方法继承过来。

*/

 

1.3 总结抽象类

1 抽象类怎么定义?
在class关键字之前添加abstract关键字即可。

2 抽象类是无法实例化的,无法创建对象的,所以抽象类是用来被子类继承的。

3 final和abstract不能联合使用,这两个关键字是对立的

4 抽象类的子类可以是抽象类,也可以是非抽象类。

5 抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是给子类用的。

6 抽象类中不一定有抽象方法,抽象方法只能出现在抽象类中。

7 抽象方法怎么定义?
public abstract void doSome();

8 一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行重写/覆盖/实现。

面试题:java语言中凡是没有方法体的方法都是抽象方法。
不对,错误的。
Object类中就有很多方法都没有方法体,都是以";"结尾的,但它们都不是抽象方法,例如:
public native int hashCode();
这个方法底层调用了C++写的动态链接库程序。
前面修饰符列表中没有abstract,有一个native。表示调用JVM本地程序。

 

 

2 接口

2.1 接口的基础语法1

1、接口也是一种引用数据类型。

2、接口是完全抽象的。(抽象类是半抽象。)或者可以说接口是特殊的抽象类。

3、接口怎么定义,语法是什么?
[修饰符列表] interface 接口名{
}

4、接口支持多继承,一个接口可以继承多个接口。

5、接口中只包含两部分内容,一部分是:常量,一部分是:抽象方法。

6、接口中所有元素都是public修饰的。(都是公开的)

7 接口中的抽象方法定义时,public abstract 修饰符可以省略。

8、接口中的方法都是抽象方法,所以接口中的方法不能有方法体。

9、接口中的常量的public static final 可以省略。


public class Test01{
public static void main(String[] args){
// 访问接口的常量
System.out.println(MyMath.PI);

// 常量能重新赋值吗?
// 错误:无法为最终变量PI分配值
// MyMath.PI = 3.14;
}
}

// 定义接口
interface A{

}

// 定义接口
interface B{

}

// 定义接口
// 一个接口可以继承多个接口(支持多继承)
interface C extends A, B{

}

// 我的数学接口
interface MyMath{

// 常量
// public static final 可以省略吗?
// public static final double PI = 3.1415926;
double PI = 3.1415926;

// 抽象方法
// public abstract int sum(int a, int b);

// 接口当中既然都是抽象方法,那么在编写代码的时候,public abstract 可以省略吗?
int sum(int a, int b);

// 接口中的方法可以有方法体吗?
// 错误:接口抽象方法不能带有要体
// void doSome(){}

// 相减的抽象方法
int sub(int a, int b);

}
 

2.2 接口的基础语法2

1、类和类之间叫做继承,类和接口之间叫做实现。 
你仍然可以将“实现”看做“继承”
继承使用extends关键字完成。
实现使用implements关键字完成。

2、当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法实现


public class Test02{
public static void main(String[] args){
// 错误:MyMath是抽象的,无法实例化
// new MyMath();

// 能使用多态吗?可以
// Animal a = new Cat();

// 父类型的引用指向子类型的对象
MyMath mm = new MyMathImp1();
// 调用接口里面的方法
int result1 = mm.sum(11, 22);
System.out.println(result1);
int result2 = mm.sub(22, 11);
System.out.println(result2);
}
}

// 我的数学接口
interface MyMath{
double PI = 3.1415926;
int sum(int a, int b);
int sub(int a, int b);
}

// 编写一个类,非抽象的类
// 这样没问题
/*
abstract class MyMathImp1 implements MyMath{

}
*/

// 错误:MyMathImp1不是抽象的,并且未覆盖MyMath中的抽象方法sub(int, int)
/*
class MyMathImp1 implements MyMath{

}
*/

// 修正
class MyMathImp1 implements MyMath{
// 重写/覆盖/实现 接口中的方法
public int sum(int a, int b){
return a + b;
}

public int sub(int a, int b){
return a - b;
}
}

 

2.3 接口的基础语法3

接口和接口之间可以实现多继承,那么一个类可以同时实现多个接口吗?
对于计算机来说,一个机箱上有多个接口,一个接口是接键盘,一个接口是接鼠标的...

重点:一个类可以实现多个接口。

这种机制弥补了java中的哪个缺陷?
java中类和类只支持单继承。实际上单继承是为了简单才出现的,java中的接口弥补了单继承带来的缺陷。

之前有一个结论:
无论向上转型还是向下转型,两种类型之间必须要有继承关系
没有继承关系编译器会报错。(这句话不适用在接口方面。)
最终实际上和之前还是一样,需要加:instanceof运算符进行判断。


public class Test03{
public static void main(String[] args){
// 多态该怎么用呢?
A a = new D();
// a.m2(); // 编译报错,A接口中没有m2()方法
B b = new D();
C c = new D();

// 这个编译没问题,运行也没问题
// 调用其它接口中的方法,你需要转型(接口转型)
B b2 = (B)a;
b2.m2();

M m = new E();
// 经过测试:接口和接口之间在进行强制类型转换的时候,没有继承关系也可以强转
// 但是一定要注意,运行时可能会出现ClassCaseException异常。
// K k = (K)m;
if(m instanceof K){
K k = (K)m;
}
}
}

interface K{}
interface M{}
class E implements M{}

/*
interface X{};
interface Y{};
interface Z extends X, Y{}; // 接口和接口之间支持多继承。
*/

interface A{
void m1();
}

interface B{
void m2();
}

interface C{
void m3();
}

// 实现多个接口,其实就类似于多继承
class D implements A, B, C{
// 实现A接口的m1
public void m1(){

}

public void m2(){
System.out.println("m2...");
}

public void m3(){

}
}

 

2.4 接口的基础语法4

继承和实现都存在的话,代码应该怎么写?
extends关键字在前,implements关键字在后。


public class Test04{
public static void main(String[] args){
// 创建对象
Flyable f = new Cat();
f.fly();

Flyable f2 = new Pig();
f2.fly();

Flyable f3 = new Fish();
f3.fly();
}
}

// 动物类:父类
class Animal{

}

// 可飞翔的接口
interface Flyable{
void fly();
}

// 动物类子类:猫类
// Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫可以飞
class Cat extends Animal implements Flyable{
public void fly(){
System.out.println("飞猫起飞");
}
}

// 蛇类,如果你不想让它飞,可以不实现Flyable接口
// 没有实现这个接口表示你没有翅膀,没有给你插翅膀,你肯定不能飞
class Snake extends Animal{

}

class Pig extends Animal implements Flyable{
public void fly(){
System.out.println("我是一只会飞的猪");
}
}

// 鱼(默认实际上是存在继承的,默认继承Object)
/*
class Fish extends Object implements Flyable{

}
*/
class Fish implements Flyable{  // 没写extends,也是有的,默认继承Object
public void fly(){
System.out.println("我是六眼飞鱼");
}
}

 

 

3 接口在开发中的作用

注意:接口在开发中的作用,类似于多态在开发中的作用。
多态:面向抽象编程,不要面向具体编程,降低程序的耦合度,提高程序的扩展力。
/*
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
// 假设又要养其它的宠物,那么这个时候需要再加1个方法。(需要修改代码)
// 这样扩展力太差了,违背了OCP原则(对扩展开放,对修改关闭)
}
*/

public class Master{
public void feed(Animal a){
// 面向Animal父类编程,父类是比子类更抽象的。
// 所以我们叫做面向抽象编程,不要面向具体编程。
// 这样程序的扩展力就强。
}
}

 

3.1 接口在开发中的作用

接口是不是完全的?是
而我们以后正好要求,面向抽象编程。
面向抽象编程这句话以后可以修改为:面向接口编程。
有了接口就有了可插拔。可插拔表示扩展力很强。

总结:
面向接口 编程,可以降低程序的耦合度,提高程序的扩展力,符合OCP开发原则。
接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度)

接口可以解耦合,解开的是谁和谁的耦合
任何一个接口都有一个调用者和实现者。
接口可以将调用者和实现者解耦合。
调用者面向接口调用
实现者面向接口编写实现

ChinaCooker.java


// 中国厨师
// 实现菜单上的菜
// 厨师是接口的现实者
public class ChinaCooker implements FoodMenu{
// 西红柿炒蛋
public void shiZiChaoDan(){
System.out.println("中餐师傅做的西红柿炒鸡蛋");
}

// 鱼香肉丝
public void yuXiangRouSi(){
System.out.println("中餐师傅做的鱼香肉丝");
}
}

AmericCooker.java


// 西餐厨师
// 厨师是接口的实现者
public class AmericCooker implements FoodMenu{
// 西红柿炒蛋
public void shiZiChaoDan(){
System.out.println("西餐师傅做西红柿炒鸡蛋");
}

// 鱼香肉丝
public void yuXiangRouSi(){
System.out.println("西餐师傅做鱼香肉丝");
}
}

Customer.java


// 顾客
public class Customer{
// 顾客手里有一个菜单
// Customer has a FoodMenu!
// 记住:以后凡是能够使用 has a 来描述的,统一以属性的方式存在。
// 实例变量,属性
// 面向抽象编程,面向接口编程,降低程序的耦合度,提高程序的扩展力。
private FoodMenu foodMenu;  // 要养成封装的好习惯

// 如果以下这样写,就表示写死了(没有可插拔了)
// 中餐厨师
// ChinaCooker cc;
// 中餐厨师
// AmericCooker ac;

// 构造方法
public Customer(){}
public Customer(FoodMenu foodMenu){
this.foodMenu = foodMenu;
}

// setter and getter
public void setFoodMenu(FoodMenu foodMenu){
this.foodMenu = foodMenu;
}
public FoodMenu getFoodMenu(){
return foodMenu;
}

// 提供一个点菜的方法
public void order(){
// 先拿到菜单
FoodMenu fm = this.getFoodMenu();
fm.shiZiChaoDan();
fm.yuXiangRouSi();
}
}

/*
Cat is Animal,但凡满足is a的表示都可以设置为继承。
Customer has a FoodMenu,但凡是满足 has a 的表示都以属性的形式存在。
*/

FoodMenu.java


/*
接口:菜单:抽象的
*/

public interface FoodMenu{
// 西红柿炒蛋
void shiZiChaoDan();

// 鱼香肉丝
void yuXiangRouSi();
}

Test.java


public class Test{
public static void main(String[] args){
// 创建厨师对象
//FoodMenu cooker1 = new ChinaCooker();
FoodMenu cooker2 = new AmericCooker();
// 创建顾客对象
// Customer customer = new Customer(cooker1);
Customer customer2 = new Customer(cooker2);
// 顾客点菜
// customer.order();
customer2.order();
}
}

 

 

4 类型和类型之间的关系:is a、has a、like a

is a:
Cat is a Animal (猫是一个动物)
凡是能够满足 is a 的表示“继承关系”
A extends B

has a:
I has a Pen (我有一只笔)
凡是能够满足 has a 关系的表示“关联关系”
关联关系通常以“属性”的形式存在
A{
B b;
}

like a:
Cooker like a FoodMenu (厨师像一个菜单一样)
凡是能够满足 like a 关系的表示“实现关系”
实现关系通常是:类实现接口
A implements B

 

 

5 抽象类和接口有什么区别?

在这里我们只说一下抽象类和接口在语法上的区别。
至于以后抽象类和接口应该怎么进行选择,通过后面的项目去体会/学习。

抽象类是半抽象的。
接口是完全抽象的。

抽象类中有构造方法。
接口中没有构造方法。

接口和接口之间支持多继承。
类和类之间只能单继承。

一个类可以同时实现多个接口。
一个抽象类只能继承一个类(单继承)

接口中只允许出现常量和抽象方法。

以后接口使用的比抽象类多,一般抽象类使用的还是少。
接口一般都是对“行为”的抽象。
 

 

 

 

 

posted @   路走  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示