IO流介绍
Java提供了很多种类型的I/O,以读取文件、处理数据、网络通讯等等。IO大致分为两种字节流(二进制流)和字符流(文件流),其中字节流又分为字节输入,输出流和字节缓冲输入,输出流。字符流又分为字符输入,输出流和字符缓冲输入,输出流。
分类
字节流:
字节输入流: InputStream
字节输出流: OutputStream
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOutputStream
字符流
字符流抽象类:Reader
字符流抽象类:Writer
字符输入缓冲流:BufferedReader
字符输出缓冲流:BufferedWriter
区别
- 字节流操作的基本单元是字节;字符流操作的基本单元是字符
- 字节流默认不使用缓冲区;字符流使用缓冲区
- 字节流通常用于处理二进制数据,不支持直接读写字符;字符流通常用于处理文本数据
- 在读写文件需要对文本内容进行处理:按行处理、比较特定字符的时候一般会选择字符流;仅仅读写文件,不处理内容,一般选择字节流
字节流
字节流是以字节为单位进行输入和输出的数据流,常用于处理二进制数据和音视频文件等特定的数据类型。
字节流主要包括字节输入流和字节输出流,以及字节缓冲输入流和字节缓冲输出流。
一、字节输入流
字节输入流用于读取数据,Java 中提供了 InputStream
类来实现字节输入流的操作。通过 InputStream
类的 read()
方法读取数据,该方法返回的是读取的字节的整型值。如果返回值为 -1,则表示已经读取到流的末尾。
示例代码:
import java.io.*;
public class ByteStreamExample1 {
public static void main(String[] args) {
try {
InputStream inputStream = new FileInputStream(new File("D:\\example.txt"));
int data = inputStream.read();
while(data != -1){
System.out.print((char)data);
data = inputStream.read();
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
二、字节输出流
字节输出流用于将数据写入文件或其他输出流中,Java 中提供了 OutputStream
类来实现字节输出流的操作。通过 OutputStream
类的 write()
方法写入数据,该方法会将传入参数中的字节数组写入流中。
示例代码:
import java.io.*;
public class ByteStreamExample2 {
public static void main(String[] args) {
try {
byte[] data = "Hello World".getBytes();
OutputStream outputStream = new FileOutputStream(new File("D:\\example.txt"));
outputStream.write(data);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、字节缓冲输入流和字节缓冲输出流
字节缓冲输入流和字节缓冲输出流是使用缓冲区来优化字节输入输出流的性能。Java 中提供了 BufferedInputStream
和 BufferedOutputStream
类来实现字节缓冲输入输出流的操作。
示例代码:
import java.io.*;
public class ByteStreamExample3 {
public static void main(String[] args) {
try {
//创建字节输入流
InputStream inputStream = new FileInputStream(new File("D:\\example.txt"));
//创建字节缓冲输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
//输入
int data = bufferedInputStream.read();
while(data != -1){
System.out.print((char)data);
data = bufferedInputStream.read();
}
//关闭字节缓冲输入流
bufferedInputStream.close();
//关闭字节输入流
inputStream.close();
byte[] data2 = "Hello World".getBytes();
//创建输出流
OutputStream outputStream = new FileOutputStream(new File("D:\\example.txt"));
//创建字节缓冲输出流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
//输出
bufferedOutputStream.write(data2);
bufferedOutputStream.flush();
bufferedOutputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、字节流的特点和使用场景
字节流是以字节为单位进行输入和输出的数据流,适用于处理二进制数据和音视频文件等特定的数据类型。相较于字符流,字节流处理数据的速度更快,但不方便阅读和编辑。
字节流特点:
-
以字节为最小单位,能够处理二进制数据,适用于处理音视频文件、图片文件等特定数据类型。
-
处理速度快,比字符流快得多。
-
不支持字符编码转换,读取字节后需要手动转换为其他编码格式。
-
不便于阅读和编辑,需使用其他工具进行编辑。
字节流的应用场景:
-
音视频文件、图片文件的读写,例如MP3、JPEG格式的数据。
-
网络编程中的数据传输,如Socket通信时需要使用字节流进行数据传输。
-
压缩文件的读写,例如压缩包的制作和解压缩等。
-
二进制数据的处理,如序列化和反序列化等处理。
在实际应用中,字节流通常用于对特定数据类型(如音视频文件、压缩文件)进行读写操作,以及传输二进制数据等场景。
字符流
字符流是用于读取文本数据的输入流和用于写入文本数据的输出流。与字节流不同,字符流会将数据作为字符流式读取或写入。在Java中,字符流主要包括字符输入流、字符输出流、字符缓冲输入流和字符缓冲输出流。
一、字符输入流
字符输入流是用于读取字符数据的流。在Java中,常用的字符输入流类有FileReader
、InputStreamReader
和CharArrayReader
等。FileReader
类是通过的 read()
方法输入数据。
示例代码:
FileReader fr = new FileReader("D:\\test.txt");
int c;
while ((c = fr.read()) != -1) {
System.out.print((char) c);
}
fr.close();
FileWriter fw = new FileWriter("D:\\test.txt");
String str = "Hello World";
fw.write(str);
fw.close();
二、字符输出流
字符输出流是用于将字符数据写入到输出流中。在Java中,常用的字符输出流类有FileWriter
、OutputStreamWriter
和CharArrayWriter
等。FileWriter
类是通过的 write()
方法输出数据。
示例代码:
try {
FileWriter fw = new FileWriter("D:\\test.txt");
String str = "hello world";
fw.write(str);
fw.close();
} catch(Exception e) {
e.printStackTrace();
}
三、字符缓冲输入流和字符缓冲输出流
字符缓冲输入流是用于读取字符数据并缓存字符数据的流。在Java中,常用的字符缓冲输入流类有BufferedReader
和StringReader
等。BufferedReader
类是通过的 read()
方法输入数据。
示例代码:
try {
FileReader fr = new FileReader("D:\\test.txt");
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} catch(Exception e) {
e.printStackTrace();
}
字符缓冲输出流是用于将字符数据写入到缓存中并在一定条件下将缓存中的数据写入到输出流中的流。在Java中,常用的字符缓冲输出流类有BufferedWriter
和StringWriter
等。BufferedWriter
类是通过的 write()
方法输出数据。
示例代码:
try {
FileWriter fw = new FileWriter("D:\\test.txt");
BufferedWriter bw = new BufferedWriter(fw);
String str = "hello world";
bw.write(str);
bw.close();
} catch(Exception e) {
e.printStackTrace();
}
四、字符流的特点和使用场景
字符流的特点是对于文本数据的读取和写入比较方便,可以避免处理编码问题,比如在读取或写入中文时,字符流可以自动进行编码转换。字符流主要适用于读取和写入文本类型的文件,比如txt文件、html文件等。而字节流适用于处理所有类型的文件,比如图片、影音等二进制类型的文件。
字符流的特点:
-
字符流一次可以处理多个字符,所以适用于处理文本文件。
-
字符流采用Unicode编码,因此可以处理多种语言的字符。
-
字符流是面向字符的,因此比字节流更容易处理文本数据。
常见的字符流:
-
FileReader和FileWriter: FileReader和FileWriter类用于读写字符文件的数据。它们是基于文件的字符流,可以读写小型的文本文件。
-
BufferedReader和 BufferedWriter: BufferedReader 和 BufferedWriter 用于提供缓冲机制,以增加读写速度。它们通过内部缓存处理,可以提高文件的读写效率。
-
CharArrayReader和CharArrayWriter: CharArrayReader和CharArrayWriter能够将数据读取到内存中的字符数组中,以便在内存中处理和操作数据。
-
InputStreamReader 和 OutputStreamWriter: InputStreamReader和OutputStreamWriter是连接字节流和字符流的类。 它们可以将字节流转换为字符流,从而处理文本数据。
字符流适用于在处理字符串数据时需要读取或写入大量字符的情况,特别是在处理文本文件或网络传输数据时。常见的使用场景包括:
-
读取文本文件:使用字符流可以逐行读取文本文件中的内容,进行处理并输出。
-
写入文本文件:使用字符流可以将文本数据写入到文件中,例如日志文件、配置文件等。
-
处理网络传输数据:使用字符流可以读取和写入通过网络传输的文本数据,例如HTTP协议中的请求和响应。
-
处理标准输入输出:使用字符流可以从控制台读取用户输入并进行处理,也可以将程序输出打印到控制台。
-
处理字符数据:使用字符流可以对字符数据进行读取、加密、解密、转换等操作。
总之,字符流是处理文本数据的常用工具,适用于各种情况下需要读取或写入大量字符的场景。
JAVA之IO流传输案例
现在让我们来看看如何使用 Java IO 流进行文件传输。要传输文件,我们首先需要创建一个输入流来读取源文件,然后使用输出流将其写入目标文件。以下是一个示例代码,它将从一个文件中读取数据并将其写入另一个文件。
import java.io.*;
public class FileTransferExample {
public static void main(String[] args) throws IOException {
//创建输入流和输出流
FileInputStream inputStream = new FileInputStream("D:\\source.txt");
FileOutputStream outputStream = new FileOutputStream("D:\\target.txt");
//定义缓冲区大小
byte[] buffer = new byte[1024];
int bytesRead;
//开始传输
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
//关闭流
inputStream.close();
outputStream.close();
System.out.println("文件传输成功!");
}
}
在上面的代码中,我们创建了一个输入流和一个输出流来读取源文件和写入目标文件。然后,我们定义了一个缓冲区大小并使用 while 循环逐块读取源文件中的数据,并使用输出流写入目标文件中。最后,我们关闭了输入流和输出流,并输出一条传输成功的消息。
JAVAIO之NIO、AIO、BIO
Java IO(输入/输出)是Java中用于访问数据的标准机制。Java包含三种IO类:BIO,NIO,AIO。
BIO(同步阻塞I/O)
BIO(Blocking I/O)是 Java IO 的最基本形式,也是所有I/O形式的基础。在BIO中,所有I/O操作都是同步的并阻塞线程,比如:当线程从输入流中读取数据时,线程会被阻塞,直到有数据可读。同样,当线程向输出流中写入数据时,线程也被阻塞,直到所有的数据都被写入。
优点:
- 简单易学易用。
- 可以处理大部分的IO场景。
缺点:
- 性能较差,当读写数据时会阻塞线程。
- 编写复杂应用程序时,代码不易维护。
实现:
- InputStream和OutputStream。
- InputStreamReader和OutputStreamWriter。
- FileInputStream和FileOutputStream。
NIO(同步非阻塞I/O)
NIO(New I/O)是Java 1.4引进的一种新的I/O API,提供了比BIO更高效的I/O机制。在NIO中,所有I/O操作都是非阻塞的,并且采取轮询方式,不会阻塞线程。当数据就绪时,线程会通过回调函数被通知。NIO使用多路复用器(Selector),通过一个线程轮询多个通道,从而管理多个输入和输出通道。
优点:
- 更高效的IO操作,非阻塞,避免线程被阻塞。
- 更适合处理较大数量的连接。
- 适用于客户端和服务端均需要读写数据的场景,如 IM、聊天室等。
缺点:
- 编程比BIO难,需要了解更多的概念和技术。
- 可能会出现粘包、拆包问题,需要程序员自己解决。
实现:
- Selector:负责多个通道的管理,决定哪个通道是已经准备好进行IO操作的通道。
- Channel:是通道的共同接口。
- SocketChannel:类似客户端的套接字。
- ServerSocketChannel:类似服务器的套接字。
- DatagramChannel:类似UDP协议的套接字。
AIO(异步非阻塞I/O)
AIO(Asynchronous I/O)是Java 1.7引进的一种新的I/O API。AIO比NIO更进一步,它尽可能地使用异步消息通知机制来处理事件(如连接、读、写等)。
在AIO中,当一个I/O操作完成时,操作系统会向应用程序发送信号,告诉它读写完成,程序可以开始处理数据了。与NIO不同的是,AIO的操作是基于事件触发的,因此,无需阻塞和轮询。当一个事件完成时,操作系统会通知应用程序,应用程序接到通知后再去处理该事件。
优点:
- 适用于高负载并发场景,如:Web服务器。
- 将I/O操作交给操作系统完成,业务线程无需参与处理。
- 可以提供更高的吞吐量和更低的延迟。
缺点:
- 不适用于短期的I/O操作,存在一些额外的系统开销。
实现:
- AsynchronousSocketChannel。
- AsynchronousServerSocketChannel。
- AsynchronousFileChannel。
不同场景下的IO方式选择
BIO适用于以下两种场景:
- 简单的应用程序,在这种情况下,只需要使用少量的连接和数据。
- 需要保持连接的场景,如HTTP协议,一般客户端和服务端都需要保持连接。
NIO适用于以下场景:
- 需要同时处理多个连接。
- 连接之间的数据传输量较小。
- 较多时间处于等待状态的连接,如长连接。
AIO适用于以下场景:
- I/O请求频繁且数据传输量较大的场景,如文件上传和下载。
- 需要处理高并发且I/O操作耗时的场景,如高并发的Web服务器。