6.java——接口,抽象类

2023-12-26 10:05:38

抽象类——没有实例对象,没有方法实体

先将一个故事,为什么父类的方法有用呢?**扪心自问一下父类我们真正用的多吗?**不多,我们大部分时间都是直接用子类对象的。把话说难听点就是——子类对父类真正的继承的只是他的方法名字,不是他的方法体。
由于多态的存在,**每个子类都可以覆写父类的方法,**例如:

class Person {
    public void run() {}
}

class Student extends Person {
    @Override
    public void run() {}
}

class Teacher extends Person {
    @Override
    public void run() {}
}

从Person类派生的Student和Teacher都可以覆写run()方法。
如果父类Person的run()方法没有实际意义,能否去掉方法的执行语句?

class Person {
    public void run(); // Compile Error!
}

答案是不行,会导致编译错误,因为定义方法的时候,必须实现方法的语句。
能不能去掉父类的run()方法?
答案还是不行,因为去掉父类的run()方法,就失去了多态的特性。例如,runTwice()就无法编译

public void runTwice(Person p) {
    p.run(); // Person没有run()方法,会导致编译错误
    p.run();
}

如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法:


```java
class Person {
    public abstract void run();
}

接下来的故事才刚刚开始

把一个方法声明为abstract,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的(没有函数体的函数无法执行),所以,Person类也无法被实例化。编译器会告诉我们,无法编译Person类,因为它包含抽象方法。
必须把Person类本身也声明为abstract,才能正确编译它:

abstract class Person {
    public abstract void run();
}

下面是一个经典的例子

  public  interface  Dog  {
           void  FurColor();
        }

  abstract  class  WhiteDog  implements  Dog{
  public  void  FurColor(){
     System.out.println("Fur  is  white");
           }
     abstract  void  SmallBody();
    }

在抽象类中,具有如下特征
如果—个类中有抽象方法,那么这个类—定是抽象类,也就是说,使用关键字 abstract 修饰的 方法—定是抽象方法,具有抽象方法的类—定是抽象类。实现类方法中只有方法具体的实现。

抽象类中不—定只有抽象方法,抽象类中也可以有具体的方法,你可以自己去选择是否实现这些方 法。

抽象类中的约束不像接口那么严格,你可以在抽象类中定义 构造方法、抽象方法、普通属性、方 法、静态属性和静态方法

抽象类和接口—样不能被实例化,实例化只能实例化 具体的类

版本2

如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类:

Person p = new Person(); // 编译错误

无法实例化的抽象类有什么用?
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
例如,Person类定义了抽象方法run(),那么,在实现子类Student的时候,就必须覆写run()方法:

// abstract class
public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run();
    }
}

abstract class Person {
    public abstract void run();
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

接口——在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。

定义:接口(Interface)在Java中是一种提供抽象方法定义的机制,是一种行为规范的协定。接口中只能包含抽象方法和常量(静态final变量),而不能包含普通的成员属性(非常量的实例变量)。
接口(高度抽象——抽象到他就不是一个正常的类了,没有实例,也没有方法体,连class都没有了)
接口相当于就是对外的—种约定和标准,这里拿操作系统举例子,为什么会有操作系统?就会为了屏蔽 软件的复杂性和硬件的简单性之间的差异,为软件提供统—的标准。
在 Java 语言中,接口是由 interface 关键字来表示的,比如我们可以向下面这样定义—个接口

public interface CxuanGoodJob {}
比如我们定义了—个 CxuanGoodJob 的接口,然后你就可以在其内部定义 cxuan 做的好的那些事情, 比如 cxuan 写的文章不错。??

   public  interface  CxuanGoodJob  {

      void  writeWell();
        }

这里隐含了—些接口的特征:??
interface 接口是—个完全抽象的类,他不会提供任何方法的实现,只是会进行方法的定义。

接口中只能使用两种访问修饰符, —种是 public ,它对整个项目可见; —种是 default 缺省 值,它只具有包访问权限。

接口只提供方法的定义,接口没有实现,但是接口可以被其他类实现。也就是说,实现接口的类需 要提供方法的实现,实现接口使用 implements 关键字来表示, —个接口可以有多个实现。

 class  CXuanWriteWell  implements  CxuanGoodJob{

    @Override
          public  void  writeWell()  {
             System .out .println("Cxuan  write  Java  is  vary  well");
                }
        }

接口不能被实例化,所以接口中不能有任何构造方法,你定义构造方法编译会出错。

接口的实现比如实现接口的全部方法,否则必须定义为 抽象类 ,这就是我们下面要说的内容

如果一个抽象类没有字段,所有方法全部都是抽象方法:??

abstract class Person {
    public abstract void run();
    public abstract String getName();
}

就可以把该抽象类改写为接口:interface 😀
在Java中,使用interface可以声明一个接口:?

interface Person {
    void run();
    String getName();
}

🌐所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。
当一个具体的class去实现一个interface时,需要使用implements关键字。举个例子:

class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}

?我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个

interface,例如:
class Student implements Person, Hello { // 实现了两个interface
    ...
}

术语

注意区分术语:
🌐Java的接口特指interface的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等。
抽象类和接口的对比如下:

在这里插入图片描述

接口继承

一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:

interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。

继承关系

合理设计interface和abstract class的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class中,具体逻辑放到各个子类,而接口层次代表抽象程度。可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:

┌───────────────┐
│   Iterable    │
└───────────────┘
        ▲                ┌───────────────────┐
        │                │      Object       │
┌───────────────┐        └───────────────────┘
│  Collection   │                  ▲
└───────────────┘                  │
        ▲     ▲          ┌───────────────────┐
        │     └──────────│AbstractCollection │
┌───────────────┐        └───────────────────┘
│     List      │                  ▲
└───────────────┘                  │
              ▲          ┌───────────────────┐
              └──────────│   AbstractList    │
                         └───────────────────┘
                                ▲     ▲
                                │     │
                                │     │
                     ┌────────────┐ ┌────────────┐
                     │ ArrayList  │ │ LinkedList │
                     └────────────┘ └────────────┘

在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:
List list = new ArrayList(); // 用List接口引用具体子类的实例
Collection coll = list; // 向上转型为Collection接口
Iterable it = coll; // 向上转型为Iterable接口

default方法

在接口中,可以定义default方法。例如,把Person接口的run()方法改为default方法:

public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

面向抽象编程

当我们定义了抽象类Person,以及具体的Student、Teacher子类的时候,我们可以通过抽象类Person类型去引用具体的子类的实例:

Person s = new Student();
Person t = new Teacher();

这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person类型变量的具体子类型:
// 不关心Person变量的具体子类型:

s.run();
t.run();

同样的代码,如果引用的是一个新的子类,我们仍然不关心具体类型:

// 同样不关心新的子类是如何实现run()方法的:
Person e = new Employee();
e.run();

这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。
面向抽象编程的本质就是:

  • 上层代码只定义规范(例如:
    abstract class Person);
  • 不需要子类就可以实现业务逻辑(正常编译);
  • 具体的业务逻辑由不同的子类实现,调用者并不关心。
    练习
    用抽象类给一个有工资收入和稿费收入的小伙伴算税

文章来源:https://blog.csdn.net/m0_73464576/article/details/135212693
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。