面向对象编程之—抽象类和接口

灰太狼 2024-04-08 10:37 297阅读 0赞

目录

一、抽象类

1.概念

抽象方法:

2.abstract关键字

3.抽象类三个原则

1)抽象类无法直接实例化对象,得通过向上转型实例化

2)抽象类仍然可以有构造方法,普通方法等

3)final abstract能否同时出现?private abstract能否同时出现?

4.抽象类意义

5.抽象类的局限

二、接口

1.概念

2.定义接口—interface关键字

1)定义

2)实现接口

3.使用原则

1)接口中只有public权限,接口中只有全局常量和抽象方法

2)接口的多实现:接口没单继承限制,一个子类可以同时实现多个接口

3)一个接口可以使用extends继承多个父接口

4)子类若同时继承父类,实现接口

4.接口的应用

1)定义规范、标准

2)表示一种能力或行为

5.JDK内置的两大接口

1)java.lang.Comparable:比较接口

2)java.lang.Cloneable:克隆接口

三、接口(interface)和抽象类(abstract)的区别


一、抽象类

接面向对象编程三大特性—封装、继承和多态_林纾y的博客-CSDN博客

1.概念

抽象类是普通类的超集(普通类有的抽象类都有),只是比普通类多了一些(0~N)抽象方法,抽象类abstract修饰。现实生活中,抽象类很多(无法映射到具体的对象的类),如人类,动物类…

抽象方法:

父类当中的方法,被他的子类们重写,子类的各自实现又不一样。那么父类的方法声明和方法体,只有声明还有意义,而方法体内容则没有存在的意义。我们把这种没有方法体内容的方法称为抽象方法。Java语言规定,如果一个类包含了抽象的方法。那么该类就是一个抽象类。

抽象方法:只有声明、没有方法体{}的方法

  1. public abstract void print();//抽象方法
  2. public void print(){}//普通方法

抽象类:包含抽象方法的类

  1. 修饰符 abstract class ClassName {
  2. public abstract void print();
  3. }

2.abstract关键字

在java中,使用abstract表示抽象类和抽象方法,抽象方法所在类一定是抽象类。

  1. 使用abstract关键字修饰成员方法,该方法就成了抽象方法:
  2. 修饰符 abstract 返回值类型 方法名(参数列表);
  3. public abstract void run();
  4. public abstract void eat();
  5. public abstract void jump();
  6. public abstract void print();

3.抽象类三个原则

1)抽象类无法直接实例化对象,得通过向上转型实例化

Sharp sharp=new Sharp();//❌

A a=new B();//✔✔

子类继承抽象类,必须覆写抽象类中的所有抽象方法(前提:子类是普通类),当子类也是抽象类,可以选择不覆写抽象方法。

  1. public abstract class Triangle extends Sharp{//子类Triangle也是抽象类
  2. }

2)抽象类仍然可以有构造方法,普通方法等

普通类有的,抽象类也有,子类仍然遵循对象的实例化流程(先调用父类构造方法)

3)final abstract能否同时出现?private abstract能否同时出现?

final修饰的类不能有子类,而abstract修饰的类必有子类,矛盾,不可。private修饰的方法是当前类内部可见,说明子类没法覆写,abstract修饰的抽象方法必须被覆写,矛盾,不可

4.抽象类意义

抽象类最大意义就是强制要求子类(子类是普通类时)必须覆写抽象方法,保证多态正确运行

5.抽象类的局限

1)子类继承抽象类,仍然满足is a原则(继承原则)

2)抽象类仍然是类,是单继承局限,一个类(子类)只能继承一个抽象类(父类)

所以,也就有了更混合的结构:接口

二、接口

1.概念

接口就是比“抽象类”还“抽象”的“抽象类”, 可以更加规范的对子类进行约束。全面地专业地实现了规范和具体实现的分离。接口是完全面向规范的,规定了一批类具有的公共方法规范,接口就是一个契约、协议。

从接口的实现者角度看,接口定义了可以向外部提供的服务。

从接口的调用者角度看,接口定义了实现者能提供哪些服务。

接口和实现类不是父子关系,是实现规则的关系。比如:我定义一个接口Runnable , Car实现它就能在地上跑, Train实现它也能在地上跑,飞机实现它也能在地上跑。就是说,如果它是交通工具,就定能跑,但是一定要实现Runnable接口。

2.定义接口—interface关键字

1)Java中使用interface关键字定义接口,接口中只有全局常量和抽象方法(JDK8之前,JDK8扩展了default方法)

2)子类使用implements实现接口

3)一般接口命名使用大写I开头和类做区分,子类实现接口可以使用impl结尾,表示是接口的子类

1)定义

格式:

[访问修饰符号] interface 接口name [extends 父接口1,父接口2] {

常量定义:

方法定义:

}

  1. //interface定义了一个IMessage接口
  2. public interface IMessage {
  3. //全局常量-⭐在 Java 接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为 public、static 和 final,即常量,所以接口中定义的变量必须初始化
  4. public static final int NUM=100;
  5. //抽象方法-⭐方法的声明不需要其他修饰符,在接口中声明的方法,将隐式地声明为公有的(public)和抽象的(abstract)
  6. public String msg();
  7. }

注意:接口没有构造方法,不能被实例化

2)实现接口

  1. public class MessageImpl implements IMessage{
  2. @Override
  3. public String msg() {
  4. return "hello bit";
  5. }
  6. }

3.使用原则

1)接口中只有public权限,接口中只有全局常量和抽象方法

所以,只有在接口中(接口内部)如下关键字可以省略:abstract(接口中全是抽象方法)static final public【阿里编码规约中,定义接口尽量要简洁,上面这些都不需要写】

定义接口的简写如下:

  1. public interface IMessage {
  2. //全局常量
  3. int NUM=100;
  4. //抽象方法
  5. String msg();
  6. }

2)接口的多实现:接口没单继承限制,一个子类可以同时实现多个接口

1)子类在使用多个父接口时,使用”,”分隔。子类必须要实现父接口中所有抽象方法(前提:子类是普通类)

2)子类实现接口没有is a原则,is a原则只是描述类之间的继承

  1. public class MessageImpl implements IMessage,INews{//逗号分隔,且子类必须覆写所有抽象方法
  2. @Override
  3. public String msg() {
  4. return "hello bit";
  5. }
  6. @Override
  7. public void getNews() {
  8. System.out.println("hello news");
  9. }
  10. }
  11. public interface INews {
  12. void getNews();
  13. }

一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法并且该类也必须定义成抽象类。

3)一个接口可以使用extends继承多个父接口

  1. interface A{
  2. void testA();
  3. }
  4. interface B{
  5. void testB();
  6. }
  7. interface C extends A,B{//接口C继承了接口A,B
  8. void testC();
  9. }
  10. public class DTest implements C{//使用接口C,C又继承了A,B,所以总共要覆写3个方法
  11. @Override
  12. public void testA() {
  13. }
  14. @Override
  15. public void testB() {
  16. }
  17. @Override
  18. public void testC() {
  19. }
  20. }

子接口可以继承多个父接口,注意在类上不能用extends关键字继承接口,类上是说实现接口,用implements实现接口。

注意:

接口仍然不能直接实例化对象,需通过子接口的向上转型实现实例化

抽象类都不能直接new(实例化对象),更何况接口

278e73b6f4a847adb6ae26f0ba6d70f9.png

  1. public static void main(String[] args) {
  2. IMessage message=new MessageImpl();//创建一个子类
  3. System.out.println(message.msg());
  4. }

父接口之间的相互转化

eg:message只能调用msg()方法message.msg();而不能调用message.getNews();

5f673e723b034aefb90ccebbfcb5222e.png

why?->getNews方法定义在INews接口中,而这里用的IMessage接口定义。要想使用getNews方法,就需要父接口间的相互转换(把IMessage引用转换成INews的引用)

  1. INews news=(INews)message;//强转
  2. news.getNews();

对比总结:

父类/父接口 父引用=new 子类实例();

到底能调用什么方法,看父类/父接口中定义了啥方法;调用的方法长什么样子,看new的是哪个子类

4)子类若同时继承父类,实现接口

先使用extends继承一个父类,而后使用implements实现多个接口

public class DTest extends D implements A,B{

D是DTest的父类,DTest子类通过implements实现A,B两个父接口

4.接口的应用

1)定义规范、标准

eg:USB接口

  1. interface USB{//定义了USB接口
  2. void steUp();//安装驱动
  3. void work();//正常工作
  4. }

鼠标-实现了USB接口,能插到电脑上的设备都要实现这个接口

键盘类同,为了让设备通过USB线连接到电脑,就必须实现USB接口,与鼠标都是USB的实现者

电脑-需要实现USB接口吗?:不用,只要该设备通过USB数据线连接到电脑上,都需要实现USB接口,而没有一个电脑能通过一根USB线连到另一个电脑。电脑是USB接口的使用者而非实现者【

什么是使用者?类的内部拥有USB接口对象】

  1. class Mouse implements USB{}
  2. class KeyBoard implements USB{}
  3. class Computer
  4. /**
  5. * 电脑-USB接口的使用者
  6. */
  7. public class Computer {
  8. //电脑上的USB插口1
  9. public void plugIn1(USB port1){
  10. port1.setUp();
  11. port1.work();
  12. }
  13. //电脑上的USB插口2
  14. public void plugIn2(USB port2){
  15. port2.setUp();
  16. port2.work();
  17. }
  18. //如何使用
  19. public static void main(String[] args) {
  20. Computer computer=new Computer();
  21. computer.plugIn1(new Mouse());//mouse插到第一个接口
  22. computer.plugIn2(new Keyboard());//keyboard插到第二个接口
  23. }
  24. }

e5a5bd83a14c4250add25696ebfc9b4a.png

提问:为何对电脑来说,插入方法的参数是USB接口【plugIn1(USB port1)】而不是具体子类?比如说Mouse或Keyboard【plugeIn1(Mouse mouse)】?

plugeln1(Mouse mouse)说明此时这个方法只能接收Mouse类型的参数,即电脑的这个插口1只能插入鼠标,键盘也是这个道理。假设这么设计电脑,若有100种不同类型的USB设备,电脑就得开放100个端口来插入。而使用这些USB设备共同的父接口USB接口,电脑根本不关心具体插入的是啥设备,只要该设备实现USB接口,都可以连接。

#

2)表示一种能力或行为

003ac433e34f4faeaaa7cb33697535e2.png

定义了飞、跑、游的能力

5.JDK内置的两大接口

1)java.lang.Comparable:比较接口

457550b77b184307bbfa691fefb96896.png

Student是自定义类型,当使用Arrays.sort对自定义类型进行排序时,自定义类型需要实现Comparable接口,使其具备可比较的能力。

Comparable中有1个方法:public int compareTo(To);

对于返回值int:=0,说明当前对象等于目标对象o

0,说明当前对象大于目标对象o

<0,说明当前对象小于目标对象o

  1. public class Student implements Comparable<Student>{//<>是一个泛型,就是类型的意思
  2. private String name;
  3. private int age;
  4. public Student(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. //假设根据年龄大小比较
  9. @Override
  10. public int compareTo(Student o) {//年龄按升序排列,小的在前。若想要降序排列,<时return 1,>时return -1
  11. if(this.age==o.age){
  12. return 0;
  13. }else if(this.age<o.age){
  14. return -1;
  15. }else{
  16. return 1;
  17. }
  18. }

总结:一个类实现了Comparable接口,说明这个类就具备了可比较的能力,具体的规则就是覆写compareTo方法,让它具备可比较能力,compareTo中规则可以自己定,现在可以用年龄去比,也可以按姓名比,也可以按分数比。

2)java.lang.Cloneable:克隆接口

程序中的克隆:复制一个新的对象,新的对象的属性值是从旧对象中拷贝过来的

1)Clone是标记接口,本身没有任何抽象方法,当一个类实现了Cloneable接口,表示该类具备了克隆的能力(JVM赋予的能力),在堆上开辟空间和对象创建都是JVM来做的。JVM会识别所有带克隆标识的类,赋予该类克隆的能力。

2)实现(implements)接口后应该自己覆写Object类提供的clone方法(不是Clone接口给的,Clone接口本身没有任何抽象方法)

  1. public class Animal implements Cloneable{
  2. private String name;
  3. @Override
  4. protected Animal clone() throws CloneNotSupportedException {
  5. return (Animal) super.clone();
  6. }
  7. }

直接打clone()回车完成覆写,Object处改为Animal,在做一个(Animal)的强制类型转换即可。

3.注意对比区分下面情况【问题判断:没有方法体的方法就是抽象方法?—错误】

  1. //抽象方法
  2. public abstract void print();

观察如下native方法,也没有方法体{},但不是抽象方法

  1. protected native Object clone() throws CloneNotSupportedException;

native方法是本地方法,clone方法不是Java语言实现的,是C++写的(JVM就是C++实现的)。本地方法表示Java调用了C++的同名方法,此处只是方法声明,具体的方法实现在C++中。

拓展:JNDI调->用C++实现的方法平台

4)测试

  1. public class Animal implements Cloneable{
  2. private String name;
  3. @Override
  4. protected Animal clone() throws CloneNotSupportedException {
  5. return (Animal) super.clone();
  6. }
  7. public static void main(String[] args) throws CloneNotSupportedException {
  8. Animal animal=new Animal();
  9. animal.name="zhangsan";
  10. Animal animal1=animal.clone();
  11. System.out.println(animal==animal1);
  12. System.out.println(animal1.name);
  13. }
  14. }

//animal==animal1返回false,说明两个确实不是一个东西,animal1是通过animal对象复制过来的,但不是引用的复制,而是创建了一个新的对象,把属性值拷贝,是在堆上开辟了新空间的。这里也涉及深浅拷贝的概念。

//animal1.name打印出来也是zhangsan说明确实达到了拷贝

关于深浅拷贝概念,见:

三、接口(interface)和抽象类(abstract)的区别

































比较 抽象类 接口
结构组成 普通类+抽象方法(抽象类是普通类的超集) 抽象方法+全局常量
权限 可以有各种权限 public权限(没有指定 public 的接口,其访问将局限于所属的包)
子类使用 使用extends关键字继承抽象类 使用implements关键字实现接口
子类限制 一个子类只能继承一个抽象类 一个子类可以实现多个接口
关系 一个抽象类可以实现若干个接口,接口不能继承抽象类,但接口可以使用extends关键字继承多个父接口

下一篇:

Java中23种设计模式之—基于抽象类的模板设计模式_林纾y的博客-CSDN博客

Java中23种设计模式—(关于接口的)代理设计模式_林纾y的博客-CSDN博客

发表评论

表情:
评论列表 (有 0 条评论,297人围观)

还没有评论,来说两句吧...

相关阅读