定义:存储和读出数据的解决方案
作用:用于读写文件中的数据
分类:
①按照方向划分:
输入输出的方向是相对于程序来说的
输入流:将本地文件中的数据读取到程序中。本地文件——>程序
输出流(写入):将程序中的数据写入到本地文件中。程序——>本地文件
②操作的文件类型
字节流:InputStream、OutputStream。可以操作所有类型的文件。
字符流:Write、Reader。只可以操作纯文本文件。
补充:纯文本文件就是可以使用Windows系统自带的记事本打开且能读懂的文件。比如.txt、.xml、.lrc
字节流
FileOutputStream文件字节输出流
作用:可以把程序中的数据写到本地文件上,是字节流的基本流
使用步骤:
①创建字节输出流对象——在程序和文件之间创建一条数据传输的通道
细节1:参数可以是字符串表示的路径,也可以是File对象
细节2:如果操作的文件不存在,则会创建一个新文件,但是要保证父路径存在
细节3:如果文件已经存在,则会清空文件
②写数据
细节1:write方法的参数是整数,但实际上写到本地文件中的数据是整数在ASCII上对应的字符
③释放资源——关闭数据传输的通道
细节1:每次使用完流都要释放资源
public class ByteStreamDemo02 {
public static void main(String[] args) throws IOException {
/**
* 字节输出流FileOutputStream
*
* 需求:写出一段文字到本地文件中
*
* 步骤:
* 1.创建对象
* 2.写出数据
* 3.释放资源
*/
//1.创建对象
//让程序和文件之间产生一个数据传输的通道
FileOutputStream fos = new FileOutputStream("IO流\\a.txt");
//2.写出数据
fos.write(97);//a
//3.释放资源
//将数据传输的通道关闭掉
fos.close();
}
}
public class ByteStreamDemo03 {
public static void main(String[] args) throws IOException {
/**
* void write(int b) 一次写一个数据
* void write(byte[] b) 一次写一个字节组数据
* void write(byte[] b,int off,int len) 一次写一个字节组的部分数据
* 参数1:
* 数组
* 参数2:
* 起始索引
* 参数3:
* 个数
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("IO流\\a.txt");
//2.写数据
// fos.write(97);//a
// fos.write(98);//b
byte[] bytes = {97,98,99,100,101};
// fos.write(bytes);//abcde
fos.write(bytes,0,3);//abc
//3.释放资源
fos.close();
}
}
方法:
void write(int b) 一次写一个数据
void write(byte[] b) 一次写一个字节组数据
void write(byte[] b,int off,int len) 一次写一个字节组的部分数据
public class ByteStreamDemo04 {
public static void main(String[] args) throws IOException {
/**
* 换行写
* 写个换行符即可
* Windows系统:\r\n 回车换行符
* Linux系统: \n
* Mac系统: \r
*
* 细节: 在Windows操作系统当中,java对回车换行进行了简化。可以只写\n或者\r,java底层会自动换行
*
* 续写
* 如果想要续写,打开续写开关就行了
* 开关位置:创建对象的第二个参数
* 默认为false,表示关闭续写,此时创建对象会清空文件
* 手动传递true,表示打开续写,此时创建对象不会清空文件
*
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("IO流\\a.txt",true);
//2.写数据
String str = "kankedelaoyezuishuai";
byte[] bytes = str.getBytes();
fos.write(bytes);
String str1 = "666";
String wrap = "\r\n"; //换行符
byte[] wrapBytes = wrap.getBytes();
fos.write(wrapBytes);
byte[] bytes1 = str1.getBytes();
fos.write(bytes1);
//3.释放资源
fos.close();
}
}
FileInputStream字节输入流
作用:操作本地文件,可以把本地文件中的数据读取到程序中来。
步骤:
①创建字节输入流对象
细节1:如果文件不存在,则直接报错
②读取数据
细节1:使用read方法读取数据时,一次只读取一个字节,读出来的是数据在ASCII上对应的数字
细节2:多次读,如果读到文件末尾了,read方法返回-1
③释放资源
public class ByteStreamDemo01 {
public static void main(String[] args) throws IOException {
/**
* 演示字节输入流FileInputStream
*
* 需求:读取文件中的数据
*/
//1.创建对象
//a.txt的内容abcd
FileInputStream fis = new FileInputStream("IO流\\a.txt");
int b1 = fis.read();//97
int b2 = fis.read();//98
int b3 = fis.read();//99
int b4 = fis.read();//100
int b5 = fis.read();//100
System.out.println(b1);
System.out.println((char)b1);
System.out.println((char)b2);
System.out.println((char)b3);
System.out.println((char)b4);
System.out.println(b5);//-1
fis.close();
}
}
当文件中的数据很多的时候,一个一个读很费时,可以采取循环读取。
public class ByteStreamDemo02 {
public static void main(String[] args) throws IOException {
/**
* 演示字节输入流FileInputStream
*
* 需求:循环读取文件中的数据
*/
//1.创建对象
//a.txt的内容abcd
FileInputStream fis = new FileInputStream("IO流\\a.txt");
//2.循环读取数据
int b;
while ((b = fis.read()) != -1){
System.out.println((char)b);
}
//3.释放资源
fis.close();
}
}
常用方法:
public int read() | 一次读取一个字节的数据 |
public int read(byte[] buffer) | 一次读一个字节数组的数据 |
注意1:一次读取一个字节数组的数据,每次读取会尽可能把数组装满。
注意2:一般创建数组的长度是1024的整数倍。eg:1024*1024*5
public class ByteStreamDemo03 {
public static void main(String[] args) throws IOException {
/**
* public int read(byte[] buffer) 一次读取一个字节数组的数据
*
* 需求:一次读取两个字节
*
*/
//1.创建对象
FileInputStream fis = new FileInputStream("IO流\\a.txt");
//2.读取数据
byte[] bytes = new byte[2];
//一次读取多少个字节的数据,和数组的长度有关
//返回值表示读取到了多少个字节的数据
//读取到的数据存储到数组当中
int len = fis.read(bytes);
System.out.println(len);
String str = new String(bytes,0,len);
System.out.println(str);
int len1 = fis.read(bytes);
System.out.println(len1);
String str1 = new String(bytes,0,len1);
System.out.println(str1);
int len2 = fis.read(bytes);
System.out.println(len2);
String str2 = new String(bytes,0,len2);
System.out.println(str2);
//3.释放资源
fis.close();
}
}
产生乱码的原因:
编码和解码的方式不一样
如何产生乱码?
①读取数据时未读完整个汉字
②编码解码时使用同一个码表,同一个编码方式
字符流
字符流的底层其实就是字节流
字符流=字节流+字符集
特点:
输入流:一次读取一个字节,遇到中文时,一次读取多个字节
字符输入流
FileReader操作本地文件的字符输入流
步骤:
①创建字符输入流对象
public FileReader(File file)
public FileReader(String pathname)
②读取数据
public int read() | 读取数据,读到末尾返回-1 |
public int read(char[] buffer) | 读取多个数据,读取到末尾返回-1 |
细节1:按照字节进行读取,遇到中文,一次读取多个字节,读取后解码,返回一个整数
细节2:读到文件末尾了,read方法返回-1
细节3:在使用有参的read方法读取数据时,是将读数据、解码、强转三步合一了。把强转后的数据存到数组当中
③释放资源
字符输出流
FileWriter操作本地文件的字符输出流
步骤:
①创建字符输出流对象
public FileWriter(File file) | 创建字符输出流关联本地文件 |
public FileWriter(String pathname) | 创建字符输出流关联本地文件 |
public FileWriter(File file,boolean append) | 创建字符输出流关联本地文件,续写 |
public FileWriter(String pathname,boolean append) | 创建字符输出流关联本地文件,续写 |
②写数据
void write(int c) | 写出一个字符 |
void write(String str) | 写出一个字符 |
void write(String str,int off,int len) | 写出一个字符串的一部分 |
void write(char[] cbuf) | 写出一个字符数组 |
void write(char[] cbuf,int off,int len) | 写出一个字符数组的一部分 |
细节:如果write方法的参数是整数,但是实际写到本地文件中的是整数在字符集上对应的字符
③释放资源
缓冲流
可以提高读写的效率
字节缓冲流
原理:底层自带了长度为8192的缓冲区提高性能
BufferedInputStream 字节缓冲输入流
BufferedOutputStream 字节缓冲输入流
步骤:
①创建对象
public BufferedInputStream(InputStream is)
public BufferedOutputStream(OutputStream os)
②读写数据
③释放资源
字符缓冲流
BufferedReader
BufferedWriter
步骤:
①创建字符缓冲流对象
public BufferedReader(Reader r)
public BufferedWriter(Writer w)
②读写数据
特有方法
字符缓冲输入流 | public String readLine() | 读取一行数据,如果没有数据可读了,会返回null |
字符缓冲输出流 | public void newLine() | 写一行,会根据操作系统自动更换换行符 |
转换流
是字节流和字符流之间的桥梁
InputStreamReader
public InputStream(File file,String charsetName)
OutputStreamReader
public OutputStream(File file,String charsetName)
ObjectOutputStream序列化流
作用:可以吧Java中的对象写到本地文件中
public ObjectOutputStream(OutputStream out):把基本流包装成高级流
public final void writeObject(Object obj):把对象序列化到文件中去
注意:要想序列化对象的前提,该对象的类必须实现Serializable接口
ObjectInputStream反序列化流
将序列化到文件中的对象,读取到程序中来
public ObjectInputStream(InputStream out):把基本流包装成高级流
public final void readObject(Object obj):把序列化到本地文件中的对象,读取到程序中
细节1:在序列化流将对象写到文件中时,需要让JavaBean类实现Serialzable接口。否则会出现NotSerialzableException异常。
细节2:序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来
细节3:序列化对象后,修改了JavaBean类,再次反序列化,则会报错。会抛出InvalidClassException异常。
解决方案:给JavaBean类添加serialVerSionUID(序列号、版本号)
细节4:如果一个对象中的某个成员变量的值不想被序列化,可以给该成员变量加transient关键字修饰(瞬态关键字),该关键字标记的成员变量不参与序列化过程
打印流
PrintStream、PrintWriter
字节打印流
字符打印流
总结:
①打印流不操作数据源,只操作目的地。即只能读不能写
②字节打印流:默认自动刷新,特有的println自动换行
③字符打印流:自动刷新需要开启,特有的prinln自动换行
解压缩流
压缩包里的每一个文件在java中都是一个ZipEntry对象
解压
public class ZipStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建一个File表示要解压的压缩包
File src = new File("F:\\1heima-test\\ddd.zip");
//创建一个文件夹表示解压的目的地
File dest = new File("F:\\1heima-test\\dest");
unZip(src,dest);
}
public static void unZip(File src,File dest) throws IOException {
//解压的本质就是把压缩包中的每个文件读取出来,然后按照层级拷贝到目的地
//创建一个解压流 读取压缩包中的数据
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
//获取压缩包中的每个文件 ZipEntry
ZipEntry zipEntry;
while ((zipEntry = zip.getNextEntry())!=null){
System.out.println(zipEntry);
//判断获取到数据是文件还是文件夹
if (zipEntry.isDirectory()){
//是文件夹的话,在目的地dest创建一个同样的文件夹
//dest为父路径,zipEntry名字为子路径
File file = new File(dest,zipEntry.toString());
file.mkdirs();
}else {
//是文件的话。需要读取文件,并把它存放到目的地dest文件夹中
FileOutputStream fos = new FileOutputStream(new File(dest,zipEntry.toString()));
int b;//按字节读取
while ((b = zip.read())!=-1){
fos.write(b);
}
fos.close();
//表示在压缩包中的一个文件处理完毕了
zip.closeEntry();
}
}
zip.close();
}
}
压缩单个文件
public class ZipStreamDemo02 {
public static void main(String[] args) throws IOException {
/**
* 压缩单个文件
*/
//1.创建File对象表示要压缩的文件
File src = new File("F:\\1heima-test\\a.txt");
//2.要压缩到哪里
File dest = new File("F:\\1heima-test");
//3.调用方法
toZip(src,dest);
}
public static void toZip(File src,File dest) throws IOException {
//创建压缩流对象
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
//创建ZipEntry对象,表示压缩包中的每个文件和文件夹
ZipEntry zipEntry = new ZipEntry("a.txt");
//把ZipEntry对象放到压缩包中
zos.putNextEntry(zipEntry);
//把src文件中的数据写到压缩包中
FileInputStream fis = new FileInputStream(src);
int b;
while ((b = fis.read())!=-1){
zos.write(b);
}
zos.closeEntry();
zos.close();
}
}
压缩多个文件
public class ZipStreamDemo03 {
public static void main(String[] args) throws IOException {
/**
* 压缩文件夹
*/
//创建文件 表示要压缩的文件夹
File src = new File("F:\\1heima-test\\aaa");
//创建文件 表示要压缩到哪里
File destParent = src.getParentFile();
//创建文件 表示压缩包的路径
File dest = new File(destParent,src.getName()+".zip");
//创建压缩流关联压缩包
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
//获取src里面的每一个文件,变成ZipEntry对象,放到压缩包中
toZip(src,zos,src.getName());
//释放资源
zos.close();
}
/**
*
* @param src 数据源
* @param zos 压缩流
* @param name 压缩包内部路径
* @throws FileNotFoundException
*/
public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {
//获取被压缩文件中的所有文件和文件夹
File[] files = src.listFiles();
for (File file : files) {
if (file.isFile()){
//是文件 变成ZipEntry对象,放到压缩包中
ZipEntry zipEntry = new ZipEntry(name +"\\"+file.getName());
//放到压缩包中
zos.putNextEntry(zipEntry);
//将数据源中的数据写入
FileInputStream fis = new FileInputStream(file);
int b;
while ((b = fis.read())!= -1){
zos.write(b);
}
fis.close();
zos.closeEntry();
}else {
//是文件夹的话 递归
toZip(file,zos,name+"\\"+file.getName());
}
}
}
}
Commons-io工具包
下载地址: Apache Commons – Apache Commons
Hutool工具包
Hutool — 🍬A set of tools that keep Java sweet.