java安全-io流
java安全-IO流
io流用来在 Java 程序和“外部世界”之间传输数据
java项目中的相对路径
Java 中的相对路径,默认是相对于「程序的工作目录(Working Directory)」
在 IDEA 里,默认就是「项目根目录」。
一、文件相关操作
1.创建file对象
本质上是三种构造方式
根据完整路径字符串创建
1
2
3
4
5
6// 直接传递完整路径
File f1 = new File("D:\\test\\java.txt");
// 或者使用相对路径(相对于项目根目录)
File f2 = new File("src/main/resources/config.xml");
System.out.println(f1.getAbsolutePath());根据“父路径字符串”和“子路径字符串”创建
1
2
3
4
5
6
7
8String parentPath = "D:\\test";
String fileName = "java.txt";
// 父路径字符串 + 子路径字符串
// 系统会自动补全中间的分隔符(Windows是\, Linux是/)
File f3 = new File(parentPath, fileName);
System.out.println(f3.getPath()); // 输出 D:\test\java.txt根据“父 File 对象”和“子路径字符串”创建
1
2
3
4
5
6
7
8// 先创建父级目录对象
File parentDir = new File("D:\\test");
String fileName = "java.txt";
// 父File对象 + 子路径字符串
File f4 = new File(parentDir, fileName);
System.out.println(f4.getPath());
boolean created = file.createNewFile();通过调用file对象的这个方法可以创建文件
2.获取file对象信息
获取路径与名称
获取文件状态
获取文件属性

3.目录与文件操作
| 操作 | 方法名 | 返回值 | 关键注意点 |
|---|---|---|---|
| 文件/目录删除 | delete() | boolean | 不走回收站,直接抹除;删除目录时,目录必须为空。 |
| 创建单级目录 | mkdir() | boolean | 只有父目录存在时才能创建成功。 |
| 创建多级目录 | mkdirs() | boolean | 最推荐。如果父目录不存在,会自动补全所有父目录。 |
1 | // 场景 A:创建单级目录 (mkdir) |
二、IO流操作
在 Java 中,数据传输像水流一样。
- 输入(Input):数据从“外部”(硬盘、网络、键盘)流向“程序内存”。(读)
- 输出(Output):数据从“程序内存”流向“外部”。(写)
- 特点:流是有方向的,而且通常是顺序的(先读的先到)。
架构如下:

1.字节输入流(InputStream)
定义:它是所有字节输入流的超类(父类),是一个抽象类。它以**字节(Byte,8位)**为单位读取数据。
常用子类:FileInputStream
核心方法:
int read(): 一次读一个字节。如果读到文件末尾,返回 -1。int read(byte[] b): 一次读一个字节数组(效率高)。返回读取到的有效字节个数。
1 | // 目标:使用字节流读取文件内容 |
2.字节输出流(OutputStream)
定义:它是所有字节输出流的超类。以字节为单位写出数据。
核心场景:复制文件、下载文件、写入二进制数据。
常用子类:FileOutputStream
核心方法:
void write(int b): 写出一个字节。void write(byte[] b): 写出一个字节数组。void write(byte[] b, int off, int len): 写出数组的一部分。
注意点:
- 覆盖与追加:
new FileOutputStream("a.txt")默认会清空文件重新写;new FileOutputStream("a.txt", true)则是追加模式。
代码示例:
1 | // 目标:向文件写入 "abc" |
3.字符输入流(Reader)
定义:它是所有字符输入流的父类。以**字符(Char)**为单位读取数据。
为什么要有字符流?
- 一个汉字在 UTF-8 编码下占 3 个字节。
- 如果用字节流读,一次读一截,汉字会被拆碎,导致乱码。
- 字符流会自动处理编码,一次读一个完整的字符(无论它是英文还是中文)。
常用子类:FileReader
核心方法:
int read(): 一次读一个字符。int read(char[] cbuf): 一次读一个字符数组。
FileReader fr = new FileReader("chinese.txt",true) 追加操作
代码示例:
1 | try (FileReader fr = new FileReader("chinese.txt")) { |
4.字符输出流(Writer)
定义:它是所有字符输出流的父类。
常用子类:FileWriter
核心方法:
void write(int c): 写出一个字符。void write(String str): 直接写出字符串(非常方便,字节流做不到)。void write(char[] cbuf): 写出字符数组。
重要特性(缓冲区):
Writer 写出数据时,通常是先写到内存的一个缓冲区中,并不会立刻落入硬盘。如果不关闭流或者不刷新,数据可能会丢失!
1 | try (FileWriter fw = new FileWriter("text.txt")) { |
5.流的知识点
将字节转化为字符
调用 new String(byte数组) 时,Java 会拿着这些数字,去查一本“字典”(编码表,通常是 UTF-8)。
- Java 看到数字 97 查表 →→发现是字符 ‘a’。
- Java 看到连续三个数字 -28, -67, -96 →→查表发现这三个凑在一起是汉字 ‘你’。
new String(buffer, 0, len)这个可以限定字符读取个数,从而避免严重的 Bug(脏数据问题)。在 FileReader(字符流)中:
- 我们用
char[] buffer作为一个**“铲子”**。 read(buffer):用铲子从文件里铲出一堆字符(珠子)。new String(buffer, 0, len):把铲子里的珠子加工成项链(字符串),方便程序使用。
- 我们用
流的刷新
缓冲机制:为了提高效率,字符流和部分缓冲流(Buffered…)配备了内存缓冲区。写入的数据会先暂存在内存里,攒够了一波再写给硬盘。
问题:如果程序突然结束,缓冲区里还剩一点数据没攒够,这些数据就不会写入硬盘,导致丢数据。
方法:
flush()强制把缓冲区的数据“推”到硬盘里。流的关闭
资源占用:Java 程序通过操作系统来读写文件。流如果不关闭,该文件就会一直被 Java 程序占用(类似于你打开一个 Word 文档不关,别人就删不掉它)。
内存泄露:长期的资源不释放会导致内存泄露。
方法:
close(),他的底层也调用了flush()注意:

目前流关闭与刷新的最佳实践
try-with-resources代码块实现1
2
3
4
5
6
7// 语法格式
try (创建流对象1; 创建流对象2) {
// 读写操作
} catch (IOException e) {
// 异常处理
}
// 只要走出这个 try 块(无论是正常结束还是报错),括号里的流都会自动执行 close()。
6.缓冲流
分类
缓冲流属于处理流(包装流),它自己不能直接连文件,必须包裹在现有的节点流(如 FileInputStream)之上。

字节输出与输入流利用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23File srcFile = new File("video.mp4");
File destFile = new File("video_copy.mp4");
// 使用 try-with-resources 语法,自动关闭流
try (
// 1. 创建基本流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
// 2. 创建缓冲流,把基本流"包"起来
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
) {
// 3. 读写操作
int len;
byte[] bytes = new byte[1024]; // 这里还是可以用一个字节数组作为中间桥梁
// 从 bis 读,虽然看起来和普通流一样,但其实它底层已经在帮你批量预读了
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
System.out.println("复制完成!");
字符缓冲流 (用于处理文本)
日常开发处理文本(txt, json, config等)最常用的方式,因为它们有两个超级好用的特有方法。
- BufferedReader 特有方法:readLine()
- 一次读取一行文本。
- 读不到时返回 null(而不是 -1)。
- BufferedWriter 特有方法:newLine()
- 自动写入一个换行符。
- 好处:它是跨平台的(Windows是 \r\n,Linux是 \n),它会自动根据系统识别。
缓存流:内置了缓冲区(8KB),极大减少了读写硬盘的次数,速度比普通流快很多

但是非缓冲流也可以将数组写很大以达到缓冲流的效果
7.其他高级流
转换流
核心组件:
InputStreamReader:字节流通向字符流的桥梁。OutputStreamWriter:字符流通向字节流的桥梁。
InputStreamReader isr = new InputStreamReader(fis, "GBK");就是将普通字节流包装,并将其以规定编码方式读取!
打印流
核心组件:
PrintStream(字节打印流):System.out 就是这个类型。PrintWriter(字符打印流):Web 开发中常用。
1 | PrintWriter pw = new PrintWriter("print.txt"); |
普通流写整数 97,会写出字符 ‘a’(因为 97 是 ‘a’ 的 ASCII 码)。但打印流写 97,文件里就是 “97”。它提供了 print() 和 println() 方法
数据流
核心组件:
DataInputStreamDataOutputStream
把一个 int 类型的数字、一个 double 类型的浮点数、或者一个 boolean 值写入文件,并且以后读出来的时候,还能直接被识别为 int, double, boolean
1 | // 写 |