基本概念

  • IO就是Input和Output的简写,也就是输入和输出的含义。
  • IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为“流”

    基本分类

    按照读写数据的基本单位不同,分为 字节流 和 字符流。

  • 其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。

  • 其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件。

按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)。

  • 其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件。
  • 其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件。

按照流的角色不同分为节点流和处理流。

  • 其中节点流主要指直接和输入输出源对接的流。
  • 其中处理流主要指需要建立在节点流的基础之上的流。

IO流 - 图1
IO流 - 图2

相关流—常用方法类似

FileWriter类

java.io.FileWriter类主要用于将文本内容写入到文本文件。
image.png

  1. package com.lagou.task17;
  2. import java.io.FileWriter;
  3. import java.io.IOException;
  4. /**
  5. * @author lijing
  6. * @date 2020/10/14 10:38
  7. * @description
  8. */
  9. public class FileWriterTest {
  10. public static void main(String[] args) {
  11. FileWriter fw= null;
  12. try {
  13. //1.构造FileWrite类型的对象与D:/atxt文件关联
  14. //若文件不存在,会创新新文件
  15. //若文件存在,该流会清空文件中原有内容
  16. // fw = new FileWriter("D:/a.txt")
  17. //已追加的方法创建对象去关联
  18. fw=new FileWriter("D:/a.txt",true);
  19. //2.通过流对象写入数据内容
  20. fw.write('a');
  21. //混呗一个字数组
  22. char[] cArr=new char[]{'h','e','l','l','o'};
  23. //将整个字符数组写入
  24. fw.write(cArr,1,3);
  25. fw.write(cArr);
  26. //刷新流
  27. fw.flush();
  28. System.out.println("写入数据成功");
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }finally {
  32. //3.关闭流对象并释放有关的资源
  33. if(null!=fw){
  34. try {
  35. fw.close();
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. }
  42. }

FileReader类

java.io.FileReader类主要用于从文本文件读取文本数据内容。
image.png

  1. package com.lagou.task17;
  2. import java.io.FileReader;
  3. import java.io.IOException;
  4. /**
  5. * @author lijing
  6. * @date 2020/10/14 11:08
  7. * @description
  8. */
  9. public class FileReaderTest {
  10. public static void main(String[] args) {
  11. FileReader fr= null;
  12. try {
  13. //1.构造
  14. fr = new FileReader("D:/a.txt");
  15. //2.读取
  16. /*int read = fr.read();
  17. System.out.println("read-single-char:"+ ((char) read));*/
  18. /*int res=0;
  19. while((res=fr.read())!=-1){
  20. System.out.println("read-single-char:"+ ((char) res));
  21. }*/
  22. //准备一个字符数组来保存读取到的数据内容
  23. char[] cArr=new char[5];
  24. /*//期望读满字符数组中的一部分空间
  25. int read = fr.read(cArr, 1, 3);
  26. System.out.println("The actual number of reads is:"+ read);//3
  27. for (char cv :cArr) {
  28. System.out.println("The actual single character read is:"+ ((char) cv));
  29. }*/
  30. //期望读满字符数组
  31. int read=fr.read(cArr);
  32. System.out.println("The actual number of reads is:"+ read);//5
  33. for (char cv :cArr) {
  34. System.out.println("The actual single character read is:"+ ((char) cv));
  35. }
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. } finally {
  39. if (null!=fr){
  40. try {
  41. //3.关流
  42. fr.close();
  43. } catch (IOException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }
  48. }
  49. }

FileOutputStream类

java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中。
image.png

FileInputStream类

java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等。
image.png

  1. package com.lagou.task17;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. /**
  6. * @author lijing
  7. * @date 2020/10/14 13:27
  8. * @description
  9. */
  10. public class FileByteCopyTest {
  11. public static void main(String[] args) {
  12. long g1 = System.currentTimeMillis();
  13. FileInputStream fis = null;
  14. FileOutputStream fos = null;
  15. try {
  16. //1.创建字节流输入输出的对象
  17. // fis = new FileInputStream("D:/02总结.png");
  18. // fos = new FileOutputStream("D:/总结.png");
  19. fis = new FileInputStream("D:/navicat150_mysql_cs_x64.exe");
  20. fos = new FileOutputStream("D:/1navicat150_mysql_cs_x64.exe");
  21. //2.不断地从输入流总读取数据内容并写入到输出流中
  22. System.out.println("Copying...");
  23. //方式一:以单个字节为单位进行拷贝,也就是每次读取一个字节后在写入一个字节
  24. //缺点:文件稍大时,拷贝的效率很低
  25. /*int res=0;
  26. while((res=fis.read())!=-1){
  27. fos.write(res);
  28. }*/
  29. //方式二:准备一个和文件大小一样的缓冲区,一次性将文件中的所有内容取出缓冲区然后一次性写入
  30. //缺点:若文件过大时,无法申请和文件大小一样的缓冲区,真实物理内存不足
  31. // int len=fis.available();
  32. // System.out.println("gets the Bytes of the file:" + len);
  33. // byte[] bArr = new byte[len];
  34. // int res=fis.read(bArr);
  35. // System.out.println("actual the bytes of the file:" + res);
  36. // fos.write(bArr);
  37. //方式三:准备一个相对适当的缓冲区,分多次将文件拷贝完成
  38. byte[] bArr=new byte[1024];
  39. int res=0;
  40. while((res=(fis.read(bArr)))!=-1){
  41. fos.write(bArr,0,res);
  42. }
  43. System.out.println("Copy Completely");
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. } finally {
  47. //3.关流
  48. if(null!=fos){
  49. try {
  50. fos.close();
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. }
  54. }
  55. if(null!=fis){
  56. try {
  57. fis.close();
  58. } catch (IOException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. long g2 = System.currentTimeMillis();
  64. System.out.println("The time it takes to copy:" + (g2 - g1));
  65. }
  66. }

BufferedOutputStream类

image.png
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统。

BufferedInputStream类

java.io.BufferedInputStream类主要用于描述缓冲输入流。
image.png

BufferedReader类

java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中。


BufferedWriter(Writer out)

根据参数指定的引用来构造对象

BufferedWriter(Writer out, int sz)

根据参数指定的引用和缓冲区大小来构造对象

void write(int c)

写入单个字符到输出流中

void write(char[] cbuf, int off, int
len)

将字符数组cbuf中从下标off开始的len个字符写入输出流中

void write(char[] cbuf)

将字符串数组cbuf中所有内容写入输出流中

void write(String s, int off, int len)

将参数s中下标从off开始的len个字符写入输出流中

void write(String str)

将参数指定的字符串内容写入输出流中

void newLine()

用于写入行分隔符到输出流中

void flush() 刷新流
void close() 关闭流对象并释放有关的资源
  1. package com.lagou.task17;
  2. import java.io.*;
  3. /**
  4. * @author lijing
  5. * @date 2020/10/14 13:50
  6. * @description
  7. */
  8. public class BufferedByteCopyTest {
  9. public static void main(String[] args) {
  10. long g1 = System.currentTimeMillis();
  11. BufferedInputStream bis= null;
  12. BufferedOutputStream bos= null;
  13. try {
  14. //1.创建BufferedInputStream类型的对象与文件关联
  15. bis = new BufferedInputStream(new FileInputStream("D:/navicat150_mysql_cs_x64.exe"));
  16. //2.创建BufferedOutputStream类型的对象与文件关联
  17. bos = new BufferedOutputStream(new FileOutputStream("D:/1navicat150_mysql_cs_x64.exe"));
  18. //3.拷贝
  19. System.out.println("Copying...");
  20. byte[] bArr=new byte[1024];
  21. int res=0;
  22. while((res=bis.read(bArr))!=-1){
  23. bos.write(bArr,0,res);
  24. }
  25. System.out.println("Copy Completely");
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. } finally {
  29. //4.关流
  30. if (null!=bos){
  31. try {
  32. bos.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. if(null!=bis){
  38. try {
  39. bis.close();
  40. } catch (IOException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }
  45. long g2 = System.currentTimeMillis();
  46. System.out.println("The time it takes to copy:" + (g2 - g1));
  47. }
  48. }

BufferedWriter类

java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串。

printStream类

java.io.PrintStream类主要用于更加方便地打印各种数据内容。

  1. package com.lagou.task17;
  2. import java.io.*;
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. /**
  6. * @author lijing
  7. * @date 2020/10/14 14:17
  8. * @description
  9. */
  10. public class PrintStreamTest {
  11. public static void main(String[] args) {
  12. BufferedReader br = null;
  13. PrintStream ps = null;
  14. try {
  15. br = new BufferedReader(new InputStreamReader(System.in));
  16. ps = new PrintStream(new FileOutputStream("d:/a.txt"));
  17. //声明一个boolean类型的变量作为发送方的代表
  18. boolean flag=true;
  19. while(true) {
  20. //1.提示输入并使用变量记录
  21. System.out.println("Please enter what "+(flag?"张三":"李四")+" want to send:");
  22. //由手册可知,System.in代表键盘输入,而且是InputStream类型 字节流
  23. //BufferedReader要的参数是字符流,需要转换
  24. String str = br.readLine();
  25. //2.判断用户输入的内容是否为"bye",若是则结束
  26. if ("bye".equals(str)) {
  27. System.out.println("Contact Over!");
  28. break;
  29. }
  30. //3.否则 写入到文件d:/a.txt当中
  31. //获取当前系统时间并调整格式
  32. Date d1=new Date();
  33. SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  34. ps.println(sdf.format(d1)+(flag?"张三说:":"李四说:")+str);
  35. flag=!flag;
  36. }
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. } finally {
  40. //4.关流
  41. if(null!=ps){
  42. ps.close();
  43. }
  44. if(null!=br){
  45. try {
  46. br.close();
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }
  52. }
  53. }

printWriter类

java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流。

OutputStreamWriter类

java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换。


OutputStreamWriter(OutputStream out)

根据参数指定的引用来构造对象

OutputStreamWriter(OutputStream out, String charsetName)

根据参数指定的引用和编码构造对象

void write(String str)

将参数指定的字符串写入

void flush() 刷新流
void close() 用于关闭输出流并释放有关的资源

InputStreamReader类

java.io.InputStreamReader类主要用于实现从字节流到字符流的转换。

字节编码

计算机只能识别二进制数据,早期就是电信号。为了方便计算机可以识别各个国家的文字,就需要将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表,该表就叫做编码表。

  • ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示。
  • ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
  • GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
  • GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。
  • Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。

编码的发展:

  • 面向传输的众多 UTF(UCS Transfer Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所有文化的字符了。
  • Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的Unicode编码是UTF-8.UTF-16。
  • UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

    DataOutputStream类

    java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中。

DataOutputStream(OutputStream out)

根据参数指定的引用构造对象OutputStream类是个抽象类,实参需要传递子类对象

void writeInt(int v)

用于将参数指定的整数一次性写入输出流,优先写入高字节

void close()

用于关闭文件输出流并释放有关的资源
  1. package com.lagou.task17;
  2. import java.io.DataOutputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. /**
  6. * @author lijing
  7. * @date 2020/10/14 15:05
  8. * @description
  9. */
  10. public class DataOutputStreamTest {
  11. public static void main(String[] args) {
  12. DataOutputStream dos = null;
  13. try {
  14. //1.创建dos类型的对象
  15. dos = new DataOutputStream(new FileOutputStream("D:/a.txt"));
  16. //2.准备整数数据并写入输出流
  17. int num=66;
  18. dos.writeInt(num);
  19. System.out.println("写入数据成功");
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. } finally {
  23. //3.关流
  24. if(null!=dos){
  25. try {
  26. dos.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }
  33. }

DateInputStream类

java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据。

  1. package com.lagou.task17;
  2. import java.io.DataInputStream;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. /**
  6. * @author lijing
  7. * @date 2020/10/14 15:12
  8. * @description
  9. */
  10. public class DateInputStreamTest {
  11. public static void main(String[] args) {
  12. DataInputStream dis= null;
  13. try {
  14. //1.创建dis对象
  15. dis = new DataInputStream(new FileInputStream("D:/a.txt"));
  16. //2.从输入流读取整数并打印
  17. int res=dis.readInt();
  18. System.out.println("读取到的整数数据为:"+res);
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. } finally {
  22. //3.关流
  23. if(null!=dis){
  24. try {
  25. dis.close();
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }
  31. }
  32. }

ObjectOutputStream类—需要序列化

  • java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中。
  • 只能将支持 java.io.Serializable 接口的对象写入流中。
  • 类通过实现 java.io.Serializable 接口以启用其序列化功能。
  • 所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。
    |
    ObjectOutputStream(OutputStream out) |
    根据参数指定的引用来构造对象 | | —- | —- | |
    void writeObject(Object obj) |
    用于将参数指定的对象整体写入到输出流中 | |
    void close() |
    用于关闭输出流并释放有关的资源 |
  1. package com.lagou.task17;
  2. import java.io.Serializable;
  3. /**
  4. * @author lijing
  5. * @date 2020/10/14 15:24
  6. * @description
  7. */
  8. public class User implements Serializable {
  9. private static final long serialVersionUID = -3543126975751553657L;
  10. private String userName;
  11. private String password;
  12. private String phoneNum;
  13. public User() {
  14. }
  15. public User(String userName, String password, String phoneNum) {
  16. this.userName = userName;
  17. this.password = password;
  18. this.phoneNum = phoneNum;
  19. }
  20. public String getUserName() {
  21. return userName;
  22. }
  23. public void setUserName(String userName) {
  24. this.userName = userName;
  25. }
  26. public String getPassword() {
  27. return password;
  28. }
  29. public void setPassword(String password) {
  30. this.password = password;
  31. }
  32. public String getPhoneNum() {
  33. return phoneNum;
  34. }
  35. public void setPhoneNum(String phoneNum) {
  36. this.phoneNum = phoneNum;
  37. }
  38. @Override
  39. public String toString() {
  40. return "User{" +
  41. "userName='" + userName + '\'' +
  42. ", password='" + password + '\'' +
  43. ", phoneNum='" + phoneNum + '\'' +
  44. '}';
  45. }
  46. }
  1. package com.lagou.task17;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectOutputStream;
  5. /**
  6. * @author lijing
  7. * @date 2020/10/14 15:25
  8. * @description
  9. */
  10. public class ObjectOutputStreamTest {
  11. public static void main(String[] args) {
  12. ObjectOutputStream oos= null;
  13. try {
  14. //1.创建对象
  15. oos = new ObjectOutputStream(new FileOutputStream("D:/a.txt"));
  16. //2.初始化User对象
  17. User user=new User("name","pass","18815621");
  18. //3.将User类型的对象写入输出流
  19. oos.writeObject(user);
  20. System.out.println("写入成功");
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. } finally {
  24. //4.关流
  25. if(null!=oos){
  26. try {
  27. oos.close();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }
  33. }
  34. }

ObjectInputStream类

  • java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
  • 所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
    ```java package com.lagou.task17;

import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream;

/**

  • @author lijing
  • @date 2020/10/14 15:55
  • @description */ public class ObjectInputStreamTest { public static void main(String[] args) {

    1. ObjectInputStream ois= null;
    2. try {
    3. //1.ois
    4. ois = new ObjectInputStream(new FileInputStream("D:/a.txt"));
    5. //2.从输入流总读取一个对象并打印
    6. Object obj = ois.readObject();
    7. System.out.println("读取到的对象是:" + obj);
    8. } catch (IOException e) {
    9. e.printStackTrace();
    10. } catch (ClassNotFoundException e) {
    11. e.printStackTrace();
    12. } finally {
    13. //3.关流
    14. if(null!=ois){
    15. try {
    16. ois.close();
    17. } catch (IOException e) {
    18. e.printStackTrace();
    19. }
    20. }
    21. }

    } }

  1. **序列化版本号**<br />序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常<br />(InvalidCastException)。<br />**transient:**<br />transientJava语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。<br />**经验分享:**<br />当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,从而避免了通过返回值进行是否达到文件末尾的判断
  2. <a name="gpffW"></a>
  3. ### **RandomAccessFile类**
  4. java.io.RandomAccessFile类主要支持对随机访问文件的读写操作。
  5. | <br />RandomAccessFile(String name, String<br />mode) | <br />根据参数指定的名称和模式构造对象<br />r: 以只读方式打开<br />rw:打开以便读取和写入<br />rwd:打开以便读取和写入,同步文件内容的更新<br />rws:打开以便读取和写入,同步文件内容和元数据的更新 |
  6. | --- | --- |
  7. | <br />int read() | <br />读取单个字节的数据 |
  8. | <br />void seek(long pos) | <br />用于设置从此文件的开头开始测量的文件指针偏移量 |
  9. | <br />void write(int b) | <br />将参数指定的单个字节写入 |
  10. | <br />void close() | <br />用于关闭流并释放有关的资源 |
  11. ```java
  12. package com.lagou.task17;
  13. import java.io.IOException;
  14. import java.io.RandomAccessFile;
  15. import java.util.RandomAccess;
  16. /**
  17. * @author lijing
  18. * @date 2020/10/14 16:06
  19. * @description
  20. */
  21. public class RandomAccessFileTest {
  22. public static void main(String[] args) {
  23. RandomAccessFile ra= null;
  24. try {
  25. //1.创建
  26. ra = new RandomAccessFile("d:/a.txt","rw");
  27. //2.对文件内容随机读写
  28. //设置距离文件开头位置的偏移量,从文件开头位置向后偏移3个字节 aellhello
  29. int read = ra.read();
  30. System.out.println("读取到的单个字符为:"+(char)read);//l
  31. read=ra.read();
  32. System.out.println("读取到的单个字符为:"+(char)read);//l 指向了e
  33. ra.write('2');//执行该行代码后覆盖了原来的'e'
  34. System.out.println("写入成功");
  35. } catch (IOException e) {
  36. e.printStackTrace();
  37. } finally {
  38. //3.关流
  39. if (null!=ra){
  40. try {
  41. ra.close();
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. }
  47. }
  48. }