确保文件中字符的字符集和字符流的字符集是一致
//确保文件中字符的字符集和字符流的字符集是一致的,否则可能产生乱码。 private static void right1() throws IOException { char[] chars = new char[10]; String content = ""; try (FileInputStream fileInputStream = new FileInputStream("hello.txt"); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, Charset.forName("GBK"))) { int count; while ((count = inputStreamReader.read(chars)) != -1) { content += new String(chars, 0, count); } } log.info("result: {}", content); }
Files类流式处理注意事项
//注意1:(使用JDK1.7推出的Files类的readAllLines方法,此种方法读取超出内存大小的大文件时会出现OOM,源码中读到的内容放在List<String>中返回,内存无法容纳就会OOM) log.info("result: {}", Files.readAllLines(Paths.get("hello.txt"), Charset.forName("GBK")).stream().findFirst().orElse("")); //注意2:使用Files类静态方法(lines方法返回的Stream<String>,按需读取到内存)进行文件操作(注意释放文件句柄),此种方法需要释放资源(关闭文件) LongAdder longAdder = new LongAdder(); IntStream.rangeClosed(1, 1000000).forEach(i -> { try { Files.lines(Paths.get("demo.txt")).forEach(line -> longAdder.increment()); } catch (IOException e) { e.printStackTrace(); } }); log.info("total : {}", longAdder.longValue()); //注意3:使用try-with-resources配合,确保流的close方法可以调用释放资源(同理Files类中其他返回Stream包装对象的方法流式处理也需要释放资源) LongAdder longAdder = new LongAdder(); IntStream.rangeClosed(1, 1000000).forEach(i -> { try (Stream<String> lines = Files.lines(Paths.get("demo.txt"))) { lines.forEach(line -> longAdder.increment()); } catch (IOException e) { e.printStackTrace(); } }); log.info("total : {}", longAdder.longValue()); //注意5:Java的File类和Files类提供的文件复制、重命名、删除等操作,都不是原子性的 //因为线程不安全,并且文件类操作基本都是调用操作系统本身的API,一般来说这些文件API并不像数据库有事务机制(很难办到)文档中有提到。
读写文件需要考虑设置缓冲区
//文件IO处理,使用合适的缓冲区提高性能,使批量读写减少IO次数 //额外使用一个8KB缓冲,再使用BufferedInputStream和BufferedOutputStream private static void bufferedStreamBufferOperation() throws IOException { try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("src.txt")); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("dest.txt"))) { byte[] buffer = new byte[8192]; int len = 0; while ((len = bufferedInputStream.read(buffer)) != -1) { bufferedOutputStream.write(buffer, 0, len); } } } //ns % Task name //117807808 007% bufferedStreamBufferOperation
FileChannel流转发
//对于类似的文件复制操作,如果希望有更高性能,可以使用 FileChannel 的 transfreTo 方法进行流的复制。 //在一些操作系统(比如高版本的 Linux 和 UNIX)上可以实现 DMA(直接内存访问), //也就是数据从磁盘经过总线直接发送到目标文件,无需经过内存和 CPU 进行数据中转: private static void fileChannelOperation() throws IOException { FileChannel in = FileChannel.open(Paths.get("src.txt"), StandardOpenOption.READ); FileChannel out = FileChannel.open(Paths.get("dest.txt"), CREATE, WRITE); in.transferTo(0, in.size(), out); }
注意事项
//文件操作因为涉及操作系统和文件系统的实现,JDK 并不能确保所有 IO API 在所有平台的逻辑一致性; //因此、代码迁移到新的操作系统或文件系统时,要重新进行功能测试和性能测试。