目录
什么是IO流?
IO流是指用于输入输出操作的数据流,通常在计算机程序中用来读取或写入数据。它是一种将数据从一个地方传递到另一个地方的方法。在Java中,IO流被分为输入流(InputStream)和输出流(OutputStream),它们用于读取和写入数据。
输入流通常用来从文件、网络或其他数据源中读取数据,而输出流通常用来将数据写入文件、网络或其他数据存储位置。在Java中,IO流通常是面向字节的,即字节流,也可以使用字符流。
流的分类
从传输单位来看,分为字节流和字符流。从传输方向来看,分为输入流和输出流。
字节流:可以操作所有类型的文件,因为操作的每个单位是一个字节。
字符流:操作的只能是纯文本文件。(以.txt等结尾)
- 字节输入流:FileInputStream
- 字节输出流:FileOutputStream
- 字符输入流:FileReader
- 字符输出流:FileWriter
字节流操作的是文件中的每一个字节
字符流是按照字符来进行输入输出。
进制:
1 byte = 8 bits = 8 个二进制位
1 字节 = 1 byte
编码格式:
国家标准的编码格式位GBK
国际统一的编码是Unicode,UTF-8、UTF-16为Unicode的编码方式。
英文字母占用一个字节
中文在GBK编码中占用两个字节
Unicode中UTF-8编码方式中文占用三个字节(格式为:1110xxxx 10xxxxxx 10xxxxxx)
字节输入流 FileInputStream
每次只能操作一个字节,如果需要多个操作则需要使用循环
/**
* 创建对象,文件不存在,直接报错
*/
FileInputStream fis = new FileInputStream("stream\\src\\main\\a.txt");
/**
* 读取数据
* 一次读取一个字节,读出来的数据是ASCII对应的数字
* 读取到文件末尾返回-1
*/
int b;
while ((b = fis.read()) != -1) {
System.out.print((char) b);
}
/**
* 释放资源
* 每次使用完流之后必须释放资源
*/
fis.close();
字节输出流 FileOutputStream
//第二个参数为是否续写,为true则追加内容,false则覆盖,默认为false
FileOutputStream fos = new FileOutputStream("stream\\src\\main\\a.txt", true);
//写出数据
String str = "haohaoxuexi";
byte[] bytes = str.getBytes();
fos.write(bytes);
//写换行符
fos.write("\r\n".getBytes());
String str2 = "tiantianxiangshang";
fos.write(str2.getBytes());
//释放资源
fos.close();
每个操作系统中的换行符都不同:
windows \r\n
linux \n
mac \r
java中会自动补全,写\r、\n、\r\n都可以,但是最好写全。
字节流复制文件
使用字节流进行复制文件,就是使用输入流将文件读取,然后再使用输出流将文件写出到另一个文件中。在面对大文件的时候,一个字节一个字节的操作效率会非常低,因此可以创建一个byte数组来复制传递数据。
FileInputStream fis = new FileInputStream("stream\\src\\main\\a.jpg");
FileOutputStream fos = new FileOutputStream("stream\\src\\main\\b.jpg");
long start = System.currentTimeMillis();
int b;
byte[] bytes = new byte[1024 * 8];//1m
while ((b = fis.read(bytes)) != -1) {
fos.write(bytes, 0, b);
}
System.out.println(System.currentTimeMillis() - start);
//释放
fos.close();
fis.close();
字节流设置编码格式
编码
public byte[] getBytes() 使用默认方式进行编码
public byte[] getBytes(String charsetName) 使用指定方式进行编码
解码
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes, String charsetName) 使用指定方式进行编码
//编码
String str = "好好学习 天天向上";
byte[] bytes1 = str.getBytes();
System.out.println(Arrays.toString(bytes1));
byte[] bytes2 = str.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));
//解码
String str2 = new String(bytes1);
System.out.println(str2);
String str3 = new String(bytes2,"GBK");
System.out.println(str3);
字符输入流 FileReader
创建对象
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流关联本地文件读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1释放资源
public void close
无参read方法一个字符一个字符的读取
//创建对象
FileReader fr = new FileReader("stream\\src\\main\\a.txt");
//读取文本
//字符流底层也是字节流,默认也是一个字节一个字节的读
//如果遇到中文会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
//关流
fr.close();
有参的read方法需要传入一个字符数组,表示一次读取多少个字符
//创建对象
FileReader fr = new FileReader("stream\\src\\main\\a.txt");
//读取文本
//字符流底层也是字节流,默认也是一个字节一个字节的读
//如果遇到中文会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
//有参的read将读取数据、解码、强转散步合并,将合并后的字符放到数组中
int len;
char[] chars = new char[2];
while ((len = fr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
//关流
fr.close();
字符输出流 FileWriter
创建对象
参数是字符串表示的路径或者File对象
如果文件不存在会创建一个新的文件,但是要保证父级路径存在
入股文件已经存在,则会清空文件,如果不想清空可以打开续写开关
public FileWriter(File file) 创建字符输出流关联本地文件
public FileWriter(String pathname) 创建字符输出流关联本地文件
public FileWriter(File file, boolean append) 创建字符输出流关联本地文件,续写开关
public FileWriter(String pathname, boolean append) 创建字符输出流关联本地文件,续写开关写数据
如果write方法的参数是整数,但是实际上写道本地文件中的是数字在字符集上对应的字符
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) 写出一个字符数组的一部分释放资源
每次使用完流之后都要释放资源
public void close() 释放资源/关流
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("stream\\src\\main\\a.txt",true);
//方法一:
fw.write(25105);//根据字符集的编码方式进行编码,将编码后的数据写到文件中
//方法二:
fw.write("你好你好");//根据字符集的编码方式进行编码,将编码后的数据写到文件中
//方法三:
char[] chars = {'a','c','b','d','e','你'};
fw.write(chars);
fw.close();
字符流其他操作:复制文件、加解密文件、排序1、排序2。都在文末代码包。
高级流
在字节流和字符流的基础上,java封装了高级流,包括:缓冲流、转换流、序列化流/反序列化流、打印流、压缩流/解压缩流。
缓冲流
缓冲流是对四种基本流的增强,分为:
- 字节缓冲输入流:BufferedInputStream
- 字节缓冲输出流:BufferedOutputStream
- 字符缓冲输入流:BufferedReader
- 字符缓冲输出流:BufferedWriter
提高效率的基本原理:在创建流对象的时候,会创建一个内置的默认大小的缓冲数组,通过缓冲区进行读写操作,减少与系统IO的次数,从而提高读写效率。
创建缓冲区大小:
字节缓冲流中创建 8192个字节的缓冲区 8k大小
字符缓冲流中创建8192个字符的缓冲区 16k
字节缓冲输入流 BufferedInputStream
字节缓冲输出流 BufferedOutputStream
复制操作两种方式:
基本方式:
//创建缓冲流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("stream\\src\\main\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("stream\\src\\main\\a_copy.txt"));
//拷贝
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
//关流
bos.close();
bis.close();
增强方式:设置字节数组,一次性读取多个字节进行复制操作。
//创建缓冲流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("stream\\src\\main\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("stream\\src\\main\\a_copy2.txt"));
//拷贝
byte[] bytes = new byte[1028];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes);
}
//关流
bos.close();
bis.close();
字符缓冲输入流 BufferedReader
构造方法:
public BufferedReader(Reader r) 把基本流变成高级流
public BufferedWriter(Writer r) 把基本流变成高级流特有方法:
字符缓冲输入流
public String readLine() 读取一整行数据,如果没有数据可读了,会返回null字符缓冲输出流:
public void newLine() 跨平台换行
BufferedReader br = new BufferedReader(new FileReader("stream\\src\\main\\a.txt"));
//细节:
//readLine在读取的时候是一行一行的读取,但是不会将换行符读取
String line = br.readLine();
System.out.println(line);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
字符缓冲输出流 BufferedWriter
构造方法:
public BufferedReader(Reader r) 把基本流变成高级流
public BufferedWriter(Writer r) 把基本流变成高级流特有方法:
字符缓冲输入流
public String readLine() 读取一整行数据,如果没有数据可读了,会返回null字符缓冲输出流:
public void newLine() 跨平台换行
BufferedWriter bw = new BufferedWriter(new FileWriter("stream\\src\\main\\bufferedCharWriter.txt", true));
//写数据
bw.write("好好学习");
bw.newLine();
bw.write("天天向上");
bw.newLine();
bw.close();
字符缓冲输入/输出流的其他操作:大文件复制效率对比、排序操作。都在文末代码包。
转换流
转换流===》字符流(属于字符流)
文件在系统中都是以字节的形式存储的,由于编码方式的不同,在读取数据的时候,需要根据对应的方式进行解码,为了方便对字节流的操作,封装了转换流来简化操作。转换流分为:
- 字节到字符输入流:InputStreamReader
- 字符到字节输出流:OutputStreamWriter
作用:
- 使用指定字符集读取数据
- 字节流中想使用字符流的方法
字节到字符输入流 InputStreamReader
//创建对象指定字符编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("stream\\src\\main\\gbkfile.txt"), "GBK");
//读取数据
int ch;
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
isr.close();
JDK11后使用的方式:
FileReader fr = new FileReader("stream\\src\\main\\gbkfile.txt", Charset.forName("GBK"));
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
fr.close();
字符到字节输出流 OutputStreamWriter
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("stream\\src\\main\\gbkfile_output.txt"), "GBK");
osw.write("好好学习");
osw.close();
JDK11后使用的方式:
FileWriter fw = new FileWriter("stream\\src\\main\\gbkfile_output.txt","GBK");
fw.write("你好你好");
fw.close();
转换流其他操作:读取GBK编码文件以UTF-8编码方式输出、每次读取一整行。都在文末代码包。
序列化/反序列化流
序列化/反序列化流===》字节流(属于字节流)
Java中提供的序列化流,可以将对象保存到文件中,并且可以再通过文件读取对象,从而达到持久化的效果。主要分为:
- 序列化流:ObjectOutputStream
- 反序列化流:ObjectInputStream
注意:
- 被序列化的对象必须继承Serializable接口,否则会出现NotSerializableException异常。
- 序列化流写到文件中的数据不能修改,一旦修改就无法反序列化。
- 被序列化的类会根据成员变量、构造方法、成员方法、静态变量等,生成一个long类型的序列号,在进行序列化时也会将序列号写入到文件中,在读取的时候,会将序列号进行对比,如果不同就会抛出InvalidClassException异常。
解决方法:手动给JavaBean添加seriaVersionUID
- 如果某个成员变量不想被实例化到文件中,可以在该变量前加 transient ,该关键字标记的成员变量不会参与序列化过程(如保存的密码等敏感信息)
序列化流 ObjectOutputStream
构造方法:
public objectOutputStream(OutputStream out) 把基本流变成高级流
成员方法:
public final void writeObject(Object obj) 把对象序列化(写出)到文件中去
//创建对象
Student student = new Student("张三",23,"上海");
//创建序列化流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Stream-study\\src\\main\\student.txt"));
//写出数据
oos.writeObject(student);
//关流
oos.close();
反序列化流 ObjectInputStream
构造方法
public ObjectInputStream(InputStream in) 把基本流变成高级流成员方法
public Object readObject() 把序列化到本地的对象,读取到程序中来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Stream-study\\src\\main\\student.txt"));
Object o = ois.readObject();
Student student = (Student) o;
System.out.println(o);
System.out.println(student);
ois.close();
打印流
我们使用的System.out.println()方法和System.out.print()方法就是使用的打印流,该类可以帮助我们方便的打印出各种数据。
PrintStream
构造方法:
public PrintStream(OutputStream/File/String) 关联字节输出流/文件/文件路径
public PrintStream(String filename, Charset charset) 指定字符编码
public PrintStream(OutputStream out, boolean autoFlush) 自动刷新
public PrintStream(OutputStream out, boolean autoFlush, String encoding) 指定字符编码并且自动刷新成员方法: (字节层没有缓冲区,开不开自动刷新都一样)
public void write(int b) 常规方法:将指定字节输出
public void println(Xxx xx) 特有方法:打印任意数据,自动刷新,自动换行
public void print(Xxx xx) 特有方法:打印任意数据,不换行
public void printf(String format, Object... args) 特有方法:带有占位符的打印语句,不换行
//创建字节打印流对象
PrintStream ps = new PrintStream(new FileOutputStream("stream\\src\\main\\a.txt"), true, "UTF-8");
//写出数据
ps.println(97);//写出 + 自动刷新 + 自动换行
ps.print(true);
ps.println();
ps.printf("%s 爱上了 %s","阿珍","阿强");
ps.close();
常使用的System.out.println()方法
//获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台
//特殊的打印流,系统中的标准输出流;是不能关闭的,在系统中是唯一的,除非重启虚拟机
PrintStream ps = System.out;
//调用打印流中的方法
//写出数据 自动换行 自动刷新
ps.println("123");
PrintWriter
构造方法:
public PrintWriter(Writer/File/String) 关联字节输出流/文件/文件路径
public PrintWriter(String filename, Charset charset) 指定字符编码
public PrintWriter(OutputWriter out, boolean autoFlush) 自动刷新
public PrintWriter(OutputWriter out, boolean autoFlush, String encoding) 指定字符编码并且自动刷新成员方法:
public void write(int b) 常规方法:将指定字节输出
public void println(Xxx xx) 特有方法:打印任意数据,自动刷新,自动换行
public void print(Xxx xx) 特有方法:打印任意数据,不换行
public void printf(String format, Object... args) 特有方法:带有占位符的打印语句,不换行
PrintWriter pw = new PrintWriter(new FileWriter("stream\\src\\main\\a.txt"), true);
//写出数据
pw.println("洞房花烛夜,金榜题名时,久旱逢甘露,他乡遇故知");
pw.print("你好你好");
pw.printf("%s 爱上了 %s", "阿珍", "阿强");
pw.close();
压缩/解压缩流
可以用来压缩解压缩文件
注意:
- 压缩包里每一个文件在java中,都时一个ZipEntry对象
压缩单个文件
//压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中
//1.创建File对象 表示要压缩的文件
File src = new File("D:\\aaa.txt");
//2.创建File表示压缩包的位置
File dest = new File("D:\\");
//创建压缩流
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest, "a.zip")));
//创建ZipEntry对象 这个对象是往zos中插入的
//参数:压缩包里面的路径
ZipEntry entry = new ZipEntry("csb.txt");
//把ZipEntry对象放到压缩包中
zos.putNextEntry(entry);
FileInputStream fis = new FileInputStream(src);
int b;
while ((b = fis.read()) != -1) {
zos.write(b);
}
zos.closeEntry();
zos.close();
压缩一个文件夹内的文件
主函数
//1.创建File对象表示要压缩的文件夹
File src = new File("D:\\aaa");
//2.创建File对象表示压缩包父级路径
File destParent = src.getParentFile();
//3.创建File对象,表示压缩包的路径
File dest = new File(destParent, src.getName() + ".zip");
//4.创建压缩流关联压缩包
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
//5.获取src中的每一个文件,变成ZipEntry对象,放入到压缩包中
toZip(src, zos, src.getName());
//6.释放资源
zos.close();
压缩函数
/**
* 作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包中
*
* @param src 数据源
* @param zos 压缩流
* @param name 压缩包内部路径
*/
public static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
//1.进入src文件夹
File[] files = src.listFiles();
//2.遍历文件
for (File file : files) {
if (file.isFile()) {
//如果是文件,变成zipEntry对象,放入到压缩包当中
ZipEntry entry = new ZipEntry(name + "\\" + file.getName());
zos.putNextEntry(entry);
//读取文件数据,放入压缩包中
FileInputStream fis = new FileInputStream(file);
int b;
while ((b = fis.read()) != -1) {
zos.write(b);
}
fis.close();
zos.closeEntry();//表示当前Entry书写完毕
} else {
//如果是文件夹,递归
toZip(file, zos, name + "\\" + file.getName());
}
}
}
解压缩文件
主函数
//创建一个File表示要解压的压缩包
File src = new File("D:\\aaa.zip");
//创建一个File表示要解压的目的地
File dest = new File("D:\\");
unzip(src, dest);
解压函数
public static void unzip(File src, File dest) throws IOException {
//解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地中
//创建一个解压缩流用来读取压缩包中的数据
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
//获取到压缩包里面的每一个zipEntry对象
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
System.out.println(entry);
if (entry.isDirectory()) {
//文件夹:需要在dest创建相同文件夹
File file = new File(dest, entry.toString());
file.mkdirs();
} else {
//文件:读取压缩包中的文件,并把他们存放到dest文件夹中(按照层级目录存放)
FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString()));
int b;
while ((b = zip.read()) != -1) {
fos.write(b);
}
fos.close();
//表示在一个压缩包中的文件处理完毕
zip.closeEntry();
}
}
zip.close();
}