Java IO流

SoloWalker Lv3

Java IO流

流简介

什么是流

在 java 中,流是对数据传输的抽象。我们可以想象成一个水管,数据是水,从源流向目的地。不管数据来自文件、网络、内存还是键盘,处理方式都是类似的。

流的本质是字节序列,java 用统一的视角看待所有 I/O 操作。

IO 是指 Input/Output,即输入和输出,以内存为中心。

那么为什么要把数据读到内存中才能处理这些数据?因为代码都是在内存中运行的,数据也必须读取到内存中,最终的表示方式一般是 byte 数组,字符串等,都必须存放在内存中。

从 java 代码看,输入则是从外部(如硬盘上的某个文件)把内容读取到内存中,并且以 java 提供的某种数据类型表示。例如,byte[]String等,这样后续代码才能处理这些数据。

因为内存有“易失性”的特点,即一断电数据就消失了,所以必须把处理之后的数据以某种形式输出,例如写入文件。Output 实际上就是把 java 表示的数据格式(如byte[]String)输出到某个地方。

IO 流是一种顺序读写数据的模式,特点是单向流动,数据类似水一样。

创建文件的三种方式

根据路径创建一个 File 对象

使用new File(String pathname)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package IOStream;

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

public class IOStreamTest {

public static void createFile() throws Exception{
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt");
try {
file.createNewFile();
System.out.println("successful");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) throws Exception{
createFile();
}
}

根据父目录 File 对象,在子路径创建一个文件

使用new File(File parent,String child)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package IOStream;

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

public class IOStreamTest {

public static void createFile() throws Exception{
File pfile = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files");
File cfile = new File(pfile, "test1.txt");
try {
cfile.createNewFile();
System.out.println("successful");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) throws Exception{
createFile();
}
}

根据父目录路径,在子路径生成文件

使用new File(String parent,String child)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package IOStream;

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

public class IOStreamTest {

public static void createFile() throws Exception{
String parentpath="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files";
String filename="test2.txt";
File file = new File(parentpath, filename);
try {
file.createNewFile();
System.out.println("successful");
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) throws Exception{
createFile();
}
}

获取文件信息

利用File类的一些方法获取基本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package IOStream;

import java.io.File;

public class GetFileInfo {
public static void main(String[] args) {
getFileContents();
}

public static void getFileContents(){
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt");
System.out.println("文件名:"+file.getName());
System.out.println("文件绝对路径:"+file.getAbsolutePath());
System.out.println("文件上一级目录:"+file.getParent());
System.out.println("文件大小(字节):"+file.length());
System.out.println("是不是文件:"+file.isFile());
System.out.println("是不是目录:"+file.isDirectory());
}
}

目录与文件操作

文件删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package IOStream;

import java.io.File;

public class FileDelete {

public static void deleteFile(){
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test2.txt");

System.out.println(file.delete() ? "successful" : "failed");
}
public static void main(String[] args) {
deleteFile();;
}
}

目录删除

注:只有空的目录可以删除,不然会显示删除失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package IOStream;

import java.io.File;

public class DeleteDict {
public static void main(String[] args) {
deleteDict();
}

public static void deleteDict(){
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\FileDeleteTest");
System.out.println(file.delete()?"successful":"failed");
}
}

创建单级目录

使用file.mkdir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package IOStream;

import java.io.File;

public class CreateSingleDict {
public static void main(String[] args) {
create_single_dict();
}

public static void create_single_dict(){
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\CreateDict");
System.out.println(file.mkdir()?"successful":"failed");
}
}

创建多级目录

使用file.mkdirs()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package IOStream;

import java.io.File;

public class CreateSingleDict {
public static void main(String[] args) {
create_single_dict();
}

public static void create_single_dict(){
File file = new File("C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\CreateMultiDict\\test");
System.out.println(file.mkdirs()?"successful":"failed");
}
}

IO流分类

按照处理单位分为:字节流和字符流

类型 处理单位 基类 适用场景
字节流 8位字节 InputStream/OutputStream 图片、视频、音频、可执行文件等二进制数据
字符流 16位字符(在Unicode编码下) Reader/Writer 文本文件、字符串等字符数据

按照数据流流向不同分为:输入流和输出流

按照流的角色不同分为:节点流、处理流/包装流

类型 说明
节点流 直接与数据源/目标连接,如FileInputStream
处理流 包装节点流,添加缓冲、转换等功能,如BufferedInputStream

文件流的一些操作

Runtime 命令执行操作的 payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package CmdExec;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class RuntimeExec {
public static void main(String[] args) throws IOException {
InputStream inst = Runtime.getRuntime().exec("whoami").getInputStream();
byte[] bytes = new byte[1024];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int readlen=0;
while ((readlen=inst.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,readlen);
}
System.out.println(byteArrayOutputStream);
}
}

这个 payload 把 IO 流和命令执行结合,本质是一个通过 IO 流窃取命令执行结果。

InputStream inst = Runtime.getRuntime().exec("whoami").getInputStream();这段代码建立了一个管道,主要干了三件事:

  1. exec("whoami"):启动一个子进程来执行系统命令。
  2. getInputStream():获取子进程的标准输出流。
  • 对于我们的 java 程序来说,获取子进程产生的数据是一个输入流
  • 可以理解为,java 程序与子进程之间有一个管道相连,子进程把执行结果写入管道,我通过字节输入流从管道中读取出来。
1
2
byte[] bytes = new byte[1024];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

这里的byte[] bytes = new byte[1024]是用于缓存数据的,是一个经典的中转处理流。bytes是一个缓冲区,每次从输入流中最多获取1024字节的数据。

ByteArrayOutputStream():这是一个目的地在内存的字节输出流,会把所有从输入流中读取的数据暂时存到内存中。

1
2
3
4
int readlen=0;
while ((readlen=inst.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,readlen);
}

这是 IO 流操作中最核心、最机械的循环模式。

  1. inst.read(bytes):从输入流中读取数据,放进bytes这个缓冲区,返回实际读取到的字节数。
  2. =-1判断:如果读到末尾(子进程输出完毕,管道关闭),read方法会返回-1,循环结束。
  3. write写入内存:把读取到的有效数据写入ByteArrayOutputStream内存仓库中。

循环结束,说明子进程whoami的所有输出已经转移到 JVM 内存中了。

1
System.out.println(byteArrayOutputStream);

这里使用println会自动调用byteArrayOutputStream.toString()方法,使用系统默认的字符编码,把内存中的字节数组解码成字符串输出。

FileInputStream

read() 方法

这个方法会从输入流中读取一个数据字节,如果没有输入,此方法将阻塞。返回值是下一个数据字节,如果已经到达文件末尾,则返回-1。

下面的样例使用FileInputStream.read()读取文件内容:

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
package IOStream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputRead {
public static void readfile() throws IOException {
String pathname = "C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
int readdata = 0;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(pathname);
while ((readdata = fileInputStream.read()) != -1) {
System.out.print((char) readdata);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
fileInputStream.close();
}

}

public static void main(String[] args) throws IOException {
readfile();
}
}

fileInputStream = new FileInputStream(pathname);:在 java 程序和硬盘之间建立了一个字节输入管道。FileInputStream是节点流,直接连接数据源。

while ((readdata = fileInputStream.read()) != -1):逐字节读取数据。因为流本质是二进制数据,所以我们接收数据的变量的数据类型要是数字类型的。注:java 在读取文件中的字节时是按照无符号方式解读,所以一个字节的值的范围是从0255,而 byte 类型数据是-128127,已经超出范围,所以需要用 int 来接收数据。

System.out.print((char) readdata);:这里则是把接收的数据经过 ASCII 编码强转成字符。

read(byte[] b) 方法

这个方法和上面的 read() 方法完全不同,下面是不同之处:

方法 一次读取量 返回值含义
read() 1个字节 字节本身
read(byte[]) 最多填满数组 实际读取的字节数

样例:

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
package IOStream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputRead {
public static void readfile() throws IOException {
String pathname = "C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
int readdata = 0;

byte[] bytes = new byte[8];
try (FileInputStream fileInputStream = new FileInputStream(pathname)) {
while ((readdata = fileInputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, readdata));
}
} catch (IOException e) {
throw new RuntimeException(e);
}

}

public static void main(String[] args) throws IOException {
readfile();
}
}

FileOutputStream

write(int b) 方法

这个方法很简单,和上面的 read() 是相反的。read() 是逐个字节读取,而 write(int b) 是把 b 的最低8位写入文件,高24位直接忽略。

write(byte[] b) 方法

这个方法是FileOutputStream最常用的写入方式之一。

与 write(int b) 写单个字节不同,这个方法一次性写入整个字节数组到文件输出流中,没有返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package IOStream;

import java.io.FileOutputStream;

public class FileOutputWrite {
public static void main(String[] args) throws Exception {
writeFile();
}

public static void writeFile() throws Exception{
String filepath="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
try (FileOutputStream fileOutputStream = new FileOutputStream(filepath)) {
String contents="solo";
fileOutputStream.write(contents.getBytes());
//String类型的字符串可以使用getBytes()方法将字符串转换为byte数组
}catch (Exception e){
e.printStackTrace();
}
}
}

write(byte[] b,int off,int len)

指定 byte[] 数组中从偏移量 off 开始的 len 个字节写入文件输出流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package IOStream;

import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;

public class FileOutputWrite {
public static void main(String[] args) throws Exception {
writeFile();
}

public static void writeFile() throws Exception{
String filepath="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
try (FileOutputStream fileOutputStream = new FileOutputStream(filepath)) {
String contents="solowalker";
fileOutputStream.write(contents.getBytes(StandardCharsets.UTF_8),0,contents.length());

}catch (Exception e){
e.printStackTrace();
}
}
}

追加写入

如果写入的数据不想覆盖之前的数据,可以设置FileOutputStream的构造方法append参数为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package IOStream;

import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;

public class FileOutputWrite {
public static void main(String[] args) throws Exception {
writeFile();
}

public static void writeFile() throws Exception{
String filepath="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";

try (FileOutputStream fileOutputStream = new FileOutputStream(filepath,true)){

String contents="solo";
fileOutputStream.write(contents.getBytes(StandardCharsets.UTF_8),0,contents.length());

}catch (Exception e){
e.printStackTrace();
}
}
}

文件拷贝

FileInputStreamFileOutputStream结合起来,原理就是先把文件的内容读取出来,然后写入新的文件。

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
package IOStream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;

public class FileOutputWrite {
public static void main(String[] args) throws Exception {
copyfile();
}

public static void copyfile() throws Exception{
String originalfile="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
String cpyfile="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test1.txt";
byte[] bytes = new byte[1024];
int readlen=0;
try (FileInputStream fileInputStream = new FileInputStream(originalfile);
FileOutputStream fileOutputStream = new FileOutputStream(cpyfile)) {
while ((readlen=fileInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,readlen);
}
}catch (Exception e){
e.printStackTrace();
}

}
}

FileReader

这是 java io 中最常用的字符文件输入流,和FileInputStream很像,但是有本质区别。

FileInputStream FileReader
父类 InputStream Reader
处理单位 字节(byte) 字符(char)
适用场景 图片视频等任意二进制文件 纯文本文件(.txt,.java,.xml)
读取中文 会把多字节字符拆解,出现乱码 自动按照字符读取,中文完整没有乱码
底层原理 直接读取字节 内部使用InputStream读取字节,再按照编码解码成字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package IOStream;

import java.io.FileReader;

public class FileReadPrint {
public static void main(String[] args) {
readfile();
}

public static void readfile(){
String filepath="C:\\Users\\32202\\Desktop\\java测试\\test\\src\\IOStream\\Files\\test.txt";
try(FileReader fileReader = new FileReader(filepath)){
int readlen=0;
char[] codes = new char[8];
while ((readlen= fileReader.read(codes))!=-1){
System.out.println(new String(codes,0,readlen));
}
}catch (Exception e){
e.printStackTrace();
}

}
}

这段代码和FileInputStream的意思是类似的。

总结

花了两个晚上左右的时间来搞定这一篇博客和 java io 流的学习,对 io 流这种东西算是有了一个系统的理解,剩下的就要在题目中学习了,光学习了理论没有进入实战环境感觉还是有点虚的,等 java 安全基础这部分理论学完就会开始 java 基础开发阶段和 cc 链。希望学习 java 安全能够顺利进行下去。

向着大厂 offer 进军!!!

  • Title: Java IO流
  • Author: SoloWalker
  • Created at : 2026-04-29 00:00:00
  • Updated at : 2026-04-30 21:45:00
  • Link: https://s0lowalker.github.io/2026/04/29/java-IO流/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments