【Java SE】12. IO流

12.1 File类的使用

  1. 构造器:

    File类对象可以是文件,也可以是目录

    1. public File(String pathname)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储

      1. 绝对路径: 是一个固定的路径,从盘符开始
      2. 相对路径: 是相对于某个位置开始
      File file = new File("hello.txt");
      //注意:如果你创建文件或者文件目录没有写盘符路径, 那么默认在项目路径下
      
    2. public File(String parent,String child)以parent为父路径, child为子路径创建File对象

      File file1 = new File("E:", "aaa");
      
    3. public File(File parent,String child)根据一个父File对象和子文件路径创建File对象

      File file2 = new File(file, "bbb.txt");
      

      注意:此时都没有考虑在硬盘中真实存在这些文件,只是生成内存层面的对象而已

  2. 路径分隔符:

    1. 路径中的每级目录之间用一个路径分隔符隔开

    2. 路径分隔符和系统有关:

      1. windows和DOS系统默认使用“\”来表示
      2. UNIX和URL使用“/”来表示
      3. Java程序支持跨平台运行,因此路径分隔符要慎用
    3. 为了解决这个隐患, File类提供了一个常量:
      public static final String separator。根据操作系统,动态的提供分隔符

      File file = new File("hello.txt");//相对路径
      File file1 = new File("E:\\1_课件\\dbcp.txt");//绝对路径 windows
      File file2 = new File("E:/1_课件/dbcp.txt");//Unix和URL
      File file3 = new File("E:"+File.separator+"1_课件"+File.separator+"dbcp.txt");//通用
      
  3. 常用方法:

    1. File类的获取功能

      1. String getAbsolutePath(): 获取绝对路径

      2. String getPath(): 获取路径

      3. String getName(): 获取名称

      4. String getParent(): 获取上层文件目录路径,若无,返回null

      5. long length(): 获取文件长度(即:字节数),不能获取目录的长度。

      6. long lastModified(): 获取最后一次的修改时间, 毫秒值

      7. String[] list(): 获取指定目录下的所有文件或者文件目录的名称数组

        File file = new File("D:\\DriverGenius");
        String[] list = file.list();
        
        for (String s:list
             ) {
            System.out.println(s);
        }
        //kadblock kcdpt kplanet ksoft log 
        
      8. File[] listFiles(): 获取指定目录下的所有文件或者文件目录的File数组

        File file = new File("D:\\DriverGenius");
        File[] files = file.listFiles();
        
        for (File f:files
             ) {
            System.out.println(f);
        }
        //D:\DriverGenius\kcdpt
        //D:\DriverGenius\kplanet
        //D:\DriverGenius\ksoft
        
    2. File类的重命名功能

      1. boolean renameTo(File dest):把文件重命名为指定的文件路径,file1移动到file2指定位置,且改名为指定名

        File file1 = new File("D:\\hi.txt");
        File file2 = new File("D:\\hh.txt");
        boolean b = file1.renameTo(file2);
        

        要想返回true,需要file1一定是存在的,且file2不能存在(执行完file1会消失,如果再执行一次,会返回false)

    3. File类的判断功能

      1. boolean isDirectory(): 判断是否是文件目录

      2. boolean isFile() : 判断是否是文件

      3. boolean exists() : 判断是否存在

      4. boolean canRead() : 判断是否可读

      5. boolean canWrite(): 判断是否可写

      6. boolean isHidden() : 判断是否隐藏

        在这里插入图片描述

    4. File类的创建功能

      1. boolean createNewFile(): 创建文件。 若文件存在, 则不创建, 返回false
      2. boolean mkdir(): 创建文件目录。 如果此文件目录存在, 就不创建了;如果此文件目录的上层目录不存在, 也不创建
      3. boolean mkdirs() : 创建文件目录。 如果输入路径中上层文件目录不存在, 一并创建
    5. File类的删除功能

      1. boolean delete(): 删除文件或者文件夹
      2. 删除注意事项:
        1. Java中的删除不走回收站
        2. 要删除一个文件目录, 该文件目录内不能包含文件或者文件目录(空才能删)
    6. 并未涉及到写入或者读取文件内容的操作,这些操作需要使用IO流来完成

    7. 后续File类的对象会作为参数传递到流的构造器中,指明读取或写入的”终点“

12.2 IO流原理及流的分类

  1. Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行

  2. Java IO 原理:

    1. 输入input: 读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中

    2. 输出output: 将程序(内存)数据输出到磁盘、光盘等存储设备中

      在这里插入图片描述

  3. 流的分类:

    1. 操作数据单位不同分为: 字节流(8bit-byte),字符流(16bit-char)

    2. 按数据流的流向不同分为: 输入流,输出流

    3. 流的角色的不同分为: 节点流,处理流(已有流的外边包的一层,如加速作用)

      在这里插入图片描述

    4. 流的**抽象基类**:

      字节流字符流
      输入流InputStreamReader
      输出流OutputStreamWriter
  4. IO流体系:

    在这里插入图片描述

12.3 节点流(文件流-开发中使用少)

  1. 读入操作:

    1. 示例:

      //1.实例化File对象,指明要操作的文件
      //读入的文件一定要存在,否则FileNotFoundException
      File file1 = new File("D:\\hh.txt");
      
      //2.提供具体的流
      FileInputStream fileInputStream = new FileInputStream(file1);
      
      //3.数据的读入
      //⭐read():返回下一个字符的byte,如果到达文件末尾,则返回-1
      int read = fileInputStream.read();
      while (read != -1){
          System.out.print((char) read);//helloworld
          read = fileInputStream.read();
      }
      
      //4.关闭流
      fileInputStream.close();
      
    2. 一定要关闭流:

      垃圾回收机制只回收JVM堆内存里的对象空间。对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力

    3. 异常处理优化:为了保证流关闭的处理一定会被执行不使用throws抛异常,而使用try/catch

      FileInputStream fileInputStream = null;
      try {
          
          File file1 = new File("D:\\hh.txt");
          
          fileInputStream = new FileInputStream(file1);
      
          int read = fileInputStream.read();
          while (read != -1){
              System.out.print((char) read);//helloworld
              read = fileInputStream.read();
          }
      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          try {
          		//一定会执行关闭操作
              fileInputStream.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      
    4. 使用FileReader优化:不再一个个字符读,改为一次性读入5个字符

      FileReader fileReader = null;
      try {
          File file1 = new File("D:\\hh.txt");
          fileReader = new FileReader(file1);
      
          char[] charsBuffer = new char[5];
          //⭐read(char[] charsBuffer):
          //返回每次读入charsBuffer数组中的字符的个数,到达末尾返回-1
          int read;
          while ((read = fileReader.read(charsBuffer)) != -1){
              //错误写法:
              /*for (int i = 0; i < charsBuffer.length; i++) {
                  System.out.print(charsBuffer[i]);//helloworld123ld
                  //最后一次数组中存着:123ld
              }*/
      
              //正确写法:
              for (int i = 0; i < read; i++) {
                  //read:返回本次读入的字符的个数
                  System.out.print(charsBuffer[i]);//helloworld123
              }
          }
      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          try {
              fileReader.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      
  2. 写出操作:

    FileWriter fileWriter = null;
    try {
        File file1 = new File("D:\\hh.txt");
        fileWriter = new FileWriter(file1);
        //如果文件不存在:会自动创建该文件
        //但如果目录不存在:并不会自动造目录
    
        //如果文件不存在:
        //1.若FileWriter构造器的参数append为false则对文件进行覆盖
        fileWriter.write("aaa\n");
        fileWriter.write("123");
    
        //2.若FileWriter构造器的参数append为true则对文件进行追加操作
        /*FileWriter fileWriter1 = new FileWriter(file1,true);
          fileWriter1.write("456");*/
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  3. 使用FileReader与FileReader对文本文件实现复制操作

    文本文件(.txt、.java、.c、.cpp)只能使用字符流操作,因为如果有中文的话,字节数组中放不下中文编码(使用字节流处理文本文件,仅限于复制文件的操作(不会在内存中转换显示)

    FileReader fileReader = null;
    FileWriter fileWriter = null;
    try {    
    	File file1 = new File("D:\\hh.txt");    
    	File file2 = new File("D:\\h2.txt");    
    	fileReader = new FileReader(file1);    
    	fileWriter = new FileWriter(file2);    
    	char[] charsBuffer = new char[5];    
    	int read;    
    	while ((read = fileReader.read(charsBuffer)) != -1){        
        	fileWriter.write(charsBuffer,0,read);    
    	}
    } catch (IOException e) {    
    	e.printStackTrace();
    } 
    finally {    
    	try {        
        	if(fileReader != null)            
            	fileReader.close();    
    	} catch (IOException e) {        
        	e.printStackTrace();    
    	}        
    	try {        
        	if(fileWriter != null)            
            	fileWriter.close();    
    	} catch (IOException e) {        
        	e.printStackTrace();    
    	}
    }
    
  4. 实现复制图片的操作:

    图片是字节文件,(非文本文件,如视频图片)不能使用FileReader和FileWriter操作,要使用字节流fileInputStream和fileOutputStream,且charsBuffer数组要是字节类型的(byte [ ])

    FileInputStream fileInputStream = null;
    FileOutputStream fileOutputStream = null;
    try {
        File file1 = new File("D:\\123.png");
        File file2 = new File("D:\\111.png");
    
        fileInputStream = new FileInputStream(file1);
        fileOutputStream = new FileOutputStream(file2);
    
        byte[] charsBuffer = new byte[1024];
        //缓存数组大小通常写为1024
        
        int read;
        while ((read = fileInputStream.read(charsBuffer)) != -1){
            fileOutputStream.write(charsBuffer,0,read);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if(fileInputStream != null)
                fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        try {
            if(fileOutputStream != null)
                fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

12.4 缓冲流(处理流1-提高速度)

  1. 缓冲流:提高流的读取、写入的速度

    1. BufferedInputStream
    2. BufferedOutputStream
    3. BufferedReader
    4. BufferedWriter
  2. 复制非文本文件示例:

    BufferedInputStream bufferedInputStream = null;
    BufferedOutputStream bufferedOutputStream = null;
    try {
        File file1 = new File("D:\\123.png");
        File file2 = new File("D:\\133.png");
        
        FileInputStream fileInputStream = new FileInputStream(file1);
        FileOutputStream fileOutputStream = new FileOutputStream(file2);
    
        bufferedInputStream = new BufferedInputStream(fileInputStream);
        bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        
        byte[] bytes = new byte[1024];
        int read;
        while ((read = bufferedInputStream.read(bytes)) != -1 ){
            bufferedOutputStream.write(bytes,0,read);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //要求:先关外层,再关外层
        try {
            bufferedInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        try {
            bufferedOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //说明:外层流关闭时,内层流也会自动进行关闭,故可以省略关闭内层
        //fileInputStream.close();
        //fileOutputStream.close();
    }
    
  3. 速度提高的原因:

    1. 内部提供了一个缓存区

    2. public class BufferedInputStream extends FilterInputStream {
      
          private static int DEFAULT_BUFFER_SIZE = 8192;
          
          public BufferedInputStream(InputStream in) {
              this(in, DEFAULT_BUFFER_SIZE);
          }
      

      在这里插入图片描述

  4. 处理流就套接在已有流的基础上的

    bufferedInputStream = new BufferedInputStream(fileInputStream);
    bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
    
  5. 文本文件的复制操作示例:

    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        File file1 = new File("D:\\h2.txt");
        File file2 = new File("D:\\h3.txt");
        
        FileReader fr= new FileReader(file1);
        FileWriter fw = new FileWriter(file2);
    
        br = new BufferedReader(fr);
        bw = new BufferedWriter(fw);
        
        /*
        //方式一:
        char[] bytes = new char[1024];
        int read;
        while ((read = br.read()) != -1 ){
            bw.write(bytes,0,read);
        }*/
    
        //方式二:⭐一行一行读
        String data;
        while ((data = br.readLine()) != null){
            bw.write(data);//data中不好含换行符
            bw.newLine();//换行
        }
        
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //要求:先关外层,再关外层
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //说明:外层流关闭时,内层流也会自动进行关闭,故可以省略关闭内层
        //fileInputStream.close();
        //fileOutputStream.close();
    }
    
  6. 例题:

    1. 实现图片的加密:(解密同理)

      byte[] bytes = new byte[1024];
      int read;
      while ((read = bufferedInputStream.read(bytes)) != -1 ){
          for (int i = 0; i < read; i++) {
              bytes[i] = (byte) (bytes[i]^5);
          }
          bufferedOutputStream.write(bytes,0,read);
      }
      
    2. 统计文本上每个字符的个数:

      FileReader fr = null;
      BufferedWriter bw = null;
      try {
          //1.创建Map集合
          Map<Character, Integer> map = new HashMap<Character, Integer>();
      
          //2.遍历每一个字符,每一个字符出现的次数放到map中
          fr = new FileReader("D:\\h2.txt");
          int c = 0;
          while ((c = fr.read()) != -1) {
              //int 还原 char
              char ch = (char) c;
              // 判断char是否在map中第一次出现
              if (map.get(ch) == null) {
                  map.put(ch, 1);
              } else {
                  map.put(ch, map.get(ch) + 1);
              }
          }
      
          //3.把map中数据存在文件count.txt
          //3.1 创建Writer
          bw = new BufferedWriter(new FileWriter("D:\\word.txt"));
      
          //3.2 遍历map,再写入数据
          Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
          for (Map.Entry<Character, Integer> entry : entrySet) {
              switch (entry.getKey()) {
                  case ' ':
                      bw.write("空格=" + entry.getValue());
                      break;
                  case '\t'://\t表示tab 键字符
                      bw.write("tab键=" + entry.getValue());
                      break;
                  case '\r'://
                      bw.write("回车=" + entry.getValue());
                      break;
                  case '\n'://
                      bw.write("换行=" + entry.getValue());
                      break;
                  default:
                      bw.write(entry.getKey() + "=" + entry.getValue());
                      break;
              }
              bw.newLine();
          }
      } catch (IOException e) {
          e.printStackTrace();
      } finally {
          //4.关流
          if (fr != null) {
              try {
                  fr.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
          if (bw != null) {
              try {
                  bw.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      

12.5 转换流(处理流2-转换字符与字节)

  1. 转换流:

    1. FileInputStream:将一个字节输入流转换为字符输入流
    2. InputStreamReader:将一个字符输出流转换成字节输出流
  2. 作用:提供字节流与字符流之间的转换

    在这里插入图片描述

  3. 在读入文件时,使用UTF-8字符集,在写出文件时,使用GBK字符集

     File file1 = new File("D:\\h2.txt");
     File file2 = new File("D:\\h4.txt");
    
     FileInputStream fis = new FileInputStream(file1);
     FileOutputStream fos = new FileOutputStream(file2);
    
     //参数1:字节流;参数2:使用系统默认的字符集
     InputStreamReader isr = new InputStreamReader(fis,"utf-8");
     //参数2,指定字符集,具体使用哪个字符集,取决于文件保存时使用的字符集
     //InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
    
     //输出时:使用gbk字符集将字节转化成字符
     OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
    
     char[] chars = new char[1024];
     int len;
     while ((len = isr.read()) != -1){
     	osw.write(chars,0,len);
     }
    
     isr.close();
     osw.close();
    
  4. 常见的编码表

    1. ASCII: 美国标准信息交换码:用一个字节的7位可以表示

    2. ISO8859-1:拉丁码表,欧洲码表用一个字节的8位表示

    3. GB2312: 最初的中文编码表,最多两个字节编码所有字符

    4. GBK: 中文编码表升级,融合了更多的中文文字符号。最多两个字节编码

    5. Unicode: 国际标准码, 融合了目前人类使用的所有字符。为每个字符分配唯一的字符码,所有的文字都用两个字节来表示,是对UTF-8、UCS-2/UTF-16等具体编码方案的统称而已,并不是具体的编码方案

    6. UTF-8: 变长的编码方式,可用1-4个字节来表示一个字符,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位

      在这里插入图片描述

12.6 标准输入、输出流(处理流3)

  1. System:

    1. PrintStream System.er:The “standard” error output stream
    2. InputStream System.in:The “standard” input stream,标准输入流,默认从键盘输入
    3. PrintStream System.out:The “standard” output stream,标准输出流,默认从控制台输出
  2. System类的setIn(InputStream is)setOut(PrintStream ps)方式重新指定输入和输出的方式

  3. 练习题:

    从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序

    BufferedReader br = null;
     try {
     	InputStreamReader isr = new InputStreamReader(System.in);
     	br = new BufferedReader(isr);
    
     	while (true){
         	String data = br.readLine();
         	if(data.equalsIgnoreCase("e") || data.equalsIgnoreCase("eexit")){
             	System.out.println("程序结束");
             	break;
         	}
    
         	String toUpperCase = data.toUpperCase(Locale.ROOT);
         	System.out.println(toUpperCase);
     	}
     } catch (IOException e) {
     	e.printStackTrace();
     } finally {
     	try {
         	br.close();
     	} catch (IOException e) {
         	e.printStackTrace();
     	}
     }
    

12.7 打印流

  1. 实现将基本数据类型的数据格式转化为字符串输出

  2. 打印流: PrintStream和PrintWriter

    1. 提供了一系列重载的print()println()方法,用于多种数据类型的输出
    2. PrintStream和PrintWriter的输出不会抛出IOException异常
    3. PrintStream和PrintWriter有自动flush功能
    4. PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
    5. System.out返回的是PrintStream的实例
  3. 示例:

    PrintStream ps = null;
     try {
     	FileOutputStream fos = new FileOutputStream(new File("D:\\text.txt"));
     
     	// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
     	ps = new PrintStream(fos, true);
     	if (ps != null) {
         	System.setOut(ps);// 把标准输出流(控制台输出)改成文件
     	}
     
     	for (int i = 0; i <= 255; i++) { // 输出ASCII字符
         	System.out.print((char) i);
         	if (i % 50 == 0) { // 每50个数据一行
             	System.out.println(); // 换行
         	}
     	}
     } catch (FileNotFoundException e) {
     	e.printStackTrace();
     } finally {
     	if (ps != null) {
         	ps.close();
     	}
     }
    

12.8 数据流

  1. (用于读取和写出基本数据类型、 String类的数据

  2. 数据流有两个类:DataInputStream 和 DataOutputStream

  3. 分别“套接”在 InputStream 和 OutputStream 子类的流上

    1. DataInputStream中的方法

      boolean readBoolean()

      char readChar()

      double readDouble()

      long readLong()

      String readUTF()

      byte readByte()

      float readFloat()

      short readShort()

      int readInt()

      void readFully(byte[] b)

    2. DataOutputStream中的方法

      将上述的方法的read改为相应的write即可

  4. 示例:

    将文件中存储的基本数据类型或字符串读取到内存中,保存在变量中

    或将内存中的变量的内容,持久化到文件当中

    //写入
    DataOutputStream dos = null;
    try { // 创建连接到指定文件的数据输出流对象
        dos = new DataOutputStream(new FileOutputStream("destData.dat"));
        dos.writeUTF("我爱北京天安门"); // 写UTF字符串
        dos.writeBoolean(false); // 写入布尔值
        dos.writeLong(1234567890L); // 写入长整数
        System.out.println("写文件成功!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally { // 关闭流对象
        try {
            if (dos != null) {
            // 关闭过滤流时,会自动关闭它包装的底层节点流
                dos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    //读出(读的顺序按写入的顺序来)
    DataInputStream dis = null;
    try {
        dis = new DataInputStream(new FileInputStream("destData.dat"));
        String info = dis.readUTF();
        boolean flag = dis.readBoolean();
        long time = dis.readLong();
        System.out.println(info);
        System.out.println(flag);
        System.out.println(time);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (dis != null) {
            try {
                dis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

12.9 对象流

  1. 可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回

  2. 序列化:用ObjectOutputStream类(将内存中的Java对象保存到磁盘中,或通过网络传输出去)
    反序列化: 用ObjectInputStream类(将磁盘中的对象还原为内存中的Java对象)

  3. 对象的序列化机制:

    1. 允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点

    2. 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

    3. 不能序列化static和transient(不想让某属性可序列化时使用)修饰的成员变量

    4. 对象所属的类一定是可序列化的

      1. 需要该类实现Serializable接口
      2. 需要该类提供一个常量serialVersionUID:若对象序列化后,所属类若类做了修改, 且没有定义serialVersionUID,则在反序列化中,对象就找不到自己的所属类了
      3. 该类内部的所有属性也是可序列化的(基本数据类型默认都是可序列化的)
      public class Person implements Serializable {
         
          public static final long serialVersionUID = 4687687868L;
          
          private Accont accont;//Accont类也需要是可序列化的
          
      }
      
  4. 序列化:

    ObjectOutputStream oos = null;
    try {
        FileOutputStream fos = new FileOutputStream("D:\\h2.txt");
        
        oos = new ObjectOutputStream(fos);
        oos.writeObject(new String("AAA"));
        oos.flush();
    
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  5. 反序列化:

    ObjectInputStream ois = null;
    try {
        FileInputStream fis = new FileInputStream("D:\\h2.txt");
        
        ois = new ObjectInputStream(fis);
    
        Object o = ois.readObject();
        String str = (String) o;
        System.out.println(str);//AAA
    
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        try {
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

12.10 随机存取文件流

  1. RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类

  2. 实现了DataInputDataOutput这两个接口,也就意味着这个类既可以读也可以写

  3. 支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件

    1. 支持只访问文件的部分内容
    2. 可以向已存在的文件后追加内容
  4. 复制示例:

    RandomAccessFile raf1 = null;
    RandomAccessFile raf2 = null;
    try {
        raf1 = new RandomAccessFile(new File("D:\\h2.txt"),"r");
        raf2 = new RandomAccessFile(new File("D:\\h5.txt"),"rw");
    
        byte[] bytes = new byte[1024];
        int len;
        while ((len = raf1.read(bytes)) != -1){
            raf2.write(bytes,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        ...
    }
    
  5. 如果RandomAccessFile作为输出流时:

    1. 写出到的文件不存在,则在执行过程中自动创建
    2. 写出到的文件存在,则对源文件进行覆盖(默认从开始处覆盖)
    RandomAccessFile raf = new RandomAccessFile(new File("D:\\h2.txt"), "rw");
    
    raf.write("xyz".getBytes(StandardCharsets.UTF_8));
    
    raf.close();
    
    1. RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置,可以自由移动记录指针:void seek(long pos): 将文件记录指针定位到 pos 位置

      RandomAccessFile raf = new RandomAccessFile(new File("D:\\h2.txt"), "rw");
      
      //1.将指针调到角标为1的位置,从1开始覆盖
      //raf.seek(1);
      
      //2.实现末尾追加
      //raf.seek(raf.length());
      
      //3.实现中间插入的效果
      //stringBuilder用于保存插入位置之后的内容
      StringBuilder stringBuilder = new StringBuilder((int) new File("D:\\h2.txt").length());
      //读出插入位置之后的内容
      byte[] bytes = new byte[1024];
      int len;
      while ((len = raf.read(bytes)) != -1){
          stringBuilder.append(new String(bytes,0,len));
      }
      //写入目标内容
      raf.seek(3);
      raf.write("xyz".getBytes(StandardCharsets.UTF_8));
      //写入stringBuilder中保存的内容
      raf.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8));
      
      raf.close();
      

12.11 NIO.2中Path、Paths、Files类的使用

  1. Java NIO (New IO, Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。 NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。 NIO将以更加高效的方式进行文件的读写操作

  2. JDK 7对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2

  3. Path、 Paths和Files核心API

    1. 早期只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且, 大多数方法在出错时仅返回失败,并不会提供异常信息

    2. 为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。 Path可以看成是File类的升级版本,实际引用的资源也可以不存在

    3. NIO.2在java.nio.file包下还提供了Files、 Paths工具类, Files包含了大量静态的工具方法来操作文件; Paths则包含了两个返回Path的静态工厂方法。

      Paths 类提供的静态 get() 方法用来获取 Path 对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值