Java IO流

Java I/O流

I/O流即input和output产生的流。Java程序是通过“流”的形式进行数据输入和输出。

流是一种抽象的概念,可以理解为输入/输出的途径。I/O部分内容是很庞大的,包括有标准的输入/输出、文件操作、网络上的数据流、字符串流、对象流等。当程序需要读取数据时,就会开启一个通向数据源的流,这个数据源可以是文件、内存或网络连接。类似的,当程序需要写入数据时,就会开启一个通向目的地的流。

流的概念

Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类表示:

  • InputStream
  • OutputStream
  • Reader
  • Writer

Input和Output流继承关系

Reader和Writer流继承关系

输入流和输出流

流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对于输出流,只能往其中写,而不能读它。

输入流

输入流的信息源可以位于文件、内存或网络套接字(Socket)等地方,信息源可以是对象、字符、图像、声音等。

输出流

与输入流类似,程序页能通过打开一个输出流并顺序地写入数据来将信息送至目的端。

字节流与字符流的区别

1. 数据处理方式
  • 字节流:处理原始的字节数据。它适用于所有类型的数据,包括图像、音频、视频等,因为这些数据并不依赖于特定的字符编码。常见的字节流类包括 InputStreamOutputStream 及其子类,例如 FileInputStreamFileOutputStream
  • 字符流:处理字符数据,并且能够自动处理字符编码和解码。字符流是基于字节流的封装,能将字节流转换成字符流,处理时考虑了字符编码(如 UTF-8、ISO-8859-1 等)。常见的字符流类包括 ReaderWriter 及其子类,例如 FileReaderFileWriter
2. 适用场景
  • 字节流:适合于二进制数据的读写,如图像、音频文件等,因为它们的数据通常不需要字符编码转换。例如,当你需要处理一个图片文件时,使用字节流是比较合适的选择。
  • 字符流:适合于文本数据的读写,尤其是当你需要处理不同字符编码时。字符流可以直接处理字符、字符串,并且会根据指定的字符集进行编码和解码。例如,当你读取或写入文本文件时,使用字符流更为方便。
3. 处理单位
  • 字节流:以字节为单位进行读写,操作的是原始的二进制数据。
  • 字符流:以字符为单位进行读写,操作的是文本数据,并会进行字符编码的转换

字节流

在前面我们知道I/O类中所有对字节流处理的类,都继承与InputStream类和OutputStream类。

InputStream类

InputStream 是 Java 中用于读取字节流的抽象类。它是所有字节输入流类的超类,提供了基本的输入流操作方法。InputStream 类的设计使得你可以从各种数据源(如文件、网络、内存等)读取字节数据。以下是关于 InputStream 类的一些关键点:

  1. int read()

    • 读取下一个字节的数据。

    • 如果流末尾已到达,返回 -1

    • 示例:

      1
      int data = inputStream.read();
  2. int read(byte[] b)

    • 从输入流中读取数据到字节数组中。

    • 返回实际读取的字节数,如果流末尾已到达,返回 -1

    • 示例:

      1
      2
      byte[] buffer = new byte[1024];
      int bytesRead = inputStream.read(buffer);
  3. int read(byte[] b, int off, int len)

    • 从输入流中读取数据到字节数组中的指定位置。

    • 参数 off 指定写入数据的起始位置,len 指定最大读取字节数。

    • 返回实际读取的字节数,如果流末尾已到达,返回 -1

    • 示例:

      1
      2
      byte[] buffer = new byte[1024];
      int bytesRead = inputStream.read(buffer, 0, buffer.length);
  4. void close()

    • 关闭输入流并释放与之关联的系统资源。

    • 示例:

      1
      inputStream.close();
  5. long skip(long n)

    • 跳过并丢弃输入流中的 n 个字节。

    • 返回实际跳过的字节数。

    • 示例:

      1
      long skipped = inputStream.skip(100);
  6. available()

    • 返回流中当前可读的字节数,但不一定能保证实际读取的字节数。

    • 示例:

      1
      int availableBytes = inputStream.available();

常见子类:

  • FileInputStream:从文件中读取字节流。
  • ByteArrayInputStream:从内存中的字节数组中读取字节流。
  • BufferedInputStream:提供缓冲功能的输入流,能提高读取效率。
  • DataInputStream:提供读取 Java 原始数据类型的输入流。

示例:

以下是一个简单的使用 FileInputStream 类读取文件内容的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.FileInputStream;
import java.io.IOException;

public class FileReadExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("example.txt")) {
int content;
while ((content = fis.read()) != -1) {
// 打印读取的字节(转换为字符)
System.out.print((char) content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

注意:

  • InputStream 是一个抽象类,不能直接实例化。通常,你会使用其具体子类来进行实际的字节读取操作。
  • 操作完成后,一定要关闭流,以释放系统资源。这通常通过 try-with-resources 语句来自动完成。

OutputStream类

OutputStream 是 Java 中用于写入字节流的抽象类。它是所有字节输出流类的超类,提供了基本的输出流操作方法。OutputStream 类的设计使得你可以将字节数据写入各种数据目标,如文件、网络连接、内存等。以下是 OutputStream 类的一些关键点:

  1. void write(int b)

    • 将指定的字节写入输出流。

    • 参数 b 是要写入的字节(实际上是一个 int 值,但只使用低 8 位)。

    • 示例:

      1
      outputStream.write(65); // 写入字节 'A'
  2. void write(byte[] b)

    • 将字节数组中的所有字节写入输出流。

    • 示例:

      1
      2
      byte[] data = "Hello".getBytes();
      outputStream.write(data);
  3. void write(byte[] b, int off, int len)

    • 将字节数组中的一部分写入输出流。

    • 参数 off 是字节数组中的起始偏移量,len 是要写入的字节数。

    • 示例:

      1
      2
      byte[] data = "Hello World".getBytes();
      outputStream.write(data, 0, 5); // 写入 "Hello"
  4. void flush()

    • 刷新输出流,确保所有缓冲的字节都被写入到目标地。

    • 示例:

      1
      outputStream.flush();
  5. void close()

    • 关闭输出流并释放与之关联的系统资源。

    • 示例:

      1
      outputStream.close();

常见子类:

  • FileOutputStream:将字节写入文件。
  • ByteArrayOutputStream:将字节写入内存中的字节数组。
  • BufferedOutputStream:提供缓冲功能的输出流,能提高写入效率。
  • DataOutputStream:提供写入 Java 原始数据类型的输出流。

示例:

以下是一个简单的使用 FileOutputStream 类写入文件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.FileOutputStream;
import java.io.IOException;

public class FileWriteExample {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("example.txt")) {
String content = "Hello, World!";
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

注意:

  1. 关闭流:使用完 OutputStream 后一定要关闭流,以释放系统资源。通常使用 try-with-resources 语句来自动关闭流。
  2. 缓冲:如果你需要提高写入效率,可以使用 BufferedOutputStream 进行缓冲操作。
  3. 写入数据:在写入数据时,可以选择将字节数组、单个字节或字节数组的部分写入流。确保正确处理偏移量和长度,以避免写入不必要的数据或产生异常。

OutputStream 提供了处理字节数据的基本操作,了解其使用方法可以帮助你在处理文件、网络和其他数据目标时有效地输出字节数据。

字符流

InputStream和OutputStream在早期的Java版本中就已经存在了,它们是基于字节流的,而基于字符流的Reader和Writer是后来加入作为补充的。

Rreader类

Reader 类是 Java 中用于处理字符流的抽象类。它是所有字符输入流类的超类,提供了基本的字符读取操作方法。与 InputStream 类不同,Reader 处理的是字符而不是原始字节,因此它会根据指定的字符编码进行字符转换。

其主要操作方法有下述几种,因与IO类类似,故其不在赘述。

  1. int read()
  2. int read(char[] cbuf)
  3. int read(char[] cbuf, int off, int len)
  4. long skip(long n)
  5. boolean ready()
  6. void close()

常见子类:

  • FileReader:从文件中读取字符流。
  • BufferedReader:提供缓冲功能的字符流,能提高读取效率,并且提供了 readLine() 方法以便逐行读取文本。
  • CharArrayReader:从内存中的字符数组中读取字符流。
  • StringReader:从内存中的字符串读取字符流。

示例:

以下是一个简单的使用 FileReader 类读取文件内容的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.FileReader;
import java.io.IOException;

public class FileReadExample {
public static void main(String[] args) {
try (FileReader fr = new FileReader("example.txt")) {
int character;
while ((character = fr.read()) != -1) {
System.out.print((char) character);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

使用 BufferedReader 逐行读取文本的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderExample {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

注意:

字符编码Reader 类会处理字符编码问题,因此在读取文件时,确保文件编码与你的 Reader 配置一致。

关闭流:使用完 Reader 后,一定要关闭流以释放系统资源。通常使用 try-with-resources 语句来自动关闭流。

缓冲:为了提高读取效率,可以使用 BufferedReader 类,它提供了缓冲功能,并且能更高效地读取数据。

Writer类

Writer 类是 Java 中用于处理字符流的抽象类。它是所有字符输出流类的超类,提供了基本的字符输出操作方法。与 OutputStream 类不同,Writer 处理的是字符数据,而不是原始的字节数据,因此它会根据指定的字符编码进行字符转换。

  1. void write(int c)

    • 将指定的字符写入输出流。

    • 参数 c 是要写入的字符(实际是一个 int 值,但只使用低 16 位)。

    • 示例:

      1
      writer.write('A'); // 写入字符 'A'
  2. void write(char[] cbuf)

    • 将字符数组中的所有字符写入输出流。

    • 示例:

      1
      2
      char[] data = "Hello".toCharArray();
      writer.write(data);
  3. void write(char[] cbuf, int off, int len)

    • 将字符数组中的一部分写入输出流。

    • 参数 off 是字符数组中的起始偏移量,len 是要写入的字符数。

    • 示例:

      1
      2
      char[] data = "Hello World".toCharArray();
      writer.write(data, 0, 5); // 写入 "Hello"
  4. void write(String str)

    • 将字符串中的所有字符写入输出流。

    • 示例:

      1
      writer.write("Hello, World!");
  5. void write(String str, int off, int len)

    • 将字符串的指定部分写入输出流。

    • 参数 off 是字符串中的起始位置,len 是要写入的字符数。

    • 示例:

      1
      writer.write("Hello, World!", 0, 5); // 写入 "Hello"
  6. void flush()

    • 刷新输出流,确保所有缓冲的字符都被写入到目标地。

    • 示例:

      1
      writer.flush();
  7. void close()

    • 关闭输出流并释放与之关联的系统资源。

    • 示例:

      1
      writer.close();

常见子类:

  • FileWriter:将字符写入文件。
  • BufferedWriter:提供缓冲功能的字符流,能提高写入效率。
  • CharArrayWriter:将字符写入内存中的字符数组。
  • PrintWriter:提供了更多格式化功能的字符输出流,支持打印各种数据类型。

示例:

以下是一个简单的使用 FileWriter 类写入文件的示例:

1
2
3
4
5
6
7
8
9
10
11
12
import java.io.FileWriter;
import java.io.IOException;

public class FileWriteExample {
public static void main(String[] args) {
try (FileWriter fw = new FileWriter("example.txt")) {
fw.write("Hello, World!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

使用 BufferedWriter 提高写入效率的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterExample {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("example.txt"))) {
bw.write("Hello, World!");
bw.newLine(); // 添加新行
bw.write("This is a new line.");
} catch (IOException e) {
e.printStackTrace();
}
}
}

注意:

  1. 字符编码Writer 类会处理字符编码问题,因此在写入文件时,确保文件编码与你的 Writer 配置一致。如果需要特定的编码格式,可以使用 OutputStreamWriter 类来指定编码。
  2. 关闭流:使用完 Writer 后,一定要关闭流以释放系统资源。通常使用 try-with-resources 语句来自动关闭流。
  3. 缓冲:为了提高写入效率,可以使用 BufferedWriter 类,它提供了缓冲功能,并能更高效地写入数据。

实现用户输入

Java提供了java.util.Scanner类,可以直接接收控制台命令行的输入。

使用System.in获取用户输入

Java提供了System.in、System.out及System.err类。

System.out是一个已经预先处理过的、被包装成PrintStream的对象。

System.err和System.out一样,也是一个PrintStream。

但System.in就不是了,它是一个未经处理的InputStream。

System.in输入示例:

1
2
3
4
5
6
7
8
9
10
import java.io.IOException;

public class System_test {
public static void main(String[] args) throws IOException {
char a;
System.out.println("请输入一个字符:");
a=(char)System.in.read();
System.out.println("输入的是" + a);
}
}

使用Scanner类获取用户输入

java.util.Scanner类是JDK新增的一个类,可使用该类创建一个命令行读取数据的对象,而不必再进行流的转换。使用方法如下:

1
Scannner sc = new Scanner(System.in);

然后我们就可以对sc进行调用操作,比如:

1
String n = sc.nextLine();

这里我们注意了,可以有多种调用方式:

  • next():字符串
  • nextInt():整型
  • nextDouble():浮点型
  • nextLine():字符串,但可以读入空格

在绝对路径创建文件夹,并创建一个临时txt文件

  • FileCreateAndDir.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.bayeeaa.demo1.IO_test;

import java.io.File;
import java.io.IOException;

public class FileCreateAndDir { //文件创建和目录

public static boolean createDir(String destDirName){ //创建目录
File dir = new File(destDirName);
if(dir.exists()){
System.out.println("目标目录存在!");
return false;
}
if(!destDirName.endsWith(File.separator)){ //结尾是否以"/"结束
destDirName = destDirName + File.separator;
}
if(dir.mkdir()){ //这里创建目录,并返回true
System.out.println("目录成功创建!");
return true;
}
else{
System.out.println("创建失败!");
return false;
}
}

public static boolean createFile(String filePath){
File file = new File(filePath);
if(file.exists()){
System.out.println("目标文件已存在!");
return false;
}
if(filePath.endsWith(File.separator)){
System.out.println("目标文件不能是文件!");
return false;
}
if(!file.getParentFile().exists()){
System.out.println("目标文件文件夹不存在,正在创建它!");
if(!file.getParentFile().mkdir()){
System.out.println("创建目标文件失败!");
return false;
}
}
try {
if(file.createNewFile()){
System.out.println("文件创建成功!" + filePath);
return true;
}
else{
System.out.println("文件创建失败!");
return false;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

  • test:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.bayeeaa.demo1.IO_test;

import java.io.*;

public class test {
public static void main(String[] args) {
String dirPath = "C:\\Users\\yyn19\\Desktop\\demoTest";
FileCreateAndDir.createDir(dirPath); // 创建目录

String filePath = dirPath + File.separator + "myfile.txt"; // 文件路径,并给出文件名
FileCreateAndDir.createFile(filePath); // 创建文件
}
}


Java IO流
https://bayeeaa.github.io/2024/09/03/Java-IO流/
Author
Ye
Posted on
September 3, 2024
Licensed under