-
接口仅描述行为。他没有财富。但抽象类有一个状态:它描述了两者。
让我们以抽象类
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 引入了具有实现的默认接口方法。你已经知道它们了,所以我们不再重复。
-
抽象类链接并联合具有非常密切关系的类。同时,相同的接口可以由完全没有共同点的类来实现。
让我们回到鸟类的例子。
Bird
需要我们的抽象类来基于它创建鸟类。只有鸟,没有其他人!当然他们会有所不同。有了界面,
Flyable
一切都不同了。它只是描述了与其名字相对应的行为——“飞行”。“飞行”、“能够飞行”的定义包括许多彼此不相关的物体。这 4 个实体彼此之间没有任何关系。我能说什么,并不是所有的动物都是有生命的。然而,它们都有
Flyable
飞行能力。我们无法使用抽象类来描述它们。它们没有共同的状态或相同的字段。为了描述一架飞机的特征,我们可能需要“型号”、“制造年份”和“最大乘客数量”字段。对于卡尔森来说,有他今天吃的所有糖果的字段,以及他将与孩子一起玩的游戏列表。对于一只蚊子...呃-呃...我们甚至不知道...也许是“烦恼程度”?:)
最主要的是我们不能使用抽象类来描述它们。他们太不同了。但有一个共同的行为:它们会飞。该界面非常适合描述世界上所有可以飞行、游泳、跳跃或具有其他行为的事物。
-
类可以实现任意多个接口,但只能从一个类继承。
我们已经不止一次讨论过这个问题。Java中没有多重继承,但有多重实现。这一点在一定程度上源于上一点:接口连接了许多不同的类,这些类通常没有任何共同点,而抽象类是为一组彼此非常接近的类创建的。因此,您只能从一个这样的类继承是合乎逻辑的。抽象类描述了“是”关系。
标准输入流和输出流接口
我们已经了解了负责流输入和输出的各种类。我们来看看InputStream
和OutputStream
。一般来说,这些不是接口,而是真正的抽象类。现在您知道它们是什么,因此使用它们会更容易:) 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)
SkipsbyteCount
,一个输入字节,返回忽略的字节数。
FileInputStream
:最常见的类型InputStream
。用于从文件中读取信息;StringBufferInputStream
:另一种有用的类型InputStream
。它将字符串转换为输入数据流InputStream
;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 个对象 -FileInputStream
并BufferedInputStream
作为它的“包装器”。之后,我们从文件中读取字节并将其转换为字符。依此类推,直到文件结束。正如您所看到的,这里没有什么复杂的。您可以复制此代码并在计算机上存储的某个真实文件上运行它:) 类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
:
-
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()
。 -
班级
FileOutputStream
。实现一种将数据发送到磁盘上的文件的机制。顺便说一句,我们已经在前面的示例中使用了它,您注意到了吗?我们将它传递到 DataOutputStream 内部,它充当“包装器”。 -
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
”中阅读有关继承人的内容。哦,我们还会有一个单独的讲座,所以对于第一次认识他们来说,有足够的信息。就这样!我们希望您能够很好地理解接口和抽象类之间的差异,并准备好回答任何问题,即使是一个棘手的问题:) FileInputStream
FileOutputStream
BufferedInputStream
GO TO FULL VERSION