Java IO流 学习笔记

qaq

IO流分类

字节流

  • 可以操作所有类型的文件
  • 读取文件的时候不要有中文
  • 每次只能操作一个字节(整数不能超过一个字节)
子类
  • InputStream
  • OutputStream

字符流

  • 只能操作纯文本文件
子类
  • Reader
  • Writer

字节流

字节输出流

  • FileOutputStream(File filename, boolean append)
  • FileOutputStream(String filepath)

字节输入流

FileInputStream fis = new FileInputStream("");
int b;
while((b = fis.read()) != -1) {
    System.out.println((char)b);
}
fis.close();

文件拷贝

小文件

FileInputStream fis = new FileInputStream("");
FileOutputStream fos = new FileOutputStream("");
int b;
while((b = fis.read()) != -1) {
    fos.write(b);
}
//释放资源
//先开的最后关闭
fos.close();
fis.close();

文件大的时候一次次read()太慢

FileInputStream fis = new FileInputStream("");
FileOutputStream fos = new FileOutputStream("");
byte[] buf = new byte[1024 * 1024 * 5];//一般取1024的倍数
while((len = fis.read(buf)) != -1) {
    fos.write(buf, 0, len);
}

fos.close();
fis.close(); 

字符流

  • 字符流 = 字节流 + 字符集

特点

  • 输入流:一次读一个字节,遇到中文时,一次读多个字节
  • 输出流:底层把数据按照指定的编码方式进行编码,变成字节再写到文件中

使用场景

  • 操作纯文本文件

FileReader

  • public int read():读取数据,到末尾返回 -1
  • public int read(char[] buffer):读取多个数据,到末尾返回 -1
    • 细节:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个十进制整数,代表在字符集上的数字
    • 字符流的底层也是字节流
FileReader fr = new FileReader("");
int ch;
while((ch = fr.read()) != -1) {
    System.out.println((char)ch);
}
fr.close();
FileReader fr = new FileReader("");
char[] ch = new char[10];
int len;
//read(chars):把读取数据、解码、强转三步合并了,把强转之后的字符放到数组当中
//空参的read + 强制类型转换
while((len = fr.read(ch)) != -1) {
    System.out.println(new String(ch, 0, len));
}
fr.close();

FileWriter

FileWriter fw = new FileWriter("");
fw.write(25105);//根据字符集的编码方式编码,把编码之后的数据写到文件中
fw.close();
FileWriter fw = new FileWriter("");
char[] ch = {'a', 'b', 'c', ''};
fw.write(ch);
fw.close();

字符流底层原理

  • 创建字符输入流对象
    • 关联文件,并创建缓冲区(长度为8192的字节数组)(字节流无缓冲区)
  • 读取数据
    • 判断缓冲区中是否有数据
      • 缓冲区无数据:从文件中获取数据,装入缓冲区,尽可能装满
      • 缓冲区有数据:从缓冲区读取
    • 空参read:一次读取一个字节,遇到中文一次读多个字节,把字节解码转成十进制返回
    • 有参read:读取字节、解码、强转三步合并,强转后的字符放到数组中
  • 输出数据
    • 在内存中创建一个8192的缓冲区,每一次write都会把内容编码写进缓冲区里,出现下面的情况时,会把缓冲区内容写入文件
      • 缓冲区满
      • 刷新(flush()
      • 关流(close()

缓冲流

加了缓冲区的基本流

字节缓冲流

  • public BufferedInputStream(InputStream is)
  • public BufferedOutputStream(OutputStream os)

字符缓冲流

因为字符基本流已经有缓冲区了,所以效率提高不明显

  • BufferedReader
  • BufferedWriter

主要体现在两个比较好用的方法

  • 输入流:public String readLine():读取一行数据,如果没数据了,返回null。但是不会读取回车换行
  • 输出流:public void newLine():跨平台的换行

转换流

是字符流和字节流之间的桥梁

数据源 -> 字节流 -> 转换流 -> 内存 -> 转换流 -> 字节流 ->目的文件

  • InputStreamReader
  • OutputStreamWriter
InputStreamReader isr = new InputStreamReader(new FileInputStream(""), "GBK");
int ch;
while((ch = isr.read()) != -1) {
    System.out.print((char)ch);
}
isr.close();
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(""), "GBK");
osw.write("你好");
osw.close();

JDK11后把FileInputStreamFileOutputStream的构造方法加入了字符编码

//将本地文件中的GBK文件,转成UTF-8

InputStreamReader isr = new InputStreamReader(new FileInputStream(""), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(""), "UTF-8");
int b;
while((b = isr.read()) != -1) {
    osw.write(b);
}
osw.close();
isr.close();

练习

//用字节流读文件中数据,一次读一行,不能出现乱码
//1.字节流在读中文的时候,会出现乱码,用字符流没事
//2.字节流里面是没有读一行的方法,字符缓冲流可以

//FileInputStream fis = new FileInputStream("");
//InputStreamReader isr = new InputStreamReader(fis);
//BufferedReader br = new BufferedReader(isr);

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("")));

String str;
while((str = br.readLine()) != null) {
    System.out.println(str);
}
br.close();

序列化流

  • 是字节流的一种
  • 可以把Java中的对象写到本地文件中
  • 是高级流

构造方法

  • public ObjectOutputStream(OutputStream out):基本流包装成高级流
  • public final void writeObject(Object obj):把对象序列化写出

输出

Student stu = new Student("a", 18);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(""));
oos.writeObject(stu);
oos.close();

注意:要序列化的Javabean类需要实现Serializable接口,Serializable接口是个标记接口

输入

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(""));

//Object o = ois.readObject();
Student o =(Student) ois.readObject();

System.out.println(o);

ois.close();

版本号

  • 当一个对象实现了Serializable接口之后,每一次修改这个Javabean类都会生成新的版本号,而每次序列化会把版本号序列化进去,使得在反序列化时可能会出现序列化的对象和当前Javabean类版本号不一致的问题

  • 可以固定版本号

    public class Student implements Serializable {
        private static final long serialVersionUID = 1L;// 唯一格式 建议自动生成
    }

transient关键字

  • 瞬态关键字
  • 被此关键字修饰的变量不会被序列化到本地文件中
private transient String address;

打印流

  • 只有输出流

  • 字节打印流 和 字符打印流

  • 不能操作数据源,只能操作目的地

  • PrintStream

  • PrintWriter

字节打印流

字节流底层没缓冲区,自动刷新开不开没区别

构造

  • 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...):特有方法,带有占位符的打印语句,不换行
PrintStream ps = new PrintStream(new FileOutputStream(""), true, Charset.forName("GBK"));
ps.println(97);//写出 + 自动刷新 + 自动换行
ps.print(true);
ps.println();
ps.printf("%s loves %s", "a", "b");
ps.close();

字符打印流

字符流底层有缓冲区,要自动刷新需要手动开启

构造

  • public PrintWriter(Writer/File/String):关联字节输出流/文件/路径
  • public PrintWriter(String fileName, Charset charset):指定字符编码
  • public PrintWriter(Write w, boolean autoFlush):自动刷新
  • public PrintWriter(OutputStream out, boolean autoFlush, Charset charset):指定字符编码且自动刷新

方法

和字节打印流一样

PrintWriter pw = new PrintWriter(new FileWriter(""), true);
pw.println("abc");
pw.close();

用处

  • System.out是一个静态打印流对象,虚拟机启动的时候,由JVM赋值,默认指向控制台

压缩流

  • 只能识别.zip文件
  • 压缩包里每一个文件在Java中是一个ZipEntry对象
  • 解压本质:把每一个ZipEntry对象按照层级拷贝到本地另一个文件夹中

解压缩流

ZipInputStream


未完待续

img_show