java笔记 - IO流 1 - 基本流(重点)
2025-04-01 本文已影响0人
阿巳交不起水电费
image.png
Java IO流是指数据在设备间的传输过程,包括从设备读取数据到内存(输入流)和将数据从内存写入设备(输出流)
IO流的分类:
image.png
按照数据的流向
- 输入流:读数据
- 输出流:写数据
按照数据类型来分
- 字节流
字节输入流;字节输出流;
- 字符流
字符输入流;字符输出流;
特别注意:是“程序”在读取数据,写入数据。
一般来说,我们说IO流是按照数据类型来分的。如果数据可以通过windows自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流(txt文件、md文件、xml文件、lrc文件等),否则使用字节流,如果不清楚用哪种类型的流,就使用字节流。
image.png
一、字节流
1.字节输出流 - FileOutputStream
将程序里面的数据输出到本地文件中(也就是给文件写数据)。
image.png
image.png
package com.IO流;
import java.io.FileOutputStream;
import java.io.IOException;
public class 字节输出流 {
public static void main(String[] args) throws IOException {
baseDemo1();
}
// FileOutputStream - 字节输出流基础使用
private static void baseDemo1() throws IOException {
// 如果想要续写,构造方法中传递文件路径,第二个参数传递true。默认是false,代表清空文件内容重新写入
boolean append = true;
FileOutputStream fos = new FileOutputStream("idea_test\\aaaa.txt", append);
// 注意是ASCII码值,不是字符
// FileOutputStream 写数据的3种方式
// 1.写入单个字节
fos.write(97); // a
fos.write(98); // b
fos.write(99); // b
fos.write(100); // b
/*
换行符:
windows: \r\n
linux: \n
mac: \r
*/
fos.write("\r\n".getBytes()); // 换行符
// 2.写入多个字节 - 也就是字节数组
byte[] bytes1 = {97, 98, 99, 100};
fos.write(bytes1);// abcd
// 3.写入字节数组的某一段
byte[] bytes2 = "abcde".getBytes();
// 开始索引,结束索引,左闭右开
fos.write(bytes2, 1, 2); // bc
// 每次使用完流后都要关闭,不然会占用资源,导致文件无法写入,删除等操作
fos.close();
}
}
2.字节输入流-FileInputStream
操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来。
image.png
package com.IO流;
import java.io.*;
public class 字节输入流 {
public static void main(String[] args) throws IOException {
readFileDemo("idea_test\\aaaa.txt");
// 文件复制 - demo1
copyFileDemo1();
}
// 文件读取 - FileInputStream
private static void readFileDemo(String name) throws IOException {
FileInputStream fis = new FileInputStream(name);
// 循环读取文件内容,直到文件末尾
// 一次读取一个字节,返回的是读取到的字节值,如果到达文件末尾,返回-1
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
// 释放资源
fis.close();
}
// 文件复制 - FileInputStream
private static void copyFileDemo1() throws IOException {
FileInputStream fis = new FileInputStream("idea_test\\aaaa.txt");
FileOutputStream fos = new FileOutputStream("idea_test\\aaaaCopy.txt");
// 拷贝文件 - 边读边写
byte[] bytes = new byte[1024 * 1024 * 5]; // 优化 - 一次读5M
int len;
while ((len = fis.read(bytes)) != -1) {
// 注意这里是写的读的长度,因为读取的时候是尽可能的读满数组的,如果读不满,就会返回实际读取的长度,这样后面的不会被覆盖
fos.write(bytes, 0, len);
}
// 释放资源
fos.close(); // 一般是最先定义的对象最后关闭
fis.close();
}
}
二、字符流(注意:字符流有缓冲区!)
为什么会出现字符流?
由于字节流操作中文不是很方便,所以java就提供字符流。字符流 = 字节流+编码表。
如何识别字节是中文?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。那么在可以区分是否是中文后就可以根据编码表读取对应的字节个数来解码成一个汉字,如:GBK两个字节代表一个中文,UTF-8三个字节代表一个中文。
image.png
image.png
1.字符输出流-FileWriter
image.png
image.png
package com.IO流;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
public class 输出流 {
public static void main(String[] args) throws IOException {
//baseDemo1();
baseDemo2();
}
// FileWriter - 字符输出流基础使用
private static void baseDemo2() throws IOException {
// 如果想要续写,构造方法中传递文件路径,第二个参数传递true。默认是false,代表清空文件内容重新写入
boolean append = true;
FileWriter fw = new FileWriter("idea_test\\aaaa.txt", append);
// FileWriter 写数据的3种方式
// 1.写入单个字符
fw.write(97); // a
fw.write(98); // b
fw.write(99); // b
fw.write(100); // b
// 2.写入多个字符 - 也就是字符数组
char[] chars = {'你','好',','};
fw.write(chars);
// 3.写入字符串 - 这种方式最常用
fw.write("点个赞吧!");
fw.close();
}
// FileOutputStream - 字节输出流基础使用
private static void baseDemo1() throws IOException {
// 如果想要续写,构造方法中传递文件路径,第二个参数传递true。默认是false,代表清空文件内容重新写入
boolean append = true;
FileOutputStream fos = new FileOutputStream("idea_test\\aaaa.txt", append);
// 注意是ASCII码值,不是字符
// FileOutputStream 写数据的3种方式
// 1.写入单个字节
fos.write(97); // a
fos.write(98); // b
fos.write(99); // b
fos.write(100); // b
/*
换行符:
windows: \r\n
linux: \n
mac: \r
*/
fos.write("\r\n".getBytes()); // 换行符
// 2.写入多个字节 - 也就是字节数组
byte[] bytes1 = {97, 98, 99, 100};
fos.write(bytes1);// abcd
// 3.写入字节数组的某一段
byte[] bytes2 = "abcde".getBytes();
// 开始索引,结束索引,左闭右开
fos.write(bytes2, 1, 2); // bc
// 每次使用完流后都要关闭,不然会占用资源,导致文件无法写入,删除等操作
fos.close();
}
}
image.png
image.png
2.字符输入流-FileReader
image.png
注意:字符流底层是字节流,默认是一个字节一个字节的读取,但是如果遇到中文就会一次读取多个字节,如:GBK一次读两个字节,UTF-8一次读三个字节。读取后会解码并转为十进制返回,想看到中文汉字还得进行强转。
package com.IO流;
import java.io.*;
public class 输入流 {
public static void main(String[] args) throws IOException {
//readFileDemo("idea_test\\aaaa.txt");
readFileDemo2("idea_test\\aaaa.txt");
// 文件复制 - demo1
//copyFileDemo1();
}
// FileReader - 字符输入流基础使用
private static void readFileDemo2(String s) throws IOException {
FileReader fr = new FileReader(s);
// 方式一:read() 无参数
//{
// int c;
// while ((c = fr.read())!= -1) {
// System.out.print((char) c);
// }
// fr.close();
//}
// 方式二:read(char[] cbuf) 有参数 - 一次读一个字符数组【注意:这里不用再强转了,已经是char类型的】
{
char[] chars = new char[2];
int len;
while ((len = fr.read(chars))!= -1) {
System.out.print(new String(chars, 0, len));
}
fr.close();
}
}
// 文件读取 - FileInputStream
private static void readFileDemo(String name) throws IOException {
FileInputStream fis = new FileInputStream(name);
// 循环读取文件内容,直到文件末尾
// 一次读取一个字节,返回的是读取到的字节值,如果到达文件末尾,返回-1
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
// 释放资源
fis.close();
}
// 文件复制 - FileInputStream
private static void copyFileDemo1() throws IOException {
FileInputStream fis = new FileInputStream("idea_test\\aaaa.txt");
FileOutputStream fos = new FileOutputStream("idea_test\\aaaaCopy.txt");
// 拷贝文件 - 边读边写
byte[] bytes = new byte[1024 * 1024 * 5]; // 优化 - 一次读5M
int len;
while ((len = fis.read(bytes)) != -1) {
// 注意这里是写的读的长度,因为读取的时候是尽可能的读满数组的,如果读不满,就会返回实际读取的长度,这样后面的不会被覆盖
fos.write(bytes, 0, len);
}
// 释放资源
fos.close(); // 一般是最先定义的对象最后关闭
fis.close();
}
}
image.png
三、扩展
3.异常处理(了解)
这里只作为扩展知识,实际开发基本不会再这样写了。后面直接抛出异常即可,因为spring框架会统一处理异常。
image.png
image.png
4.编码与解码
image.png
如何不产生乱码?
- 1.不要使用字节流读取文本文件
- 2.编码和解码的格式要一致,使用同一个码表同一个编码方式
package com.IO流;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class 编码与解码 {
public static void main(String[] args) throws UnsupportedEncodingException {
// 注意:编码和解码的格式要一致,否则会出现乱码
// 编码:
// 编码:字符串 --> 字节数组 - 默认是utf-8
byte[] bytes1 = "java 你好".getBytes();
System.out.println(Arrays.toString(bytes1));
// 编码:字符串 --> 字节数组 - 指定编码格式
byte[] bytes2 = "java 你好".getBytes("GBK");
System.out.println(Arrays.toString(bytes2));
// 解码:
// 解码:字节数组 --> 字符串 - 默认是utf-8
System.out.println(new String(bytes1));
System.out.println(new String(bytes2, "GBK"));
}
}
案例:
package com.IO流;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class 案例练习 {
public static void main(String[] args) throws IOException {
// 文件拷贝 - demo1
{
// 先删了,防止重复拷贝
File descFile = new File("idea_test\\文件拷贝练习-副本");
if (descFile.exists()) {
descFile.delete();
}
copyDir(new File("idea_test\\文件拷贝练习"), descFile);
}
// 文件加密解密 - demo2
{
// 给图片加密
fileEnDe("idea_test\\img.png", "idea_test\\img加密.png");
// 给图片解密
fileEnDe("idea_test\\img加密.png", "idea_test\\img解密.png");
}
}
// 案例:文件夹的拷贝 - 递归实现 - 【需要考虑子文件夹】
private static void copyDir(File src, File desc) throws IOException {
// 不用判断是否存在,因为下面的mkdirs()会自动创建
desc.mkdirs();
// 递归
// 1.进入数据源目录
File[] files = src.listFiles();
if(files == null) {
return;
}
// 2.遍历目录
for (File file : files) {
// 3.判断是文件还是目录
if (file.isFile()) {
// 是文件则直接拷贝
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(desc, file.getName()));
// 拷贝文件 - 边读边写
byte[] bytes = new byte[1024 * 1024 * 5]; // 优化 - 一次读5M
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
} else {
// 递归调用
copyDir(file, new File(desc, file.getName()));
}
}
}
/**
* 案例 - 文件的加密解密
* 借助 ^ 异或运算符实现
* 原理:
* 1. 任何数据和某个数字异或两次会得到原始数据本身
* 2. 异或运算满足交换律和结合律
* 3. 加密和解密使用的密钥是相同的
* @param src 需要加密或者解密的文件路径
* @param desc 加密或者解密后文件的存放的路径
* @throws IOException
*/
private static void fileEnDe(String src, String desc) throws IOException {
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(desc);
// 加密或者解密处理
int len;
while ((len = fis.read()) != -1) {
// 每个字节进行异或运算
fos.write(len ^ 5);
}
// 释放资源
fos.close();
fis.close();
}
}
参考:黑马java教程
image.png
image.png
image.png