JavaRush /Java 博客 /Random-ZH /抽象类和接口的区别

抽象类和接口的区别

已在 Random-ZH 群组中发布
你好!在本次讲座中,我们将讨论抽象类与接口的不同之处,并查看常见抽象类的示例。 抽象类和接口的区别 - 1我们专门用一个单独的讲座来讨论抽象类和接口之间的差异,因为这个主题非常重要。在未来 90% 的面试中,你都会被问到这些概念之间的区别。因此,请务必理解您所阅读的内容,如果您不完全理解某些内容,请阅读其他来源。所以,我们知道什么是抽象类,什么是接口。现在让我们来看看它们的差异。
  1. 接口仅描述行为。他没有财富。但抽象类有一个状态:它描述了两者。

    让我们以抽象类Bird和接口为例Flyable

    public abstract class Bird {
       private String species;
       private int age;
    
       public abstract void fly();
    
       public String getSpecies() {
           return species;
       }
    
       public void setSpecies(String species) {
           this.species = species;
       }
    
       public int getAge() {
           return age;
       }
    
       public void setAge(int age) {
           this.age = age;
       }
    }

    让我们创建一个 Bird 类Mockingjay(mockingjay) 并继承自Bird

    public class Mockingjay extends Bird {
    
       @Override
       public void fly() {
           System.out.println("Fly, birdie!");
       }
    
       public static void main(String[] args) {
    
           Mockingjay someBird = new Mockingjay();
           someBird.setAge(19);
           System.out.println(someBird.getAge());
       }
    }

    正如您所看到的,我们可以轻松访问抽象类的状态 - 它的变量species(类型)和age(年龄)。

    但如果我们尝试对界面做同样的事情,图片就会有所不同。我们可以尝试向其中添加变量:

    public interface Flyable {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface Flyable {
    
       private String species = new String(); // error
       private int age = 10; // also an error
    
       public void fly();
    }

    我们甚至无法在接口内创建私有变量。为什么?因为创建private修饰符是为了向用户隐藏实现。但接口内部没有实现:那里没有什么可隐藏的。

    接口仅描述行为。因此,我们将无法在接口内实现 getter 和 setter。这就是界面的本质:它旨在处理行为,而不是状态。

    Java8 引入了具有实现的默认接口方法。你已经知道它们了,所以我们不再重复。

  2. 抽象类链接并联合具有非常密切关系的类。同时,相同的接口可以由完全没有共同点的类来实现。

    让我们回到鸟类的例子。

    Bird需要我们的抽象类来基于它创建鸟类。只有鸟,没有其他人!当然他们会有所不同。

    抽象类和接口的区别 - 2

    有了界面,Flyable一切都不同了。它只是描述了与其名字相对应的行为——“飞行”。“飞行”、“能够飞行”的定义包括许多彼此不相关的物体。

    抽象类和接口的区别 - 3

    这 4 个实体彼此之间没有任何关系。我能说什么,并不是所有的动物都是有生命的。然而,它们都有Flyable飞行能力。

    我们无法使用抽象类来描述它们。它们没有共同的状态或相同的字段。为了描述一架飞机的特征,我们可能需要“型号”、“制造年份”和“最大乘客数量”字段。对于卡尔森来说,有他今天吃的所有糖果的字段,以及他将与孩子一起玩的游戏列表。对于一只蚊子...呃-呃...我们甚至不知道...也许是“烦恼程度”?:)

    最主要的是我们不能使用抽象类来描述它们。他们太不同了。但有一个共同的行为:它们会飞。该界面非常适合描述世界上所有可以飞行、游泳、跳跃或具有其他行为的事物。

  3. 类可以实现任意多个接口,但只能从一个类继承。

    我们已经不止一次讨论过这个问题。Java中没有多重继承,但有多重实现。这一点在一定程度上源于上一点:接口连接了许多不同的类,这些类通常没有任何共同点,而抽象类是为一组彼此非常接近的类创建的。因此,您只能从一个这样的类继承是合乎逻辑的。抽象类描述了“是”关系。

标准输入流和输出流接口

我们已经了解了负责流输入和输出的各种类。我们来看看InputStreamOutputStream。一般来说,这些不是接口,而是真正的抽象类。现在您知道它们是什么,因此使用它们会更容易:) InputStream- 这是一个负责字节输入的抽象类。Java 有一系列继承自InputStream. 它们中的每一个都被配置为从不同的源接收数据。因为InputStream它是父级,所以它提供了几种方便地处理数据流的方法。每个孩子都有这些方法InputStream
  • int available()返回可供读取的字节数;
  • close()关闭输入源;
  • int read()返回流中下一个可用字节的整数表示形式。如果到达流末尾,则返回数字-1;
  • int read(byte[] buffer)尝试将字节读入缓冲区,返回读取的字节数。当到达文件末尾时,返回-1;
  • int read(byte[] buffer, int byteOffset, int byteCount)读取字节块的一部分。当数据块可能未完全填满时使用。当到达文件末尾时,返回-1;
  • long skip(long byteCount)Skips byteCount,一个输入字节,返回忽略的字节数。
我建议您研究完整的方法列表。后继班其实还有十几个。这里有一些例子:
  1. FileInputStream:最常见的类型InputStream。用于从文件中读取信息;
  2. StringBufferInputStream:另一种有用的类型InputStream。它将字符串转换为输入数据流InputStream
  3. BufferedInputStream:缓冲输入流。它最常用于提高效率。
你还记得当我们走过时BufferedReader说我们不必使用它吗? 当我们写:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
...BufferedReader无需使用它:InputStreamReader它会完成这项工作。但BufferedReader它的效率更高,而且可以读取整行数据,而不是单个字符。一切BufferedInputStream都一样!该类将输入数据累积在特殊的缓冲区中,而无需不断访问输入设备。让我们看一个例子:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

public class BufferedInputExample {

   public static void main(String[] args) throws Exception {
       InputStream inputStream = null;
       BufferedInputStream buffer = null;

       try {

           inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");

           buffer = new BufferedInputStream(inputStream);

           while(buffer.available()>0) {

               char c = (char)buffer.read();

               System.out.println("Character was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
在此示例中,我们从位于计算机上地址“D:/Users/UserName/someFile.txt”的文件中读取数据。我们创建 2 个对象 -FileInputStreamBufferedInputStream作为它的“包装器”。之后,我们从文件中读取字节并将其转换为字符。依此类推,直到文件结束。正如您所看到的,这里没有什么复杂的。您可以复制此代码并在计算机上存储的某个真实文件上运行它:) 类OutputStream是定义字节流输出的抽象类。正如您已经了解的,这是InputStream'a 的对映体。它不负责从哪里读取数据,而是负责将数据发送到哪里。与 一样InputStream,这个抽象类为所有后代提供了一组方法以方便工作:
  • int close()关闭输出流;
  • void flush()清除所有输出缓冲区;
  • abstract void write (int oneByte)将 1 个字节写入输出流;
  • void write (byte[] buffer)将字节数组写入输出流;
  • void write (byte[] buffer, int offset, int count)从数组中写入一系列 count 字节,从位置 offset 开始。
以下是该类的一些后代OutputStream
  1. DataOutputStream。包含用于编写标准 Java 数据类型的方法的输出流。

    一个非常简单的类,用于编写原始 Java 类型和字符串。即使不解释,你也一定会理解编写的代码:

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt"));
    
           dos.writeUTF("SomeString");
           dos.writeInt(22);
           dos.writeDouble(1.21323);
           dos.writeBoolean(true);
    
       }
    }

    它对每种类型都有单独的方法 - writeDouble()writeLong()writeShort()

  2. 班级 FileOutputStream。实现一种将数据发送到磁盘上的文件的机制。顺便说一句,我们已经在前面的示例中使用了它,您注意到了吗?我们将它传递到 DataOutputStream 内部,它充当“包装器”。

  3. BufferedOutputStream。缓冲输出流。BufferedInputStream也不复杂,本质与(or 'a)中的相同BufferedReader。使用通过特殊“存储”缓冲区进行记录,而不是通常的顺序数据记录。通过使用缓冲区,您可以减少到数据目的地的往返次数,从而提高效率。

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt");
           BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
    
           String text = "I love Java!"; // we will convert this string into an array of bytes and write it to a file
    
           byte[] buffer = text.getBytes();
    
           bufferedStream.write(buffer, 0, buffer.length);
           bufferedStream.close();
       }
    }

    同样,您可以自己“玩”此代码,并检查它如何在您计算机上的真实文件上运行。

您还可以在材料“InputStream输入/输出系统OutputStream”中阅读有关继承人的内容。哦,我们还会有一个单独的讲座,所以对于第一次认识他们来说,有足够的信息。就这样!我们希望您能够很好地理解接口和抽象类之间的差异,并准备好回答任何问题,即使是一个棘手的问题:) FileInputStreamFileOutputStreamBufferedInputStream
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION