学Java不知道IO流是什么怎么下去

drun1baby大佬学习

IO流什么意思?

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

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

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

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

IO 流是一种顺序读写数据的模式,它的特点是单向流动。数据类似自来水一样在水管中流动,所以我们把它称为 IO 流。


在看IO流之前先了解一下java关于文件的操作有助于我们理解IO流

1
2
3
4
代码在:
C:/Users/86182/Desktop/JavaStudy/IOStream/???.java
新文件创建在:
C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/文件夹下

创建文件的三种方式

1,根据路径创建一个 File 对象

1
方法 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;

// 根据路径创建一个 File 对象
public class NewFile {
public static void main(String[] args) {
createFile();
} //主函数调用自定义函数
public static void createFile(){ //定义创建文件函数
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new1.txt"); //声明一个file变量类型是File并且使用new关键字和File类的构造器来创建一个新的File对象
try{
file.createNewFile();
System.out.println("Create Successfully");
} catch (IOException e){ //捕获在文件创建过程中可能发生的IOException异常
e.printStackTrace(); //打印异常信息到标准错误流
}
}
}

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

1
方法 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
package IOStream;

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

// 根据路径创建一个 File 对象
public class NewFile {
public static void main(String[] args) {
createFile();
}
public static void createFile(){
File ParentFile = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/");
File file = new File(ParentFile,"new2.txt");
try{
file.createNewFile();
System.out.println("Create Successfully");
} catch (IOException e){
e.printStackTrace();
}
}
}

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

1
方法 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
package IOStream.NewFile;

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

// 根据路径创建一个 File 对象
public class NewFile {
public static void main(String[] args) {
createFile();
}
public static void createFile(){
File ParentPath = new File("C:/Users/86182/Desktop/JavaStudy/IOstream/CreateFile");
File file = new File(ParentPath,"new3.txt");
try{
file.createNewFile();
System.out.println("Create Successfully");
} catch (IOException e){
e.printStackTrace();
}
}
}

image-20240729160918983

此时创建了三个文件

读取文件基本信息

我们尝试编辑数据进new1.txt

利用

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) {
GetFileContent();
}

public static void GetFileContent(){
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new1.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());
}
}

image-20240729161033935

目录与文件操作

1. 文件删除

1
使用 file.delete(文件)
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 main(String[] args) {
deleteFile();
}
public static void deleteFile(){
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new1.txt");
System.out.println(file.delete() ? "Delete Successfully":"Delete failed"); //利用java三元运算符
}

}

image-20240729161134314

成功删除

2. 创建单级目录

1
方法 file.mkdir()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package IOStream;

import java.io.File;

public class CreateSingleDirectory {
public static void main(String[] args) {
createSingleDirectory ();
}
public static void createSingleDirectory(){
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/TestCreate");
System.out.println(file.mkdir() ? "Create Successfully":"Create failed");
}

}

3. 创建多级目录

1
方法 file.mkdirs(),注意多了个 s 别搞错了,多级目录对吧肯定要有s啦
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package IOStream;

import java.io.File;

public class CreateManyDirectory {
public static void main(String[] args) {
createManyDirectory ();
}
public static void createManyDirectory(){
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/TestCreate1/TestCreate2");
System.out.println(file.mkdirs() ? "Create Successfully":"Create failed");
}

}

image-20240729161443870

4. 目录删除

1
1.注意只有空目录才可以删除,和正常命令删除一样非空目录需要特定语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package IOStream;

import java.io.File;

public class DirectoryDelete {
public static void main(String[] args) {
deleteDirectory();
}
public static void deleteDirectory(){
File file = new File("C:/Users/86182/Desktop/JavaStudy/IOStream/TestDelete");
System.out.println(file.delete() ? "Delete Successfully":"Delete failed");
}

}

image-20240729161521958

成功删除

IO 流分类

按照操作数据单位不同分为:字节流字符流

  • 字节流(8bit,适用于二进制文件)
  • 字符流(按字符,因编码不同而异,适用于文本文件)

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

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

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

到这里就非常重要了,因为它与我们后续的命令执行直接相关。这些 IO 流在我们命令执行的 Payload 当中充当着缓冲的作用。

关于文件流的一些操作

1. Runtime 命令执行操作的 Payload

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

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

// 使用 Runtime 类进行命令执行
public class RuntimeExec {
public static void main(String[] args) throws Exception{
// 执行系统命令 "whoami",并获取执行结果的输入流
InputStream inputStream = Runtime.getRuntime().exec("whoami").getInputStream();

// 创建一个字节数组缓存区,用于存储读取的数据
byte[] cache = new byte[1024];

// 创建一个 ByteArrayOutputStream 对象,用于将读取的数据写入内存缓冲区
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

// 定义一个变量,用于存储每次读取的字节数
int readLen = 0;

// 循环读取输入流中的数据,直到读取到流的末尾(read() 方法返回 -1)
while ((readLen = inputStream.read(cache))!=-1){
// 将读取的数据写入 ByteArrayOutputStream
byteArrayOutputStream.write(cache, 0, readLen);
}

// 将 ByteArrayOutputStream 的内容转换为字符串,并打印到控制台
System.out.println(byteArrayOutputStream);
}
}

1
其中,byte[] cache = new byte[1024] 用来缓存数据

2. FileInputStream

read() 方法

1
之前我们用 file 的一系列操作读取过文件的信息,现在我们用 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
31
package IOStream;

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

// 使用 Runtime 类进行命令执行
// 使用 FileInputStream.read 读取文件
public class FileInputRead {
public static void main(String[] args) {
readFile();
}
public static void readFile(){
String filePath = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new2.txt";
FileInputStream fileInputStream = null;
int readData = 0;
try{
fileInputStream = new FileInputStream(filePath);
while((readData = fileInputStream.read())!=-1){
System.out.print((char)readData);
}
} catch (IOException e){
e.printStackTrace();
} finally {
try{
fileInputStream.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}

在这我们输出语句是利用System.out.print()这样才能正确输出不然他就会一个字符一换行

1
这可能是因为他就是一个字符一个字符读取的如果用println标准输出,每次读取一个字符就加一个换行符输出,但是print是不会对输出加换行符的

image-20240729144353384

read(byte[] d) 方法

1
2
3
4
5
允许在方法中添加一个字节数组。
这种方式很有意思,当我们设置缓冲区的值为 10 时,若文件中的字符长度超过了 10,则会换行输出。
//感觉像一个箱子塞满了就需要第二个箱子了

再回到之前我们讲的 Runtime 类进行命令执行的 Payload,在那里,我们设置的 Cache 缓冲区的值为 1024->1KB
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
package IOStream;

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

// 类名为 FileInputRead
public class FileInputRead {
public static void main(String[] args) {
readFile(); // 在主方法中调用 readFile 方法
}

// readFile 方法声明
public static void readFile(){
String filePath = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new2.txt";
// 声明 FileInputStream 变量,用于读取文件
FileInputStream fileInputStream = null;
// 创建一个字节数组作为缓冲区,大小为 10 字节
byte[] cache = new byte[10];
// 声明一个变量,用于存储每次读取的字节数
int readLen = 0;

try {
// 实例化 FileInputStream,并指定要读取的文件路径
fileInputStream = new FileInputStream(filePath);
// 使用 while 循环读取文件内容,直到文件末尾
while((readLen = fileInputStream.read(cache)) != -1){
// 将读取到的字节转换为字符串,并打印到控制台
// new String(cache, 0, readLen) 创建一个新的字符串对象,从缓冲区 cache 的索引 0 开始,长度为 readLen
System.out.println(new String(cache, 0, readLen));
}
} catch (IOException e){
// 捕获并打印 IOException 异常
e.printStackTrace();
} finally {
// finally 块确保在读取完成后关闭文件输入流
try {
// 如果 fileInputStream 不为 null,则关闭它
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e){
// 捕获并打印关闭流时可能发生的 IOException 异常
e.printStackTrace();
}
}
}
}

image-20240729145127340

while循环了四次输出了全部内容

3. FileOutputStream

1
往文件里面写数据

write(byte[] b) 方法

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

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

// write(byte[] b) 方法
public class FileOutputWrite {
public static void main(String[] args) {
writeFile();
}

public static void writeFile() {
String filePath = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new3.txt";
FileOutputStream fileOutputStream = null;
try { // 注意fileOutputStream的作用域,因为fileOutputStream需要在finally分支中被异常捕获
// 所以这里的 try 先不闭合
fileOutputStream = new FileOutputStream(filePath);
String content = "Hope you have a nice day";
try {
//write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中
//String类型的字符串可以使用getBytes()方法将字符串转换为byte数组
fileOutputStream.write(content.getBytes());
System.out.println("successful write");
} catch (IOException e) {
e.printStackTrace();
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
finally {
try {
fileOutputStream.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}

image-20240729150634007

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
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package IOStream;  

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

// write(byte[] b) 方法
public class FileOutputWrite {
public static void main(String[] args) {
writeFile();
}

public static void writeFile() {
String filePath = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new3.txt";
FileOutputStream fileOutputStream = null;
try { // 注意fileOutputStream的作用域,因为fileOutputStream需要在finally分支中被异常捕获
// 所以这里的 try 先不闭合
fileOutputStream = new FileOutputStream(filePath);
String content = "Grasp the present moment";
try {
//write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中
//String类型的字符串可以使用getBytes()方法将字符串转换为byte数组
fileOutputStream.write(content.getBytes(StandardCharsets.UTF_8), 0, 24);
System.out.println("successful write");
} catch (IOException e) {
e.printStackTrace();
}
}catch (FileNotFoundException e){
e.printStackTrace();
}
finally {
try {
fileOutputStream.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}

image-20240729151432535

如果设置off为6那么根据这个方法介绍len就要是18然后得到

image-20240729151545090

追加写入

1
如果想要写入的数据不被覆盖,可以设置 FileOutputStream 的构造方法 append 参数设置为 true
1
2
3
fileOutputStream = new FileOutputStream(filePath);
// 设置追加写入
fileOutputStream = new FileOutputStream(filePath), true;

4. 文件拷贝 ———— input output 结合

1
利用前文讲的 fileInputStream 和 fileOutputStream 进行文件拷贝。
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
package IOStream;  

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
// write(byte[] b) 方法
public class FileCopy {
public static void main(String[] args) {
copyFile();
}

public static void copyFile() {
String srcFilename = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new2.txt";
String desFilename = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new3.txt";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(srcFilename);
fileOutputStream = new FileOutputStream(desFilename);
System.out.println("successful copy");
byte[] cache = new byte[1024];
int readLen = 0;
while((readLen = fileInputStream.read(cache)) != -1){
fileOutputStream.write(cache, 0, readLen);
}
} catch (IOException e){
e.printStackTrace();
} finally {
try {
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}

image-20240729152210191

5. FileReader

1
2
3
public class FileReader extends InputStreamReader
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。但是如果读取的内容包含中文那么就要尝试采用FileReader
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
package IOStream;  

import java.io.FileReader;
import java.io.IOException;

// 读取文件的字符流
public class FileReaderPrint {
public static void main(String[] args) {
// System.setProperty("file.encoding", "UTF-8");
readFile();
}
public static void readFile(){
String filePath = "C:/Users/86182/Desktop/JavaStudy/IOStream/CreateFile/new3.txt";
FileReader fileReader = null;
try {
fileReader = new FileReader(filePath);
int readLen = 0;
char[] cache = new char[1024];
while ((readLen = fileReader.read(cache))!=-1){
System.out.println(new String(cache, 0, readLen));
}
} catch (IOException e){
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}

由于我用的是vscode输出的虽然是中文但是也是乱码我就在环境变量设置了

1
2
JAVA_TOOL_OPTIONS
-Dfile.encoding=UTF-8

或者在主函数加上

1
System.setProperty("file.encoding", "UTF-8");

都可以解决问题

image-20240729160005251