CodeGym/Java 博客/随机的/抽象类和接口的区别
John Squirrels
第 41 级
San Francisco

抽象类和接口的区别

已在 随机的 群组中发布
个会员
你好!在本课中,我们将讨论抽象类与接口的不同之处,并考虑一些常见抽象类的示例。 抽象类和接口的区别 - 1我们专门用一节课来介绍抽象类和接口之间的区别,因为这个主题非常重要。在 90% 的未来面试中,你会被问及这些概念之间的区别。这意味着您应该确保弄清楚您正在阅读的内容。如果您不完全理解某些内容,请阅读其他资源。所以,我们知道什么是抽象类,什么是接口。现在我们将讨论它们的差异。
  1. 接口仅描述行为。它没有状态。但是抽象类包括状态:它描述了两者。

    Bird以抽象类和接口为例CanFly

    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;
       }
    }

    让我们创建一个MockingJay鸟类并让它继承Bird

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

    如您所见,我们可以轻松访问抽象类的状态——itsspeciesagevariables。

    但如果我们尝试对界面做同样的事情,情况就不一样了。我们可以尝试给它添加变量:

    public interface CanFly {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface CanFly {
    
       private String species = new String(); // Error
       private int age = 10; // Another error
    
       public void fly();
    }

    我们甚至不能在接口内声明私有变量。为什么?因为创建private修饰符是为了对用户隐藏实现。接口内部没有实现:没有什么可以隐藏的。

    接口仅描述行为。因此,我们不能在接口内实现 getter 和 setter。这是接口的本质:它们需要处理行为,而不是状态。

    Java 8 为具有实现的接口引入了默认方法。你已经知道他们了,所以我们不会重复自己。

  2. 抽象类连接和联合非常密切相关的类。同时,单个接口可以由完全没有共同点的类实现。

    让我们回到鸟类的例子。

    需要我们的Bird抽象类来创建基于该类的鸟类。只有鸟,没有别的!当然,会有不同种类的鸟。

    抽象类和接口的区别——2

    有了CanFly界面,每个人都以自己的方式前进。它仅描述与其名称相关的行为(飞行)。许多不相关的东西“可以飞”。

    抽象类和接口的区别——3

    这 4 个实体彼此无关。他们甚至都不是活着的。然而,他们都CanFly

    我们无法使用抽象类来描述它们。它们不共享相同的状态或相同的字段。要定义一架飞机,我们可能需要型号、生产年份和最大乘客人数等字段。对于 Carlson,我们需要一些田地来存放他今天吃的所有糖果,以及他将与他的弟弟一起玩的游戏列表。对于一只蚊子,……呃……我什至不知道……也许,一个“烦人程度”?:)

    关键是我们不能用一个抽象类来描述它们。他们太不一样了。但它们确实有共同的行为:它们会飞。界面非常适合描述世界上所有可以飞行、游泳、跳跃或表现出某些其他行为的事物。

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

    我们已经不止一次提到过这一点。Java没有类的多继承,但是支持接口的多继承。这一点部分源自前一点:一个接口连接许多不同的类,这些类通常没有其他共同点,而抽象类是为一组非常密切相关的类创建的。因此,您只能继承一个这样的类是有道理的。抽象类描述了一种“is-a”关系。

标准接口:InputStream 和 OutputStream

我们已经了解了负责输入和输出流的各种类。让我们考虑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)跳过输入流中的 byteCount 个字节,并返回忽略的字节数。
我建议您研究完整的方法列表。实际上有十多个子类。例如,这里有一些:
  1. FileInputStream: 最常见的类型InputStream。它用于从文件中读取信息;
  2. StringBufferInputStream: 另一种有用的类型InputStream。它将字符串转换为InputStream;
  3. BufferedInputStream:缓冲输入流。它最常用于提高性能。
还记得我们过去BufferedReader说你不必使用它吗? 当我们写:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
......你不必使用BufferedReader: AnInputStreamReader就可以完成这项工作。但是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 read: " + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
在此示例中,我们从位于计算机“ D:/Users/UserName/someFile.txt ”的文件中读取数据。我们创建了 2 个对象——aFileInputStream和一个BufferedInputStream“包装”它的对象。然后我们从文件中读取字节并将它们转换为字符。我们这样做直到文件结束。如您所见,这里没有什么复杂的。您可以复制此代码并在计算机上的真实文件上运行它:) 该类OutputStream是表示字节输出流的抽象类。如您所知,这与InputStream. 它不负责从某处读取数据,而是负责将数据发送到某处。就像 一样InputStream,这个抽象类为它的所有后代提供了一组方便的方法:
  • void close()关闭输出流;
  • void flush()清除所有输出缓冲区;
  • abstract void write(int oneByte)将 1 个字节写入输出流;
  • void write(byte[] buffer)将字节数组写入输出流;
  • void write(byte[] buffer, int offset, int count)从数组的偏移位置开始写入一系列 count 字节。
以下是该类的一些后代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 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'll convert this string to a byte array and write it to a file
    
               byte[] buffer = text.getBytes();
    
               bufferedStream.write(buffer, 0, buffer.length);
         }
    }

    同样,您可以自己试用此代码并验证它是否适用于您计算机上的真实文件。

我们将有一节关于FileInputStream,FileOutputStream和的单独课程BuffreredInputStream,所以对于初次相识的人来说,这些信息已经足够了。 就是这样!我们希望您了解接口和抽象类之间的区别,并准备好回答任何问题,甚至是技巧性问题 :)
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论