(注:本篇文章分为两个部分,如果想更为深刻的了解IO,请关注下一篇;欢迎大家学习讨论和批评指正)
IO1
作用
将数据在虚拟机内存和本地磁盘之间进行传输
I:input 输入
O:output 输出
流
相当于管道,作用为进行数据传输
分类
-
从传输方向上看
- 输入流:本地磁盘的数据向JVM传输
- 输出流:JVM数据向本地磁盘传输
-
从传输单位上看
- 字节流:以字节为单位进行数据传输。可以传输任意类型的数据,如文本、图片、视频、音频等
- 字符流:以字符为单位进行数据传输。只能传输文本类型的数据,如.txt、.java、.html等
-
从传输功能上看
- 节点流:具有传输功能和意义的流
- 过滤流:没有传输功能,用来给节点流增强传输能力或增加附加功能
字节流
- 输入流:InputStream 抽象父类
- 输出流:OutputStream 抽象父类
输入流
- 节点流:FileInputStream
创建
FileInputStream fis=new FileInputStream("本地的文件路径");
-
路径:
-
绝对路径:以电脑磁盘为基点的完整路径
FileInputStream fis = new FileInputStream("D:\\test\\a.txt"); FileInputStream fis = new FileInputStream("D:/test/a.txt");
-
相对路径:以当前项目路径为基点的路径,前提是文件必须在项目下
FileInputStream fis = new FileInputStream("file\\a.txt"); FileInputStream fis = new FileInputStream("file/a.txt");
-
路径书写必须截至至文件
-
文件必须存在,否则抛出异常
-
常用方法
- void close():关闭流链接,释放相关资源。(每个流中都有)
- int read(): 读取一个字节返回,读取到达末尾,返回-1
- int read(byte[] b): 尝试读取数组长度的数据至数组中, 返回实际读取个数.读取到达末尾,返回-1
package com.by.test;import java.io.FileInputStream;public class TestFIS {public static void main(String[] args)throws Exception {// FileInputStream fis = new FileInputStream("D:\\test\\a.txt");// FileInputStream fis = new FileInputStream("D:/test/a.txt");// FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\IdeaProjects\\Chp16\\file\\a.txt");FileInputStream fis = new FileInputStream("file\\a.txt");//FileInputStream fis = new FileInputStream("file/a.txt");//读取一个字节//利用循环读取文件所有内容while (true) {//先接收本次读取内容int n = fis.read();//判断读取是否到达末尾if (n == -1) {break;}//未到达末尾,输出查看System.out.println((char) n);}//利用read(byte[])+循环读取文件所有内容while (true) {byte[] bs = new byte[3];//接收本次读取结果int n = fis.read(bs);//判断读取是否到达末尾if (n == -1) {break;}//遍历数组查看本次读取结果for (int i = 0; i < bs.length; i++) {System.out.println(bs[i]);}}//关流fis.close();System.out.println("执行成功!");}
}
输出流
- 节点流:FileOutputStream
创建
FileOutputStream fos=FileOutputStream("本地的文件路径",true|false);
- 如果文件不存在,会自动创建
- 无法创建文件夹
- true表示数据追加,false表示数据覆盖
- 默认是false
常用方法
- void flush(): 刷新缓冲区,所有输出流中都具备该方法
- void write(int ): 向目标文件写入一个字节
- void write(byte[] ): 向目标文件写入一个数组的数据
package com.by.test;import java.io.FileOutputStream;public class TestFOS {public static void main(String[] args)throws Exception {FileOutputStream fos=new FileOutputStream("file/b.txt");//写入一个字节fos.write(65);fos.write(66);fos.write(67);//写入一个数组String str = "abcdefg123456";byte[] bs = str.getBytes();fos.write(bs);//关流fos.close();System.out.println("执行成功!");}
}
标准异常处理
- JDK7.0之后,提供了自动关流的语法结构,简化了finally的工作内容
try(//需要自动关流的创建语句
){}catch(){}
- 原理: JDK7.0之后,所有的流都默认实现了AutoCloseable接口,该接口中提供了自动关流所需的close方法
package com.by.test;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class TestFOS2 {public static void main(String[] args) {try (FileOutputStream fos = new FileOutputStream("file/b.txt");){//写入一个字节fos.write(65);fos.write(66);fos.write(67);//写入一个数组String str = "abcdefg123456";byte[] bs = str.getBytes();fos.write(bs);} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败");} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}System.out.println("执行成功!");}
}
文件复制
- 原理: 先将文件A中的内容读取到JVM中,再从JVM中将读取内容写入到文件B, 借助JVM来实现A与B之间的数据复制
- 先读后写
package com.by.test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class TestFileCopy {public static void main(String[] args) {copy1();copy2();}/*** 一次复制一个字节*/public static void copy1(){try (//创建输出节点流-复制到的文件路径FileOutputStream fos=new FileOutputStream("d:/test/2.pdf");//创建输入节点流-被复制的文件路径FileInputStream fis=new FileInputStream("d:/test/1.pdf")) {//先循环读取文件所有内容while (true) {int n = fis.read();if (n == -1) {break;}//将本次读取的字节写入到目标文件fos.write(n);}System.out.println("复制成功!");} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}/*** 一次复制一个字节数组*/public static void copy2(){try (//创建输出节点流-复制到的文件路径FileOutputStream fos=new FileOutputStream("d:/test/3.pdf");//创建输入节点流-被复制的文件路径FileInputStream fis=new FileInputStream("d:/test/1.pdf")) {//先循环读取文件所有内容while (true) {//创建数组byte[] bs = new byte[1024];//读取一个数组的数据int n = fis.read(bs);if (n == -1) {break;}//将数组中的数据写入到目标文件fos.write(bs);}System.out.println("复制成功!");} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
}
缓冲过滤流
- BufferedInputStream: 输入缓冲过滤流
- BufferedOutputStream: 输出缓冲过滤流
创建
BufferedInputStream bis=new BufferedInputStream(fis对象);
BufferedOutputStream bos=new BufferedOutputStream(fos对象);
作用
拥有一个内置的数据缓冲区, 文件数据将对接至数据缓冲区中,在缓冲区刷新或关闭时再将内部内容一并的给到目标文件
/*** 一次复制一个字节+缓冲过滤流*/public static void copy3(){try (//创建输出节点流-复制到的文件路径FileOutputStream fos=new FileOutputStream("d:/test/4.pdf");//创建输入节点流-被复制的文件路径FileInputStream fis=new FileInputStream("d:/test/1.pdf");//添加缓冲过滤流BufferedOutputStream bos=new BufferedOutputStream(fos);BufferedInputStream bis=new BufferedInputStream(fis)) {//先循环读取文件所有内容while (true) {int n = bis.read();if (n == -1) {break;}//将本次读取的字节写入到目标文件bos.write(n);}System.out.println("复制成功!");} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
使用
-
如果先写后读,需要在写入完成后刷新缓冲区才能保证读取的正常进行
- 调用flush():强刷缓冲区 (推荐)
- 调用close(): 关流之前也会刷新缓冲区
-
关流时外层过滤流关闭内层节点流会一并关闭
package com.by.test;import java.io.*;public class TestBuffered {public static void main(String[] args) {try(//输出BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("file/a.txt"));//输入BufferedInputStream bis=new BufferedInputStream(new FileInputStream("file/a.txt"))){//先写bos.write("abcd".getBytes());//刷新缓冲区bos.flush(); //bos.close();//再读while (true) {int n = bis.read();if (n == -1) {break;}System.out.println((char) n);}}catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
}
对象过滤流
- ObjectInputStream 对象输入过滤流
- ObjectOutputStream 对象输出过滤流
- 附加功能1: 读写基本类型
- 附加功能2: 读写引用类型
读写基本类型
读取: ois.readXxx()
写入: oos.writeXxx(值);注: Xxx对应的为基本类型名,首字母大写
- 由于对象过滤流底层嵌套了缓冲区,所以先写后读操作时,仍然需要在写入完成后刷新缓冲区
- 为了保证数据的安全性,所以在写入时数据会根据魔数机制对其加密,在读取时在进行解密
package com.by.test;import java.io.*;public class TestObject_Double {public static void main(String[] args) {try (//输出流ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));//输入流ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))) {//先写oos.writeDouble(10.5);//强刷缓冲区oos.flush();//读取System.out.println(ois.readDouble());} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");e.printStackTrace();} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
}
读写引用类型
读取: Object ois.readObject() 读取到达末尾,抛出EOFException异常
写入: oos.writeObject(对象) 可以自动刷新缓冲区,所以先写后读时无需进行flush
读写String
package com.by.test;import java.io.*;public class TestObject_String {public static void main(String[] args) {try (//输出流ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));//输入流ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))) {//先写oos.writeObject("床前明月光");oos.writeObject("疑是地上霜");oos.writeObject("举头望明月");oos.writeObject("低头思故乡");//后读while (true) {try {String s =(String) ois.readObject();System.out.println(s);} catch (EOFException e) {//读取到达末尾,跳出循环break;}}} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");e.printStackTrace();} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
}
读写自定义类型
-
自定义类必须实现Serializable接口,表示允许被序列化,否则IO流没有读写权限
序列化:拆分对象信息的过程
反序列化:通过信息组装对象的过程
-
将属性通过transient修饰符修饰则可以防止其参与序列化
-
如果对象中有自定义类型的属性,则必须使该属性类型也实现序列化接口或者通过transient修饰符对其修饰
package com.by.entity;import java.io.Serializable;public class Student implements Serializable {private String name;private int age;//防止被序列化private transient double score;// private Teacher tea;
//省略getter、setter、构造、toString
}
package com.by.test;import com.by.entity.Student;import java.io.*;public class TestObject_Student {public static void main(String[] args) {try (//输出流ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));//输入流ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))) {//先写oos.writeObject(new Student("zhangsan", 20, 88.5));//后读System.out.println((Student)ois.readObject());} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败!");e.printStackTrace();} catch (Exception e) {System.out.println("未知异常!");e.printStackTrace();}}
}
怎么理解输出语句?
System是类库中的工具类,out为该工具类中的静态属性,类型为标准输出流类型,print和println系列方法是该流中提供的写入方法,作用为向控制台写入一个内容
今日掌握
- 流的分类
- 文件复制的源码(字节复制+缓冲过滤流)
- 对象过滤流读写自定义类型