java学习:IO流

1.什么是IO

I:Input

O:Output

通过IO可以完成文件的读与写

2.IO流的分类

有多种分类方式:

        一种方式是按照流的方向进行方向分类:

                以内存作为参照物,

                往内存种去,叫做输入(Input)。或者叫做读(Read)。

                从内存种出去,叫做输出(Output)。或者叫做写(Write)。

        另一种方式是按照读取数据方式不同进行分类:

                有的流是按照字节的方式读取数据,一次读取一个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件

                有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件存在的,这种流不能读取:图片,声音,视频等文件,只能读取纯文本文件,连word文件都无法读取

综上所述,流的分类

        输入流,输出流

        字节流,字符流

3.java种所有的流都是在java.io.*下。

4.java流这块有四大家族:

四大家族首领:

java.io.InputStream        字节输入流

java.io.OutputStream        字节输出流

java.io.Reader        字符输入流

java.io.Writer        字符输出流

四大家族首领都是抽象类(abstract class)。

所有的流都实现了:

        java.io.Closeable接口,都是可关闭的,都有close()方法。

        流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。用完流之后一定要关闭

所有的输出流都实现了:

        java.io.Flushable接口,都是可刷新的,都有flush()方法。

        输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的元素强行输出完(清空管道),刷新的所用就是清空管道

        如果没有fluash()可能会导致丢失数据。

在java中只要类名以Stream结尾的都是字节流,以Reader/Writer结尾的都是字符流

5.java.io包下需要掌握的流有16个

文件专属

java.io.FileInputStream

java.io.FileOutputStream

java.io.FileReader

java.io.FileWriter

转换流(将字节流转换成字符流)

java.io.InputStreamReader

java.io.OutputStreamWriter

缓冲流专属

java.io.BufferedReader

java.io.BufferedWriter

java.io.BufferedInputStream

java.io.BufferedOutputStream

数据流专属

java.io.DataInputStream

java.io.DataOutputStream

标准输出流

java.io.PrintWriter

java.io.PrintStream

对象专属流

java.io.ObjectInputStream

java.io.ObjectOutputStream

6.java.io.FileInputStream

  1. 文字字节输入流,万能的,任何类型的文件都可以采用这个流来读
  2. 字节的方式,完成输入的操作,完成读的操作(硬盘-->内存)
  3. 创建文件字节输入流对象
    1. 构造方法
      1. FileInputStream(String name)
        1. 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
        2. 使用try..catch语句对语句进行捕捉异常
        3. 在finally语句块当中确保流一定关闭。
        4. 流不是空。流是null的时候没必要关闭
    2. 方法
      1. int read()
        1. 从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞。
        2. 一位一位读取,每调用一次读取一个字节,读到null返回-1
        3. FileInputStream fis = null;
          try{
              fis = new FileInputStream("D:\\temp");
              int readData = 0;
              while((readData = fis.read()) != -1){
                  System.out.println(readData);
              }
          } catch(FileNotFoundException e){
              e.printStackTrace();
          } catch(IOException){
              e.printStackTrace();
          } finally {
              if(fis != null){
                  try{
                      fis.close();
                  } catch(IOException e){
                      e.printStackTrace;
                  }
              }
          }
      2. int read(byte[] b)
        1. 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。在某些输入可用之前,此方法将阻塞。
        2. 减少硬盘和内存之间的交互,提高程序的执行效率,往byte[]数组当中读
        3. 工程Project的根就是IDEA的默认当前路径
        4. 返回读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1
        5. FileInputStream fis = null;
          try{
              fis = new FileInputStream("D:\\temp");
              byte[] bytes = new byte[4];
              int readCount
              while((readCount = fis.read(bytes)) != -1){
                  //String(byte[] bytes,int offset,int length)
                  //通过使用平台的默认字符集解码指定的 byte 子数组,
                  //构造一个新的 String。新 String 的长度是字符集的函数,因此可能不等于该子数组的长度。
                  //bytes - 要解码为字符的 byte
                  //offset - 要解码的第一个 byte 的索引
                  //length - 要解码的 byte 数
                  System.out.print(new String(bytes, 0, readCount));
              }
          } catch(FileNotFoundException e){
              e.printStackTrace();
          } catch(IOException){
              e.printStackTrace();
          } finally {
              if(fis != null){
                  try{
                      fis.close();
                  } catch(IOException e){
                      e.printStackTrace;
                  }
              }
          }
      3. int available()
        1. 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。下一次调用可能是同一个线程,也可能是另一个线程。一次读取或跳过此数量个字节不会发生阻塞,但读取或跳过的字节可能小于该数。
        2. 返回剩余的没有读到的字节数量
      4. long skip(long n)
        1. 从输入流中跳过并丢弃 n 个字节的数据。

7.java.io.FileOutputStream

  1. 字节输出流,负责写
  2. 从内存到硬盘
  3. FileOutputStream(String name)
    1. 创建一个向具有指定名称的文件中写入数据的输出文件流。创建一个新 FileDescriptor 对象来表示此文件连接。
  4. FileOutputStream(String name, boolean append)
    1. 创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。创建一个新 FileDescriptor 对象来表示此文件连接。
  5. void write(byte[] b)
    1. 将 b.length 个字节从指定 byte 数组写入此文件输出流中。
    2. FileOutputStream fos = null;
      try{
          fos = new FileOutputStream("temp");
          byte[] bytes = {97,98,99};
          fos.write(bytes);
          fos.flush();
      } catch(FileNotFoundException e){
          e.printStackTrace();
      } catch(IOException){
          e.printStackTrace();
      } finally {
          if(fos != null){
              try{
                  fos.close();
              } catch(IOException e){
                  e.printStackTrace;
              }
          }
      }
  6. void write(byte[] b, int off, int len)
    1. 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

8.文件的复制

  1. 使用java.io.FileInputStream和java.io.FileOutputStream完成文件拷贝
  2. 拷贝过程:一边读一边写
  3. FileInputStream fis = null;
    FileOutputStream fos = null;
    try{
        //创建输入/输出对象
        fis = new FileInputStream("D:\\temp");
        fos = new FileOutputStream("temp", true);
        //边读边写
        byte[] bytes = new byte[1024 * 1024];  //一次最多拷贝1Mb
        int readCount = 0;
        while((readCount = fis.rade(bytes)) != -1){
            fis.write(bytes,0,readCount);
        }
        fos.flush();
    } catch(FileNotFoundException e){
        e.printStackTrace();
    } catch(IOException){
        e.printStackTrace();
    } finally {
        if(fis != null){
            try{
                fis.close();
            } catch(IOException e){
                e.printStackTrace;
            }
        }
        if(fos != null){
            try{
                fos.close();
            } catch(IOException e){
                e.printStackTrace;
            }
        }
    }
  4. 使用以上字节流拷贝文件的时候,文件类型随意,万能的。什么文件都能拷贝

9.java.io.FileReader

  1. 文件输入流,只能读取普通文本
  2. FileReader reader = null;
    try{
        //创建输入/输出对象
        reader = new FileReader("temp");
        }
        char[] chars = new char[4];  //一次读取4个字符
        int readCount = 0;
        while((readCount = reader.read(chars)) != -1){
            System.out.print(new String(chars, 0, readCount);
        }
    } catch(FileNotFoundException e){
        e.printStackTrace();
    } catch(IOException){
        e.printStackTrace();
    } finally {
        if(reader != null){
            try{
                reader.close();
            } catch(IOException e){
                e.printStackTrace;
            }
        }
    }
  3. 读取文本内容时,比较方便,快捷

10.java.io.FileWriter

  1. 文件输出流,负责写
  2. FileWriter out = null;
    try{
        //创建输入/输出对象
        out = new FileWriter("temp");
        }
        fos.flush();
        char[] chars = {"w","s"};
        out.Write(chars);
        out.flush();
    } catch(FileNotFoundException e){
        e.printStackTrace();
    } catch(IOException){
        e.printStackTrace();
    } finally {
        if(out != null){
            try{
                out.close();
            } catch(IOException e){
                e.printStackTrace;
            }
        }
    }
  3. 只能输出普通文本11.

11.java.io.BufferedReader

  1. 带有缓冲区的字符输入流
  2. 使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。
  3. 当一个流的构造方法中需要一个流的时候,被传进来的流叫做:节点流
  4. 外部负责包装的这个流,叫做:包装流。还有个名字叫:处理流
  5. String readLine()
    1. 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。
  6. FileReader reader = new FileReader("temp");
    BufferedReader br = new BufferedReader(reader);
    String s = null;
    while((s = br.readLine()) != null){
    System.out.println(s);
    }
    br.close();
  7. 对于包装流来说,只需要关闭最外层流就行,里面的节点流回会自动关闭

  8. //InputStreamReader将字节流转换成字符流
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("temp")));
    String line = null;
    while((line = br.readLine()) != null){
        System.out.print(line);
    }
    br.close();
  9. 外部负责包装的这个流,叫做:包装流。还有个名字叫:处理流

12.java.io.BufferedWriter

  1. 带有缓冲的字符输出流

13.java.io.DataOutputStream

  1. 数据专属流
  2. 这个流可以将数据连同数据类型一并写入文件
  3. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
    //写数据
    byte b = 100;
    short s = 200;
    int i = 300;
    long l = 400L;
    float f = 3.0F;
    double d = 3.14;
    boolean sex = false;
    char c = 'a';
    //把数据以及数据类型一并写入到文件夹中
    doc.writeByte(b);
    dos.writeShort(s);
    dos.writeInt(i);
    dos.writeLong(l);
    dos.writeFloat(f);
    dos.writeDouble(d);
    dos.writeBoolean(sex);
    dos.writeChar(c);
    //刷新
    dos.flush();
    //关闭最外层
    dos.close();
    
  4. 这个文件不是普通的文本文档(这个文件使用记事本打不开)

14.java.io.DataInputStream

  1. 数据字节输入流
  2. DataInputStream dis = new DataInputStream(new FileInputStream("data"));
    //开始读
    byte b = dis.readByte();
    short s = dis.readShort();
    int i = dis.readInt();
    long l = dis.readLang();
    float f = dis.readFloat();
    double d = dis.readDouble();
    boolean sex = dis.readBoolean();
    char c = dis.readChar;
    System.out.println(b);
    System.out.println(s);
    System.out.println(i);
    System.out.println(l);
    System.out.println(f);
    System.out.println(d);
    System.out.println(sex);
    System.out.println(c);
    //关闭最外层
    dos.close();
    
  3. DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。读的数据需要和写的数据一致。才可以正常读取

15.java.io.PrintStream

  1. 标准的字节输出流。默认输出到控制台
  2. PrintStream ps = System.out;
    ps.println("hello");
  3. 标准输出流不需要手动close()关闭

  4. //标准输出流不再指向控制台,指向"log"文件
    PrintStream printStream = new PrintStream(new FileOutputStream("log"));
    System.setOut(printStream);
  5. 可以改变标准输出流的输出方向

16.java.io.File类

  1. 文件和目录路径名的抽象表示形式。
  2. File类和四大家族没有关系,所以File类不能完成文件的读和写
  3. 一个对象有可能对应的是目录,也可能是文件
  4. File类的常用方法
    1. File(String pathname)
      1. 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。如果给定字符串是空字符串,那么结果是空抽象路径名。
    2. boolean exists()
      1. 测试此抽象路径名表示的文件或目录是否存在。
      2. 当且仅当此抽象路径名表示的文件或目录存在时,返回 true;否则返回 false
    3. boolean createNewFile()
      1. 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。检查文件是否存在,若不存在则创建该文件,这是单个操作,对于其他所有可能影响该文件的文件系统活动来说,该操作是不可分的。
      2. 如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在,则返回 false

    4. boolean mkdir()
      1. 创建此抽象路径名指定的目录。
      2. 当且仅当已创建目录时,返回 true;否则返回 false
    5. boolean mkdirs()
      1. 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。注意,此操作失败时也可能已经成功地创建了一部分必需的父目录。
      2. 当且仅当已创建目录以及所有必需的父目录时,返回 true;否则返回 false
    6. String getParent()
      1. 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null
    7. File getParentFile()
      1. 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null
    8. String getAbsolutePath()
      1. 返回此抽象路径名的绝对路径名字符串。
    9. String getName()
      1. 返回由此抽象路径名表示的文件或目录的名称。该名称是路径名名称序列中的最后一个名称。如果路径名名称序列为空,则返回空字符串。
    10. boolean isDirectory()
      1. 测试此抽象路径名表示的文件是否是一个目录。
    11. long lastModified()
      1. 返回此抽象路径名表示的文件最后一次被修改的时间。
      2. 返回:表示文件最后一次被修改的时间的 long 值,用与时间点(1970 年 1 月 1 日,00:00:00 GMT)之间的毫秒数表示;如果该文件不存在,或者发生 I/O 错误,则返回 0L
    12. long length()
      1. 返回由此抽象路径名表示的文件的长度。如果此路径名表示一个目录,则返回值是不确定的。
    13. File[] listFiles()
      1. 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
      2. 获取当前目录下的所有子文件

18.拷贝目录

public static void main(String[] args){
    //拷贝源
    File srcFile = new File("D:\\Data")
    //拷贝目标
    File destFile = new File("C:\\");
    //调用方法拷贝
    copyDir(srcFile, desFile);
}

private static void copyDir(file srcFile, file desFile){
    if(srcFile.isFile()){
        FileInputStream in = null;
        FileOutputStream out = null;
        try{
            in = new FileInputStream(srcFile);
            String path = destFile.getAbsolutepath().endWith("\\") ? 
                             destFile.getAbsolutepath() : destFile.getAbsolutepath() +                 
                             "\\" + srcFile.getAbsolutepath().substring(3);
            out = new FileOutputStream()
            byte[] bytes = new byte[1024 * 1024];  //一次复制1Mb
            int readCount = 0;
            while((readCount = in.read(bytes)) != -1){
                out.write(bytes, 0, readCount);
            }
            out.flush();
        }
        } catch(FileNotFoundException e){
            e.printStackTrace();
        } catch(IOException){
            e.printStackTrace();
        } finally {
            if(out != null){
                try{
                    out.close();
                } catch(IOException e){
                    e.printStackTrace;
                }
            }
            if(in != null){
                try{
                    in.close();
                } catch(IOException e){
                    e.printStackTrace;
                }
            }
        }
        //srcFile是一个文件的话递归结束
        return;
    }
    //获取源下面的子目录
    File[] files = srcFile.listFiles();
    for(File file : files){
        if(file.isDirectory()){
            String srcDir = file.getAbsolutepath();
            String desDie = destFile.getAbsolutepath().endWith("\\") ? 
                             destFile.getAbsolutepath() : destFile.getAbsolutepath() +                 
                             "\\" + srcDir.substring(3);
            file newFile = new File(des.Dir);
            if(!newFile.exists()){
                newFile.mkdirs();
            }
        }
            copyDir(srcFile, desFile);
    }
}

19.序列化和反序列化

20.序列化

  1. //创建java对象
    Student s = new Student(111, "zhangsan");
    //序列化
    ObjectOutPutStream oos = new ObjectOutputStream(new FileOutPutStream("students"));
    //序列化对象
    oos.writeObject(s);
    //刷新
    oos.flush();
    //关闭
    oos.close();
  2. 参与序列化和反序列化的对象,必须实现Serializable接口
  3. 通过源代码发现Serializable接口只是一个标志接口
    1. public interface Serializable{
      }
    2. 这个接口当中什么也没有

    3. 起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个这个接口,可能会对这个类进行特殊待遇

    4. Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号

  4. 序列化版本号

    1. java语言中只采用什么机制来区分类

      1. 首先通过类名进行比对,如果类名不一样,肯定不是同一个类

      2. 如果类名一样,靠序列版本号进行区分

    2. 这种自动化生成序列化版本号缺陷

      1. 一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会编译,此时会省成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类

    3. 凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类

    4. 建议把序列化版本号手动的写出来,不建议生成

      1. private static final long serialVersionUID = 1L;

21.反序列化

  1. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
    //开始反序列化,读
    Object obj = ois.readObject();
    //反序列化学生对象
    System.out.println(obj);
    ois.clos();

22.序列化多个对象

  1. List<User> userList = new ArrayList<>();
    userList.add(new User(1, "zhangsan");
    userList.add(new User(2, "lisi");
    userList.add(new User(3, "wangwu");
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
    //序列化一个集合
    oos.writeObject(userList);
    oos.flush();
    oos.close();
  2. 参与序列化的ArrayList集合以及集合中的元素User都需要实现java.io.Serializable接口

  3. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
    List<User> userList = (List<User>)ois.readObject();
    for(User user : userList){
        System.out.println(user);
    }
    ois.clos();
  4. transient关键字标识游离的,不参与序列化

22.IO + Properties联合使用

  1. IO流:文件的读和写
  2. Properties:是一个Map集合,key和value都是String类型
  3. //新建一个输入流对象
    FileReader reader = new FileReader("data");
    //新建一个Map集合
    Properties pro = new Properties();
    //调用Propertise对象的load方法将数据文件加载到Map集合中
    pro.load(reader);//文件中的数据顺着管道加载到Map集合中,其中等号左边作key,有边作value
    String usrname = pro.getProperty("username");
    System.out.println(username);
  4. 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启。就可以拿到动态的信息。

  5. 类似于以上机制的文件被称为配置文件

  6. 并且当配置文件中的内容格式是:"key=value"的时候,我们把这种配置文件叫做属性配置文件

  7. java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的

  8. 这种以.properties结尾的文件在java中被称为:属性配置文件。

  9. 其中Properties是专门存放属性配置文件内容的一个类

  10. 属性配置文件

    1. 注释是"#"

    2. 属性配置文件的key重复的话,value会自动覆盖

    3. 建议key和value使用=方式,=左边是key,=右边是value

    4. 最好不要有空格

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值