一、概述
IO流:存储和读取数据的解决方案,用于读写文件中的数据
I:input
O:output
流:像水流一样传输数据
IO流分类:
按流的方向:
输出流:程序 →文件
输入流:文件→程序
按操作文件类型:
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件
纯文本文件:用windows系统自带的记事本打开并且能读懂的文件:.txt .md .xml .lrc等
二、IO流体系
字节流:
FileOutputStream:
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
步骤:
①创建字节输出流对象
②写数据
③释放资源
示例代码:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//写一段文字到本地文件中 (不写中文)
//准备好写入的本地文件 a.txt
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
file.createNewFile();
//创建对象 (建立程序与文件的通道)
FileOutputStream fos=new FileOutputStream(file);
//写出数据 (传输数据)
fos.write(97);
//释放资源 (关闭这个通道)
fos.close();
}
}
FileOutputStream写数据的3种方式 :
示例代码:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//写一段文字到本地文件中 (不写中文)
//准备好写入的本地文件 a.txt
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
FileOutputStream fos=new FileOutputStream(file);
//写出数据方式一
fos.write(97); //a
fos.write(97); //a
//方式二 传递字节数组
byte arr[]=new byte[]{98,98,98}; // bbb
fos.write(arr);
//方式三 传递字节数组中的部分数据
// 参数1:数组 参数2:起始位置 参数3:传递的长度
byte arr2[]=new byte[]{99,100,101,102,103}; //c d e f g
fos.write(arr2,0,3);
//释放资源
fos.close();
}
}
FileOutputStream写数据的2个问题 :
①换行
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
//换行操作
//windows系统: \r\n
//Linux: \n
//Mac: \r
//winods系统中JAVA对回车换行进行了优化 \r 或者 \n 都表示换行 (JAVA会在底层代码中补全命令)
//准备好写入的本地文件 a.txt
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
FileOutputStream fos=new FileOutputStream(file);
String str="哈哈真厉害";
//String类中的getBytes方法 : 获取字符串对应的字节数组
byte bytes[]=str.getBytes();
//写入数据1
fos.write(bytes);
//增加换行符
String wrap="\r\n";
byte bytes1[]=wrap.getBytes();
//换行操作
fos.write(bytes1);
String str2="确实";
byte bytes2[]=str2.getBytes();
//写入数据2
fos.write(bytes2);
//释放资源
fos.close();
}
}
②续写(不清空原始文件内容)
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
//准备好写入的本地文件 a.txt
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
//增加一个参数true 表示打开续写开关 默认不传递时是表示关闭续写
FileOutputStream fos=new FileOutputStream(file,true);
//续写
String str="这是续写";
byte[] arr=str.getBytes();
fos.write(arr);
//释放资源
fos.close();
}
}
、
FileInputStream:
操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来
步骤:
①创建字节输入流对象
②读取数据
③释放资源
示例代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//准备好本地文件
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
FileOutputStream fos=new FileOutputStream(file);
String str="abcde";
byte[] bytes=str.getBytes();
fos.write(bytes);
fos.close();
//创建对象
FileInputStream fis=new FileInputStream(file);
//读取内容
int b1 = fis.read();
System.out.println(b1); //97 a
int b2 = fis.read();
System.out.println(b2); //98 b
int b3 = fis.read();
System.out.println(b3); //99 c
int b4 = fis.read();
System.out.println(b4); //100 d
int b5 = fis.read();
System.out.println(b5); //101 e
int b6 = fis.read();
System.out.println(b6); //-1 (没有数据时默认为-1)
//释放资源
fis.close();
}
}
FileInputStream循环读取:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//准备好本地文件
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
//创建对象
FileInputStream fis=new FileInputStream(file);
//循环读取内容
int b;
while((b=fis.read())!=-1){
System.out.println((char)b);
}
//释放资源
fis.close();
}
}
文件拷贝:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//准备好本地文件
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
File file2=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\b.txt");
//创建对象
FileInputStream fis=new FileInputStream(file);
FileOutputStream fos=new FileOutputStream(file2);
//循环读取内容
//拷贝
int b;
while((b=fis.read())!=-1){
fos.write(b);
}
//释放资源
fis.close();
fos.close();
}
}
弊端:
单个字节进行读取,速度很慢
改进:
示例代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//准备好本地文件
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
//创建对象
FileInputStream fis=new FileInputStream(file);
//传递数组用于读取
//设定长度为 n 的字节数组用于读取数据
//read方法会按照传递参数中数组的大小进行读取
//返回值:本次读取了多少个数据
byte[] bytes=new byte[2];
int len = fis.read(bytes);
System.out.println(len); //2
System.out.println(new String(bytes)); //ab
int len1 = fis.read(bytes);
System.out.println(len1); //2
System.out.println(new String(bytes)); //cd
//第三次读取数据,只能读取到一个数据:e
//会覆盖上一次数组中读取的内容 但是第二个值d没有新的值进行覆盖
//因此数组输出为ed
int len2 = fis.read(bytes);
System.out.println(len2); //1
System.out.println(new String(bytes)); //ed
//修改方法: 使用另一种String构造方法(字节数组,起始位置,结束位置)
System.out.println(new String(bytes,0,len2)); //e
int len3 = fis.read(bytes);
//读不到数据 默认返回-1 且 没有任何数据覆盖数组中原有的数据
//因此返回ed
System.out.println(len3); //-1
System.out.println(new String(bytes)); //ed
//释放资源
fis.close();
}
}
文件拷贝(改进):
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//准备好本地文件
File file=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.txt");
File file2=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\b.txt");
//创建对象
FileInputStream fis=new FileInputStream(file);
FileOutputStream fos=new FileOutputStream(file2);
//数组拷贝
byte[] bytes=new byte[2];
int len;
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
//释放资源
fis.close();
fos.close();
}
}
字符流:
特点:
输入流:一次读一个字节,遇到中文,一次读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
FileReader:
步骤:
①创建字符输入流对象
文件不存在则直接报错
②读取数据
③释放资源
空参的Read方法:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
//创建对象 FileReader
File file=new File("c.txt"); //相对地址
FileReader fr=new FileReader(file);
//读取数据
//底层也是使用字节流进行读取,默认一次一个字节
//如果遇到中文则会一次读取多个,GBK一次读取两个 UTF-8一次读取三个
//读取之后方法底层还会进行解码,并转换成十进制
//最终将十进制的值作为返回值
//若想直接打印汉字,则需要对这些返回值进行强转
int ch;
while((ch=fr.read())!=-1){
System.out.print((char)ch);
}
//你好啊
//哈哈啊
//今天天气
//真不错
fr.close();
}
}
有参的Read方法:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
//创建对象 FileReader
File file=new File("c.txt"); //相对地址
FileReader fr=new FileReader(file);
char[] chars=new char[2];
int ch;
//有参的read(chars): 读取整数返回值,对返回值解码,强转三合一,把强转后的字符放进char数组中
while((ch=fr.read(chars))!=-1){
System.out.println(new String(chars,0,ch));
}
//你好
//啊 (\r)不显示
// (\n)表示换行
//哈
//哈啊
// (\r)
// (\n)
//今天
//天气
// (\r)
// (\n)
//真不
//错
fr.close();
}
}
FileWriter:
步骤:
①创建字符输出流对象
②写数据
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
File file=new File("c.txt"); //相对地址
//创建对象 FileWriter 开启续写开关
FileWriter fw=new FileWriter(file,true);
fw.write("\r\n你真厉害啊");
fw.close();
}
}
③释放资源
构造方法:
成员方法:
字符流原理:
字符流在读取时会有一个缓冲区(8192字节长度的数组)每次读取若缓冲区有数据可以读取,则直接读取缓冲区中的数据(英文读一个字节,中文读三个字节)直到缓冲区内没有数据了,则会从文件中重新读取数据到缓冲区中,每次读取都尽可能放满缓冲区,若文件中没有数据了,返回-1。
输出流:
输入流:
字符流在写入时,同样会先往缓冲区(长度为8192字节的数组)中写入,当满足特定的三种情况后,缓冲区中的数据才会写入到本地文件中:①缓冲区数据存满 ② 使用了flush方法 ③使用了close方法
三、字符集
计算机存储规则:任意计算机中的数据都是以二进制的形式来存储的。
乱码原因:
①读取数据时未读完整个汉字
②编码和解码方式不同意
JAVA中编码和解码的方式:
JAVA默认: UTF-8
import java.io.*;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws UnsupportedEncodingException {
//1、编码
String str="你好啊";
byte[] bytes = str.getBytes();//JAVA默认编码规则 : utf-8 一个中文3个字节
System.out.println(Arrays.toString(bytes)); //[-28, -67, -96, -27, -91, -67, -27, -107, -118]
byte[] bytes1 = str.getBytes("gbk"); //指定编码规则: gbk gbk中一个中文 2个字节
System.out.println(Arrays.toString(bytes1)); //[-60, -29, -70, -61, -80, -95]
//2、解码
String str1=new String(bytes); //默认解码规则 utf-8
System.out.println(str1);//你好啊
str1=new String(bytes,"gbk"); //指定gbk解码
System.out.println(str1); //浣犲ソ鍟� 出现乱码 编码解码规则不统一
String str2=new String(bytes1,"gbk");
System.out.println(str2); //你好啊
}
}
四、高级流
字节缓冲流:
字节缓冲流是对基本流的包装,底层代码还是用基本流进行操作
示例代码:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("a_copy.txt"));
int b;
while((b=bis.read())!=-1){
bos.write(b);
}
bis.close();
bos.close();
}
}
//一次读取多个字节
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("a_copy.txt"));
int b;
byte bytes[]=new byte[10];
while((b=bis.read(bytes))!=-1){
bos.write(bytes,0,b);
}
bis.close();
bos.close();
}
}
缓冲流提高效率原理:
基本流会读取数据(一次性读取8192字节的数据)放入缓冲区,填满后再使用变量b将左边缓冲区中的数据存入右边缓冲区中,当缓冲区满后,再用基本流存入硬盘,若无法从左边缓冲区获得新数据后,再由左边的缓冲区从数据源中提取新的数据。
字符缓冲流:
示例代码:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("a_copy.txt"));
//readline方法每次读取文本文件中的 一整行
//遇到回车换行会停止读取,但是不会将换行读取
//当没有数据时返回null
String line;
while((line=br.readLine())!=null){
bw.write(line); //一行一行写入
bw.newLine(); //手动换行
}
br.close();
bw.close();
}
}
字节缓冲流的缓冲区的单位为byte类型 长度是8k
字符缓冲流的缓冲区的单位为char类型 长度是16k
转换流:
是字符流和字节流之间的桥梁
import java.io.*;
import java.nio.charset.Charset;
public class Main {
public static void main(String[] args) throws IOException {
//指定字符编码规则的转化输入流和输出流
//将GBK编码的文档转化为UTF-8编码
InputStreamReader isr=new InputStreamReader(new FileInputStream("gbkfile.txt"),"GBK");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbkfile_utf.txt"),"UTF-8");
int b;
while((b=isr.read())!=-1){
osw.write(b);
}
System.out.println();
isr.close();
osw.close();
//方法二:
FileReader fr=new FileReader("gbkfile.txt", Charset.forName("GBK"));
FileWriter fw=new FileWriter("gbkfile_utf2.txt", Charset.forName("UTF-8"));
int a;
while((a=fr.read())!=-1){
fw.write(a);
}
fr.close();
fw.close();
}
}
序列化流/反序列化流(字节流的高级流):
序列化流:
可以把JAVA中的对象(对象类需要实现Serializable接口)写到本地文件中
示例代码:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
girl g1=new girl("张三",18);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("a.txt"));
//写出数据
oos.writeObject(g1);
oos.close();
}
}
Serializable接口:标记型接口,一旦实现该接口表示当前的Student类可以被序列化
反序列化流(对象操作输入流):
示例代码:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt"));
//返回值是Object类型,需要进行类型转换
girl g=(girl)ois.readObject();
System.out.println(g);//girl{name = 张三, age = 18}
}
}
Tips:
①序列化对象后,如果对象类成员发生改变,则会导致在反序列化时报错(原因,两者版本不匹配) 解决办法:在对象类中增加 serialVersionUID 属性表示版本号,版本号不变则不会出现不匹配的问题。
②如果不想将类中所有的成员变量序列化到本地文件中,则需要在不想要序列化的成员属性前增加一个瞬态关键字 :transient ,作用是不会把当前属性序列化到本地文件当中。
打印流:
PrintStream(字节打印流):
刷新:刷新缓冲区,让缓冲区内数据即刻传递
示例代码:
import java.io.*;
import java.nio.charset.Charset;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
PrintStream ps=new PrintStream(new FileOutputStream("a.txt"),true, Charset.forName("UTF-8"));
ps.println(97); //写出+自动刷新+自动换行
ps.print(true);
ps.print(true);
ps.printf("%s 喜欢了 %s","阿珍","阿强");
ps.close();
}
}
PrintWriter(字符打印流):
示例代码:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
PrintWriter pw=new PrintWriter(new FileWriter("a.txt"));
//写出数据
pw.println("在撒打算啊实打实");
pw.print("你好");
pw.printf("%s 哎撒旦撒%s","asdas","士大夫");
pw.close();
}
}
解压缩流:
示例代码:
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
PrintWriter pw=new PrintWriter(new FileWriter("a.txt"));
//创建一个File表示要解压的压缩包
File file1=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\a.zip");
//创建一个File表示要解压缩的目的地
File file2=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2");
unzip(file1,file2);
}
//创建一个方法来解压缩
public static void unzip(File file1,File file2) throws IOException {
//创建一个解压缩流
ZipInputStream zip=new ZipInputStream(new FileInputStream(file1));
//获取压缩包内每一个zipentry对象
ZipEntry entry;
while((entry=zip.getNextEntry())!=null){
System.out.println(entry);
//文件夹:需要在目的地dest创建一个同样的文件夹
if(entry.isDirectory()){
File file=new File(file2,entry.getName());
file.mkdirs();
}else{
//文件:需要读取到压缩包中的文件,并将其存放到目的地中
int b;
FileOutputStream fos=new FileOutputStream(new File(file2,entry.toString()));
while((b=zip.read())!=-1){
fos.write(b);
}
fos.close();
//表示在压缩包里的一个文件处理完毕
zip.closeEntry();
}
}
//关流
zip.close();
}
}
压缩流:
示例代码:
//压缩单个文件
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//表示要压缩的文件
File file1=new File("a.txt");
//表示要压缩包的位置
File file2=new File("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2");
//压缩方法
tozip(file1,file2);
}
public static void tozip(File src,File dest) throws IOException {
//创建压缩流关联压缩包
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(new File(dest,"a_zip.zip")));
//创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
ZipEntry entry=new ZipEntry("a_test.txt");
//把Zipentry对象放到压缩包中
zos.putNextEntry(entry);
//把src文件中的数据写入到压缩包中
FileInputStream fis=new FileInputStream(src);
int b;
while((b=fis.read())!=-1){
zos.write(b);
}
zos.closeEntry();
zos.close();
}
}
//压缩文件夹
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//表示要压缩的文件夹
File file1=new File("test");
//表示要压缩包的位置
File file2=new File(file1.getAbsoluteFile()+".zip");
System.out.println(file2);
//创建压缩流关联压缩包
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(file2));
//获取文件源中每一个文件,变成Zipentry对象,放入到压缩包中
tozip(file1.getAbsoluteFile(),zos,"test");
//关流
zos.close();
}
//压缩方法
//参数一:文件源的地址
//参数二:压缩流
//参数三:文件源的父路径的名称
public static void tozip(File src,ZipOutputStream zos,String name) throws IOException {
//遍历获取文件夹中所有的文件
File file[]=src.listFiles(); //数组中记录的都是文件/文件夹的绝对路径
//判断对数组中所有的文件进行判断 ①文件 ②文件夹
for (File f : file) {
if(f.isFile()){
//如果是文件,则对其进行压缩操作
//创建Zipentry对象 ,用来表示压缩包中的要创建的每个文件和文件夹
ZipEntry entry=new ZipEntry(name+"\\"+f.getName());
zos.putNextEntry(entry);
FileInputStream fis=new FileInputStream(f);
int b;
while((b=fis.read())!=-1){
zos.write(b);
}
fis.close();
zos.closeEntry();
}else{
//如果是文件夹,则递归调用该方法
tozip(f,zos,name+"\\"+f.getName());
}
}
}
}
五、常用工具包Commons—io
使用步骤:
①在项目中创建一个文件夹:lib
②将jar包复制粘贴到lib文件夹中
③右键点击jar包,选择Add as Library -> 点击OK
④在类中导包使用
常见方法:
示例代码:
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File src=new File("a.txt");
File dest=new File("a_copyresult.txt");
// FileUtils.copyFile(src,dest);
File src1=new File("test");
File dest1=new File("test_copyresult");
FileUtils.copyDirectory(src1,dest1);
}
}
六、常用工具包Hutool
示例代码:
import cn.hutool.core.io.FileUtil;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 根据参数创建一个file对象
File file = FileUtil.file("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2", "AAA", "BBB", "CCC.txt");
System.out.println(file);
//根据参数创建文件
FileUtil.touch(file);
//把集合中的数据写入到文件中,覆盖模式
ArrayList<String> list=new ArrayList<>();
list.add("aaa");
list.add("BBB");
list.add("ccc");
File file1 = FileUtil.writeLines(list, "C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\ADC.txt", "UTF-8");
System.out.println(file1);
//同上功能,区别:增加模式
// File file2 = FileUtil.appendLines(list, "C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\ADC.txt", "UTF-8");
//读取目标文件中内容,存入指定/新集合中
List<String> strings = FileUtil.readLines("C:\\Users\\DDDic\\IdeaProjects\\Class_12_2\\ADC.txt", "UTF-8");
for (String s : strings) {
System.out.println(s);
}
}
}