> 文章列表 > java File和IO流处理

java File和IO流处理

java File和IO流处理

java File和IO流处理

  • 1、File
    • 1.1.、创建File对象
    • 1.2、判断文件类型、获取文件信息
    • 1.3、创建文件、删除文件
    • 1.4、遍历文件夹
  • 2、方法递归
    • 2.1、认识递归形式
    • 2.2、应用、执行流程、算法思想
    • 2.3、文件搜索
  • 3、字符
    • 3.1、常见字符集介绍
    • 3.2、字符集的编码、解码操作
  • 4、IO流
    • 4.1、认识IO流
    • 4.2、应用场景
    • 4.3、如何学习IO流
  • 5、IO流-字节
    • 5.1、文件字节输入流-FilelnputStream
    • 5.2、文件字节输出流-FileOutputStream
    • 5.3、案例-文件复制
  • 6、释放资源的方式
    • 6.1、try-catch-finally
    • 6.2、try-with-resource
  • 7、IO流-字符流
    • 7.1、文件字符输入流-FileReader
    • 7.2、文件字符输出流-FileWriter
  • 8、IO流-缓冲流
    • 8.1、字节缓冲流
    • 8.2、字符缓冲流
      • 8.2.1、字符缓冲输入流-bufferReader
      • 8.2.2、字符缓冲输出流-bufferWriter
    • 8.3、原始流、缓冲流的性能分析(重点)
  • 9、IO流-转换流
    • 9.1、字符输入转换流-InputStreamReader
    • 9.2、字符输出转换流-OutPutWriter
  • 10、IO流-打印流
    • 10.1、字节打印流-PrintStream
    • 10.2、字符打印流-PrintWriter
    • 10.3、打印流的应用
  • 11、IO流-数据流
    • 11.1、数据输出流-DataOutputStream
    • 11.2、数据输入流-DataInputStream
  • 12、IO流-序列化流
    • 12.1、对象字节输出流-ObjectOutputStream
    • 12.2、对象字节输入流-ObjectInputStream
  • 13、补充知识:IO框架

代码地址:https://gitee.com/hippoDocker/java-study/tree/fileAndIo

1、File

File是java.io.包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)。

作用:通过File类可以获取文件信息(大小、文件名、修改时间等)、判断文件类型、创建文件\\文件夹、删除文件\\文件夹等。

注意:File只能对文件本身进行操作,不能读写文件里面存储的数据。

1.1.、创建File对象

创建File对象

注意:File对象路径可以指定文件也可以是绝对路径(文件夹)或相对路径,当然也可以指向不存在的路径和文件

// 创建File对象,指代某个具体的文件(绝对路径:从盘符开始)
File f1 = new File("D:\\\\Txl\\\\Desktop\\\\text.txt");
File f2 = new File("D:/Txl/Desktop/text.txt");
File f3 = new File("D:" + File.separator + "Txl" + File.separator + "Desktop" + File.separator + "text.txt");
// 创建File对象,指代某个具体的文件(相对路径:不带盘符,默认直接到当前工程下目录开始寻找)
File f4 = new File("study-commons/java-file-iostream/src/main/resources/file/abc.txt");/**
* 注意:路径中的斜杠有三种方式:
* 顺斜杠(单斜杠):`D:/Txl/Desktop/test.txt`  
* 反斜杠(双斜杠):`D:\\\\Txl\\\\Desktop\\\\test.txt`
* 自动取系统分隔符:File.separator
**/

1.2、判断文件类型、获取文件信息

判断当前文件对象,对应的文件路径是否存在

// 2.判断当前文件对象,对应的文件路径是否存在,并返回true
System.out.println(f.exists());

判断当前文件对象指代的是否是一个文件

// 3.判断当前文件对象指代的是否是一个文件,并返回true
System.out.println(f.isFile());

判断当前文件对象指代的是否是一个目录

// 4.判断当前文件对象指代的是否是一个目录,并返回true
System.out.println(f.isDirectory());

获取文件名称

// 5.获取文件名称(包含后缀)
System.out.println(f.getName());

获取文件大小

注意:如果file为文件夹,取的是文件夹本身的大小,不包含文件夹里面的内容。

// 6.获取文件大小,返回字节个数
System.out.println(f.length());

获取文件最后修改时间

// 7.获取文件最后修改时间,返回毫秒值
System.out.println(f.lastModified());

获取创建文件对象时,指定的文件路径

// 8.获取创建文件对象时,指定的文件路径
System.out.println(f.getPath());

获取文件的绝对路径

// 9.获取文件的绝对路径
System.out.println(f.getAbsolutePath());

1.3、创建文件、删除文件

创建一个新文件(文件内容为空)

// 根据一个不存的文件创建File对象,调用createNewFile()创建文件
File f5 = new File("study-commons/java-file-iostream/src/main/resources/file/a.txt");
System.out.println(f5.createNewFile());

用于创建文件夹

注意:只能创建一级文件夹

/*** 通过指定路径新建File对象,然后调用mkdir()方法,创建文件夹* `/aaa`一级路径不存在*/
File f6 = new File("study-commons/java-file-iostream/src/main/resources/file/aaa");
System.out.println(f6.mkdir());

用于创建文件夹

注意:可以创建多级文件夹

/*** 通过指定路径新建File对象,然后调用mkdirs()方法,创建文件夹* `/bbb/ccc`多级级路径不存在*/
File f7 = new File("study-commons/java-file-iostream/src/main/resources/file/aaa/bbb/ccc");
System.out.println(f7.mkdirs());

删除文件或者空文件夹

注意:不能删除非空文件夹,里面只有文件夹也不行

/*** 通过指定路径新建File对象,然后调用delete()方法,删除文件或者空文件夹* `/aaa`路径下有`/bbb/ccc`存在不能删除,只能删除空文件夹`ccc`*/
File f8 = new File("study-commons/java-file-iostream/src/main/resources/file/aaa");
System.out.println(f8.delete());

1.4、遍历文件夹

获取指定目录下的所有文件和文件夹的名称,返回String数组

注意:这里的所有文件和文件夹指的是一级文件名称,子文件夹里面的文件不能获取到。

// 1.获取指定目录下的所有文件和文件夹的名称,返回String数组
File f9 = new File("study-commons/java-file-iostream/src/main/resources/file");
String[] names = f9.list();
for (String name : names) {System.out.print(name+",");
}

获取指定目录下的所有文件和文件夹的File对象,返回File数组

注意:这里的所有文件和文件夹指的是一级文件File对象,子文件夹里面的文件不能获取到。

使用listFiles方法时的注意事项:
当主调是文件,或者路径不存在时,返回null
当主调是空文件夹时,返回一个长度为0的数组
当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
当主调是一个文件夹,但是没有权限访问该文件夹时,返回null

// 2.获取指定目录下的所有文件和文件夹的File对象,返回File数组
File[] files = f9.listFiles();
for (File file : files) {System.out.print(file+",");
}

2、方法递归

2.1、认识递归形式

什么是方法递归?

1、递归是一种算法,在程序设计语言中广泛应用。
2、从形式上说:方法调用自身的形式称为方法递归( recursion)。

递归形式

直接递归:方法自己调用自己。
间接递归:方法调用其他方法,其他方法又回调方法自己。

2.2、应用、执行流程、算法思想

栈内存

递归算法三要素:

1、递归的公式: 一个方法就是一个函数,例如f(n) = f(n-1) *n;
2、递归的终结点: f(1);
3、递归的方向必须走向终结点。

2.3、文件搜索

需求:从D盘中,输入某个文件名称,找到后输出它的位置。

分析:
1、先找出D盘下所有一级目录;
2、遍历所有一级目录是否有该文件;
3、如果是文件,就判断是否是我们寻找的文件;
4、如果不是文件就重复上面操作,直到找出文件。

/*** TODO 递归搜索文件* @param dir 搜索的目录* @param fileName 搜索的文件名*/
public static void searchFile(File dir, String fileName) {// 1.获取当前目录下的所有文件和目录File[] files = dir.listFiles();if(files != null && files.length > 0) {// 2.遍历files数组for (File file : files) {// 3.判断file是否是一个目录if (file.isDirectory()) {// 4.如果是目录,继续递归搜索searchFile(file, fileName);} else {// 5.如果是文件,判断文件名是否和搜索的文件名相同if (file.getName().equals(fileName)) {// 6.如果相同,输出文件的绝对路径System.out.println(file.getAbsolutePath());}}}}
}

3、字符集

3.1、常见字符集介绍

标准ASCALL字符集
1、ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文、符号等。

2、标准ASCII使用1个字节存储一个字符首尾是0,总共可表示128个字符,对美国佬来说完全够用。

GBK(汉字内码扩展规范,国标)
1、汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储
2、注意:GBK兼容了ASCII字符集。
3、GBK规定:汉字的第一个字节的第一位必须是1。

Unicode字符集(统一码,也叫万国码)
1、Unicode是国际组织制定的,可以容纳世界上所有文字、符号的字符集。

UTF32:4个字节,由于太占用空间,被淘汰升级为UTF-8

UTF-8
1、是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节;
2、英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节

UTF-8编码方式(二进制)
0xxxxxxx (ASCALL码)
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

例子:a我m:01100001 || 110 001000 010001 || 01101101

注意

所有程序开发都应该使用UTF-8编码。
字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码。

总结
1、ASCII字符集:只有英文、数字、符号等,占1个字节。
2、GBK字符集:汉字占2个字节,英文、数字占1个字节。
3、UTF-8字符集:汉字占3个字节,英文、数字占1个字节。

3.2、字符集的编码、解码操作

Java代码完成对字符的编码

String提供以下方法 说明
byte[] getBytes() 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName) 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

Java代码完成对字符的解码

String提供了如下方法 说明
String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的 String
String(byte[] bytes,String charsetName) 通过指定的字符集解码指定的字节数组来构造新的 String
// TODO 字符集编码解码
// 1.编码:字符串-->字节数组
// 2.解码:字节数组-->字符串
// 3.编码和解码的字符集要一致,否则会出现乱码
// 1.编码:字符串-->字节数组
String s = "你好";
byte[] bytes1 = s.getBytes();// 默认使用工程的字符集(UTF-8)进行编码
byte[] bytes2 = s.getBytes("GBK");// 使用指定字符集(GBK)进行编码
System.out.println("UTF-8编码:"+Arrays.toString(bytes1));
System.out.println("GBK编码:"+Arrays.toString(bytes2));
// 2.解码:字节数组-->字符串
String s1 = new String(bytes1);// 默认使用工程的字符集(UTF-8)进行解码
String s2 = new String(bytes2, "GBK"); // 使用指定字符集(GBK)进行解码
System.out.println("UTF-8解码:"+s1);
System.out.println("GBK解码:"+s2);

4、IO流

作用:用于读写数据(可以读写文件,或者网络中的数据…)

4.1、认识IO流

IO流概述
1、I指Input,称为输入流:负责把数据读到内存中去(磁盘->程序)
2、О指Output,称为输出流:负责写数据出去(程序->磁盘)

4.2、应用场景

文件存储、文件读取、游戏本地数据、复制粘贴、对话交流等。

4.3、如何学习IO流

1、先搞清楚I0流的分类、体系。
2、再挨个学习每个IO流的作用、用法。

IO流的分类
按流的方向划分:IO流:1、输入流;2、输出流。
按流中数据的最小单位划分:IO流:1、字节流;2、字符流。

字节流:适合操作所有类型的文件(比如:音频、视频、图片、文本文件的复制转移等)
字符流:只适合操作存文本文件(比如:读写txt、java等文件)

适合操作所有类型的文件(IO流体系-java.io包)
字节流:字节输入流、字节输出流
字符流:字符输入流、字符输出流
(抽象类-实现类)
字节输入流(InputStream-FileInputStream):以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
字节输出流(OutPutStream-FileOutputStream):以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
字符输入流(Reader-FileReader):以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流。
字符输出流(Writer-FileWriter):以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。

5、IO流-字节流

字节流:适合复制文件等,不适合读写文本文件

5.1、文件字节输入流-FilelnputStream

FilelnputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。

构造器 说明
public FileInputStream(File file) 创建字节输入流管道与源文件接通
public FileInputStream(String pathName) 创建字节输入流管道与源文件接通
方法名称 说明
public int read() 读取一个字节返回,如果发现没有数据可读返回-1
public int read(byte[] buffer) 每次用一个字节数组去读取数据,返回字节数组读取了多少个字节,
发现没有数据可读返回-1
package com.hippo.javaiostream.iostream;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;/**
* @ClassName FileInputStreamOpt
* @Description TODO 文件字节输入流操作
* @Author tangxl
* @create 2023-04-06 14:40
**/
public class FileInputStreamOpt {public static void main(String[] args) throws IOException {// TODO 文件输入流操作// 1.文件字节输入流:每次读取一个字节// 2.文件字节输入流:每次读取多个字节// 3.文件字节输入流:一次性读取所有字节// 1.文件字节输入流:每次读取一个字节// 1.1 创建文件字节输入流对象InputStream in1 = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");// 1.1 每次读取一个字节int b11 = in1.read();System.out.println("b1="+(char)b11);int b12 = in1.read();System.out.println("b2="+(char)b12);int b13 = in1.read();System.out.println("b3="+(char)b13);// 1.2 循环读取单个字节int b1;while ((b1 = in1.read()) != -1) {System.out.println("b1="+(char)b1);}// 1.3 关闭资源in1.close();//每次读取一个字节,读取数据性能慢// 读取汉字(两个字节)会乱码!!!// 2.文件字节输入流:每次读取多个字节// 2.1 创建文件字节输入流对象InputStream in2 = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");byte[] bytes1 = new byte[3]; // 每次读取3个字节int lenth1 = in2.read(bytes1);System.out.println("lenth1="+lenth1);System.out.println("bytes1="+new String(bytes1, 0, lenth1, "UTF-8"));// 2.2 循环读取多个字节int lenth2; // 每次读取的字节数byte[] bytes2 = new byte[3]; // 每次读取3个字节while ((lenth2 = in2.read(bytes2)) != -1) { // 每次读取3个字节,返回读取的字节数,当读取到文件末尾时,返回-1System.out.println("lenth2="+lenth2);System.out.println("bytes2="+new String(bytes2, 0, lenth2, "UTF-8"));}// 3.关闭资源in2.close();//性能得到了明显的提升!!//这种方案也不能避免读取汉字输出乱码的问题!!// 3.文件字节输入流:一次性读取所有字节// 3.1 创建文件字节输入流对象InputStream in3 = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");// 3.2 一次性读取所有字节
//        int available = in3.available();// 获取文件字节输入流的可用字节数
//        byte[] bytes3 = new byte[available]; // 创建一个字节数组,长度为文件字节输入流的可用字节数
//        int lenth3 = in3.read(bytes3);
//        System.out.println("lenth3="+lenth3);
//        System.out.println("bytes3="+new String(bytes3, 0, lenth3, "UTF-8"));byte[] bytes3 = in3.readAllBytes(); // JDK9新增的方法,一次性读取所有字节System.out.println("bytes3="+new String(bytes3, "UTF-8"));// 3.3 关闭资源in3.close();// 一次性读取所有字节,性能最好!!// 但是,如果文件过大,会导致内存溢出!!}
}

问题:使用read(),每次读取一个字节,读取数据性能慢,读取汉字(两个字节)会乱码。
使用read(byte[] byte),每次读取多个字节,性能可以得到提升,但是也不能解决汉字乱码问题。
使用readAllByte(),一次性读取所有字节,性能最好,但是文件太大会导致内存溢出。

5.2、文件字节输出流-FileOutputStream

FileOutputStream(文件字节输出流)
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。

构造器 说明
public FileOutputstream(File file) 创建字节输出流管道与源文件对象接通
public FileOutputstream( String filepath) 创建字节输出流管道与源文件路径接通
public FileOutputstream(File file, boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
public FileOutputStream(String filepath,boolean append) 创建字节输出流管道与源文件路径接通,可追加数据
方法名称 说明
public void write(int a) 写一个字节出去
public void write(byte[ ] buffer) 写一个字节数组出去
public void write(byte[ ] buffer , int pos , int len) 写一个字节数组的一部分出去
public void close() throws IOException 关闭流
package com.hippo.javaiostream.iostream;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;/*** @ClassName FileOutputStreamOpt* @Description TODO 文件字符输出流操作* @Author tangxl* @create 2023-04-06 15:47**/
public class FileOutputStreamOpt {public static void main(String[] args) throws IOException {// TODO 文件字符输出流操作// 1.文件字节输出流:写入单个字节// 2.文件字节输出流:写入字节数组// 3.文件字节输出流:写入字节数组的一部分// 1.文件字节输出流:写入单个字节// 1.1 创建FileOutputStream对象,构造方法中绑定要写入数据的目的地// 覆盖写入OutputStream os1 = new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/b1.txt");// 追加写入OutputStream os2 = new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/b2.txt", true);// 1.2 每次写入一个字节os1.write(97);// 97对应的字符是aos1.write('b');// bos1.write('小'); // 汉字小两个字节,每次写入一个字节,会乱码// 1.3 写入字节数组byte[] bytes = "我是中国人".getBytes();os2.write(bytes);// 写入字节数组os2.write("\\r\\n".getBytes());// 写入换行,所有操作系统都支持的换行符`\\r\\n`os2.write(bytes,0, bytes.length);// 从0开始写入,写入bytes.length个字节os2.write("\\r\\n".getBytes());// 写入换行// 1.3 释放资源os1.close();}
}

5.3、案例-文件复制

将a.txt所在目录下创建一个a文件夹,将a.txt复制到子路径下/a下。

package com.hippo.javaiostream.iostream;import java.io.*;/*** @ClassName CopyFile* @Description TODO 文件复制* @Author tangxl* @create 2023-04-06 16:11**/
public class CopyFile {public static void main(String[] args) {String srcPath = "study-commons/java-file-iostream/src/main/resources/file";System.out.println("复制是否成功:"+copyFile(srcPath, srcPath + File.separator + "copy", "a.txt"));}/*** TODO 文件复制* @param srcPath 源文件路径* @param destPath 目标文件路径* @param fileName 文件名* @return*/public static boolean copyFile(String srcPath, String destPath, String fileName){// 判断目标文件夹是否存在,不存在则创建File destFile = new File(destPath);if(!destFile.exists()){destFile.mkdirs();}// 复制文件,try()中的资源会自动关闭try(InputStream is = new FileInputStream(srcPath+ File.separator + fileName);OutputStream os = new FileOutputStream(destPath + File.separator + fileName)){byte[] buffer = new byte[1024];int len = 0;while ((len = is.read(buffer)) != -1) {os.write(buffer, 0, len);}return true;}catch (Exception e) {e.printStackTrace();return false;}}
}

6、释放资源的方式

6.1、try-catch-finally

finally代码区的特点:无论try中的程序是正常执行了或者要return了,还是出现了异常,最后都一定会执行finally区,除非JVM终止
作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)

注意:千万不要在finally中return数据!!!

// 1.使用try(){}catch(){}语句块
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");
os = new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/c.txt");
// 文件字节流操作。。。。
} catch (Exception e) {e.printStackTrace();
}finally {
System.out.println("finally");
// 释放资源
if (is != null) {try {is.close();} catch (IOException e) {throw new RuntimeException(e);}
}
if (os != null) {try {os.close();} catch (IOException e) {throw new RuntimeException(e);}
}
}

6.2、try-with-resource

JDK 7开始提供了更简单的资源释放方案:try-with-resource
try括号里面只能放资源对象(流对象等)。
哪些是资源对象:资源都会去实现接口AutoCloseable接口

// 格式
try (定义资源1;定义资源2;...) {
// 使用资源,可能会抛出异常
} catch (异常类 e) {
异常处理代码
}
// 2.使用try-with-resources语句块
try (InputStream is1 = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");
OutputStream os1 = new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/c.txt")) {
// 文件字节流操作。。。。
} catch (Exception e) {
e.printStackTrace();
}

7、IO流-字符流

字符流:适合读写文本文件内容

7.1、文件字符输入流-FileReader

作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。

构造器 说明
public FileReader(File file) 创建字符输入流管道与源文件接通
public FileReader(String pathname) 创建字符输入流管道与源文件接通
方法名称 说明
public int read() 每次读取一个字符返回,如果发现没有数据可读会返回-1
public int read(char[] buffer) 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1
package com.hippo.javaiostream.iostream;import java.io.FileReader;
import java.io.Reader;/*** @ClassName FileReaderOpt* @Description TODO 文件字符输入流操作* @Author tangxl* @create 2023-04-06 18:11**/
public class FileReaderOpt {public static void main(String[] args) {// TODO 文件字符输入流操作// 1.文件字符输入流:读取单个字符// 2.文件字符输入流:读取字符数组// 3.文件字符输入流:读取字符数组的一部分// 1.文件字符输入流:读取单个字符// 1.1 创建FileReader对象,构造方法中绑定要读取的数据源// 1.2 使用FileReader对象中的方法read读取文件// 1.3 自动释放资源try(Reader fr = new FileReader("study-commons/java-file-iostream/src/main/resources/file/a.txt")){int len = 0; // 每次读取的有效字符个数while ((len = fr.read()) != -1) { // 每次读取一个字符,返回读取的字符,当读取到文件末尾时,返回-1System.out.print((char)len);}}catch (Exception e) {e.printStackTrace();}// 2.文件字符输入流:读取字符数组// 2.1 创建FileReader对象,构造方法中绑定要读取的数据源// 2.2 使用FileReader对象中的方法read读取多个字符到字符数组中// 2.3 自动释放资源try(Reader fr = new FileReader("study-commons/java-file-iostream/src/main/resources/file/a.txt")){char[] buffer = new char[1024]; // 存储读取到的多个字符int len = 0; // 每次读取的有效字符个数while ((len = fr.read(buffer)) != -1) { // 每次读取多个字符,返回读取的字符个数,当读取到文件末尾时,返回-1System.out.print(new String(buffer, 0, len));}}catch (Exception e) {e.printStackTrace();}}
}

7.2、文件字符输出流-FileWriter

作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。

构造器 说明
public Filewriter(File fild) 创建字节输出流管道与源文件对象接通
public Filewriterl( string filepath) 创建字节输出流管道与源文件路径接通
public Filewriter(File file,boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
public Filewriter(string filepath,boolean append) 创建字节输出流管道与源文件路径接通,可追加数据
方法名称 说明
void write(int c) 写一个字符
void write(String str) 写—个字符串
void write(String str, int off, int len) 写一个字符串的一部分
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void flush() throws IOException 刷新流,就是将内存中缓存的数据立即写到文件中去生效!
public void close() throws IOException 关闭流的操作,包含了刷新!
package com.hippo.javaiostream.iostream;import java.io.FileWriter;/**
* @ClassName FileWriterOpt
* @Description TODO 文件字符输出流操作
* @Author tangxl
* @create 2023-04-06 18:23
**/
public class FileWriterOpt {public static void main(String[] args) {// TODO 文件字符输出流操作// 1.文件字符输出流:写入单个字符// 2.文件字符输出流:写入字符数组// 3.文件字符输出流:写入字符数组的一部分// 1.文件字符输出流:写入单个字符// 1.1 创建FileWriter对象,构造方法中绑定要写入数据的目的地// 1.2 使用FileWriter对象中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)// 1.3 使用FileWriter对象中的方法flush,把内存缓冲区中的数据,刷新到文件中// 1.4 释放资源(会先把内存缓冲区中的数据刷新到文件中)try(FileWriter fw = new FileWriter("study-commons/java-file-iostream/src/main/resources/file/a.txt",true)){fw.write(97); // 写入单个字符fw.write('a'); // 写入单个字符fw.write('林'); // 写入单个字符fw.write('\\r');fw.write('\\n');// 写入换行符fw.flush(); // 刷新缓冲区}catch (Exception e) {e.printStackTrace();}// 2.文件字符输出流:写入字符数组// 2.1 创建FileWriter对象,构造方法中绑定要写入数据的目的地// 2.2 使用FileWriter对象中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)// 2.3 使用FileWriter对象中的方法flush,把内存缓冲区中的数据,刷新到文件中// 2.4 释放资源(会先把内存缓冲区中的数据刷新到文件中)try(FileWriter fw = new FileWriter("study-commons/java-file-iostream/src/main/resources/file/a.txt",true)){char[] buffer = {'a', 'b', 'c', 'd', 'e','\\r','\\n'}; // 字符数组fw.write(buffer); // 写入字符数组fw.write(buffer, 1, 3); // 写入字符数组的一部分,从索引1开始,写入3个字符fw.write("\\r\\n");// 写入换行符fw.flush(); // 刷新缓冲区}catch (Exception e) {e.printStackTrace();}// 3.文件字符输出流:写入字符串// 3.1 创建FileWriter对象,构造方法中绑定要写入数据的目的地// 3.2 使用FileWriter对象中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)// 3.3 使用FileWriter对象中的方法flush,把内存缓冲区中的数据,刷新到文件中// 3.4 释放资源(会先把内存缓冲区中的数据刷新到文件中)try(FileWriter fw = new FileWriter("study-commons/java-file-iostream/src/main/resources/file/a.txt",true)){String str = "林青霞";fw.write(str); // 写入字符串fw.write("\\r\\n");// 写入换行符fw.write(str, 1, 2); // 写入字符串的一部分,从索引1开始,写入2个字符fw.flush(); // 刷新缓冲区}catch (Exception e) {e.printStackTrace();}}
}

注意事项:字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效

字节流、字符流的使用场景小结:
字节流适合做一切文件数据的拷贝(音视频,文本)﹔字节流不适合读取中文内容输出。
字符流适合做文本文件的操作(读,写)。

8、IO流-缓冲流

作用:对原始流进行包装,以提高原始流读写数据的性能

8.1、字节缓冲流

字节缓冲输入流-bufferedInputStream
字节缓冲输出流-bufferedOutputStream

作用:提高’字节流读写数据的性能。
原理:字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池。

构造器 说明
public BufferedInputstream(InputStream is) 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能
public BufferedOutputStream(Outputstream os) 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能
package com.hippo.javaiostream.iostream;import java.io.*;/**
* @ClassName BufferInputStreamOpt
* @Description TODO 字节缓冲流操作
* @Author tangxl
* @create 2023-04-06 19:08
**/
public class BufferedInputStreamOpt {public static void main(String[] args) {// TODO 字节缓冲流操作try (InputStream is = new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt");// 1.定义一个字节缓冲输入流包装原始的字节输入流BufferedInputStream bis = new BufferedInputStream(is);// 缓冲区大小默认8KBOutputStream os = new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/b.txt");// 2.定义一个字节缓冲输出流包装原始的字节输出流OutputStream bos = new BufferedOutputStream(os,8*1024);// 缓冲区大小默认8KB){byte[] buffer = new byte[1024];// 字节数组int len = 0; // 每次读取的有效字节个数while ((len = bis.read(buffer)) != -1) {// 通过字节缓冲输入流读取文件bos.write(buffer, 0, len);// 通过字节缓冲输出流写入文件}}catch (Exception e) {e.printStackTrace();}}
}

8.2、字符缓冲流

8.2.1、字符缓冲输入流-bufferReader

作用:自带8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。

新增功能:整行读取。

构造器 说明
public BufferedReader(Reader r) 把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能
方法 说明
public string readLine() 读取一行数据返回,如果没有数据可读了,会返回null
package com.hippo.javaiostream.iostream;import java.io.BufferedReader;
import java.io.FileReader;/**
* @ClassName BufferReader
* @Description TODO 字符缓冲输入流
* @Author tangxl
* @create 2023-04-06 19:09
**/
public class BufferedReaderOpt {public static void main(String[] args) {// 字符缓冲输入流try(// 创建字符缓冲输入流对象BufferedReader br = new BufferedReader(new FileReader("study-commons/java-file-iostream/src/main/resources/file/a.txt"));) {// 读取数据String line = null;// 记录读取到的一行数据while ((line = br.readLine()) != null) {// 循环读取,每次读取一行数据,直到读取到null为止System.out.println(line);// 输出读取到的数据}} catch (Exception e) {e.printStackTrace();}}
}

8.2.2、字符缓冲输出流-bufferWriter

作用:自带8K的字符缓冲池,可以提高字符输出流写字符数据的性能。

新增功能:换行

构造器 说明
public Bufferediriter(Writer n) 把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能
方法 说明
public void newLine() 换行
package com.hippo.javaiostream.iostream;import java.io.BufferedWriter;
import java.io.FileWriter;/**
* @ClassName BufferWriter
* @Description TODO 字符缓冲输出流
* @Author tangxl
* @create 2023-04-06 19:09
**/
public class BufferedWriterOpt {public static void main(String[] args) {// 字符缓冲输出流try(// 创建字符缓冲输出流对象BufferedWriter bw = new BufferedWriter(new FileWriter("study-commons/java-file-iostream/src/main/resources/file/a.txt",true));) {// 写数据bw.write("hello");bw.newLine();bw.write("world");bw.newLine();bw.write("java");} catch (Exception e) {e.printStackTrace();}}
}

8.3、原始流、缓冲流的性能分析(重点)

测试用例:
分别使用原始的字节流,以及字节缓冲流复制一个很大视频。

测试步骤:
1、使用低级的字节流按照一个一个字节的形式复制文件。
2、使用低级的字节流按照字节数组的形式复制文件。
3、使用高级的缓冲字节流按照一个一个字节的形式复制文件。
4、使用高级的缓冲字节流按照字节数组的形式复制文件。

// 代码省略。。。

9、IO流-转换流

不同偏码读取出现乱码的问题!
如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码!

9.1、字符输入转换流-InputStreamReader

作用:解决不同编码时,字符流读取文本内容乱码的问题。

解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流这样字符输入流中的字符就不乱码了。

构造器 说明
public InputStreamReader(InputStream is) 把原始的字节输入流,按照代码默认编码转成字符输入流(与直接用FileReader的效果一样)
public InputStreamReader(InputStream is,String charset) 把原始的字节输入流,按照指定字符集编码转成字符输入流(重点)
package com.hippo.javaiostream.iostream;import java.io.FileInputStream;
import java.io.InputStreamReader;/*** @ClassName InputStreamReaderOpt* @Description TODO 字符输入转换流操作类* @Author tangxl* @create 2023-04-07 08:37**/
public class InputStreamReaderOpt {public static void main(String[] args) {// 字符输入转换流, 读取文件时,需要指定编码表,否则会出现乱码try (// 创建字符输入转换流对象,指定编码表(`utf-8`, `gbk`, `gb2312`)InputStreamReader isr = new InputStreamReader(new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt"), "GBK");){int len = 0;char[] chs = new char[1024];while ((len = isr.read(chs)) != -1) {System.out.print(new String(chs, 0, len));}}catch (Exception e) {e.printStackTrace();}}
}

9.2、字符输出转换流-OutPutWriter

作用:可以控制写出去的字符使用什么字符集编码。
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。

需要控制’写出去的’字符使用什么字符集编码,该咋整?
1、调用String提供的getBytes方法解决?
2、使用”字符输出转换流”实现。

构造器 说明
public OutputStreamwriter(OutputStream os) 可以把原始的字节输出流,按照代码默认编码转换成字符输出流。
public OutputStreamwriter(OutputStream os,String charset) 可以把原始的字节输出流,按照指定编码转换成字符输出流(重点)
package com.hippo.javaiostream.iostream;import java.io.FileOutputStream;
import java.io.OutputStreamWriter;/**
* @ClassName OutputStreamWriterOpt
* @Description TODO 字符输出转换流操作类
* @Author tangxl
* @create 2023-04-07 08:35
**/
public class OutputStreamWriterOpt {public static void main(String[] args) {// 字符输出转换流try(// 创建字符输出转换流对象,指定编码表(`utf-8`, `gbk`, `gb2312`)OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt",true), "UTF-8");) {// 写数据osw.write("你好中国");osw.write("world");osw.write("java");} catch (Exception e) {e.printStackTrace();}}
}

10、IO流-打印流

PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去,内部实现了缓存流。

10.1、字节打印流-PrintStream

构造器 说明
public PrintStream( Outputstream/File/String) 打印流直接通向字节输出流/文件/文件路径
public PrintStream( String fileName,charset charset) 可以指定写出去的字符编码
public PrintStream(Outputstream out, boolean autoFlush) 可以指定实现自动刷新
public PrintStream(OutputStream out,boolean autoFlush,string encoding) 可以指定实现自动刷新,并可指定字符的编码
方法 说明
public void printin(Xxx xx) 打印任意类型的数据出去
public void write(int/byte[]/byte[]一部分) 可以支持写字节数据出去
package com.hippo.javaiostream.iostream;import java.io.PrintStream;/*** @ClassName PrintStreamOpt* @Description TODO 打印输出流* @Author tangxl* @create 2023-04-07 08:55**/
public class PrintStreamOpt {public static void main(String[] args) {try (// 1.创建打印流管道PrintStream ps = new PrintStream("study-commons/java-file-iostream/src/main/resources/file/a.txt","UTF-8");){// 2.打印数据ps.println(97);ps.println("hello");ps.println(true);// 3.写入数据ps.write(97);}catch (Exception e) {e.printStackTrace();}}
}

10.2、字符打印流-PrintWriter

构造器 说明
public Printwriter(outputstream/writer/File/String) 打印流直接通向字节输出流/文件/文件路径
public Printwriter( string fileName,Charset charset) 可以指定写出去的字符编码
public Printwriter(outputstream out/Writer,boolean autoFlush) 可以指定实现自动刷新
public PrintMiriter(OutputStream out, boolean autoF1ush,String encoding) 可以指定实现自动刷新,并可指定字符的编码
方法 说明
public void print1n(Xxx xx) 打印任意类型的数据出去
public void write(int/string/char[]/…) 可以支持写字符数据出去
package com.hippo.javaiostream.iostream;import java.io.PrintWriter;/**
* @ClassName PrintWriterOpt
* @Description TODO 字符打印流操作类
* @Author tangxl
* @create 2023-04-07 09:11
**/
public class PrintWriterOpt {public static void main(String[] args) {try(// 1.创建打印流管道PrintWriter pw = new PrintWriter("study-commons/java-file-iostream/src/main/resources/file/a.txt","UTF-8");){// 2.打印数据pw.println(97);pw.println("你好中国");pw.println(true);// 3.写入数据pw.write(97);}catch (Exception e) {e.printStackTrace();}}
}

PrintStream和PrintWriter的区别
1、打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
2、PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
3、PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。

10.3、打印流的应用

输出语句重定向:可以把输出语句的打印位置改到某个文件中去。
实现:修改系统打印流,将数据打印到指定文件中去

package com.hippo.javaiostream.iostream;import java.io.*;/*** @ClassName PrintStreamOrWriterApplication* @Description TODO 打印流的应用* @Author tangxl* @create 2023-04-07 09:29**/
public class PrintStreamOrWriterApplication {public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {System.out.println("Java开发技术高,");System.out.println("编程语言普及广。");System.out.println("代码简洁易懂懂,");System.out.println("面向对象优点多。");System.out.println("Java程序员辛勤,");System.out.println("工作细致又周到。");try(// 1.创建打印流管道PrintStream ps = new PrintStream(new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt",true),true,"UTF-8");) {// 修改系统打印流,可以将数据输出到指定的文件中System.setOut(ps);System.out.println("每日写代码累累,");System.out.println("但是收获也颇多。");System.out.println("先进的编程语言,");System.out.println("强大的框架体系。");System.out.println("Java程序员智慧,");System.out.println("编程技术日益增。");System.out.println("开发效率高又快,");System.out.println("代码质量优质。");System.out.println("Java程序员专业,");System.out.println("技术实力必然强。");}catch (Exception e) {e.printStackTrace();}}
}

11、IO流-数据流

11.1、数据输出流-DataOutputStream

作用:允许把数据和其类型一并写出去。
注意:数据并不是乱码,只是这种写入的数据不是拿来看的,需要使用数据输入流(DataInputStream)读取数据。

构造器 说明
public Dataoutputstream(outputstream out) 创建新数据输出流包装基础的字节输出流
方法 说明
public final void writeByte(int v) throws IOException byte类型的数据写入基础的字节输出流
public final void writeInt(int v) throws IOException int类型的数据写入基础的字节输出流
public final void writeDouble(Double v) throws IOException double类型的数据写入基础的字节输出流
public final void writeUTF(String str) throws IOException 字符串数据以UTF-8编码成字节写入基础的字节输出流
public void write(int/byte[]/byte[]一部分) 支持写字节数据出去
package com.hippo.javaiostream.iostream;import java.io.DataOutputStream;
import java.io.FileOutputStream;/*** @ClassName DataOutputStreamOpt* @Description TODO 字节数据输出流操作类* @Author tangxl* @create 2023-04-07 10:13**/
public class DataOutputStreamOpt {public static void main(String[] args) {try(// 创建一个数据字节输出流对象DataOutputStream dos = new DataOutputStream(new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt",true));) {dos.writeInt(97);dos.writeDouble(3.14);dos.writeUTF("你好中国");dos.writeBoolean(true);}catch (Exception e) {e.printStackTrace();}}
}

11.2、数据输入流-DataInputStream

作用:用于读取数据输出流写出去的数据。
注意:读取数据时,数据类型要与写入的数据类型一致,常用于通信数据读取写入。

构造器 说明
public DataInputStream( Inputstream is) 创建新数据输入流包装基础的字节输入流
方法 说明
Public final byte readByte( ) throws IOException 读取字节数据返回
public final int readInt() throws IOException 读取int类型的数据返回
public final double readDouble() throws IOException 读取double类型的数据返回
public final string readUTF() throws IOException 读取字符串数(UTF-8)据返回
public int readInt( )/read(byte[]) 支持读字节数据进来
package com.hippo.javaiostream.iostream;import java.io.DataInputStream;
import java.io.FileInputStream;/*** @ClassName DataInputStreamOpt* @Description TODO 数据输入流操作类* @Author tangxl* @create 2023-04-07 10:18**/
public class DataInputStreamOpt {public static void main(String[] args) {try (// 创建一个数据字节输入流对象DataInputStream dis = new DataInputStream(new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt"));){// 读取数据, 读取的顺序要与写入的顺序一致int i = dis.readInt();System.out.println(i);double d = dis.readDouble();System.out.println(d);String s = dis.readUTF();System.out.println(s);boolean b = dis.readBoolean();System.out.println(b);}catch (Exception e) {}}
}

12、IO流-序列化流

对象序列化:把Java对象写入到文件中去
对象反序列化:把文件里的Java对象读出来

12.1、对象字节输出流-ObjectOutputStream

作用:可以把Java对象进行序列化:把Java对象存入到文件中去。
注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable)

构造器 说明
public objectoutputStream(outputstream out) 创建对象字节输出流,包装基础的字节输出流
方法 说明
public final void write0bject(object o) throws IOException 把对象写出去
package com.hippo.javaiostream.iostream;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;/**
* @ClassName ObjectOutputStreamOpt
* @Description TODO 对象字节输出流操作类
* @Author tangxl
* @create 2023-04-07 10:38
**/
public class ObjectOutputStreamOpt {public static void main(String[] args) {try(// 创建一个对象字节输出流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt"));) {// 创建一个学生对象Student s = new Student(1, "张三", 18,"123456");// 写入对象oos.writeObject(s);}catch (Exception e) {e.printStackTrace();}}@Data@AllArgsConstructor@NoArgsConstructor// 注意: 要想序列化对象, 需要实现Serializable接口static class Student implements Serializable {int id;String name;int age;// transient关键字修饰的属性不会被序列化transient String password;}
}

问:如果要一次性序列化多个对象,咋整?
答:用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可。
注意:ArrayList集合已经实现了序列化接口!

12.2、对象字节输入流-ObjectInputStream

作用:可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。

构造器 说明
public objectInputStream( InputStream is) 创建对象字节输入流,包装基础的字节输入流
方法 说明
public final objectlreadobject() 把存储在文件中的Java对象读出来
package com.hippo.javaiostream.iostream;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.*;/**
* @ClassName ObjectInputStreamOpt
* @Description TODO 对象字节输入流操作类
* @Author tangxl
* @create 2023-04-07 10:58
**/
public class ObjectInputStreamOpt {public static void main(String[] args) {try(// 创建一个对象字节输出流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("study-commons/java-file-iostream/src/main/resources/file/a.txt"));) {// 用一个学生对象来接收读取的对象ObjectOutputStreamOpt.Student s = (ObjectOutputStreamOpt.Student)ois.readObject();System.out.println(s);}catch (Exception e) {e.printStackTrace();}}@Data@AllArgsConstructor@NoArgsConstructor// 注意: 要想反序列化对象, 需要实现Serializable接口static class Student implements Serializable {int id;String name;int age;// transient关键字修饰的属性不会被序列化transient String password;}
}

13、补充知识:IO框架

什么是框架?
解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的。
好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率。
框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。

什么是IO框架?
封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。

Commons-io

Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高I0流的开发效率。

FileUtils类提供的部分方法展示 说明
public static void copyFile( File srcFile,File destFile) 复制文件。
public static void copyDirectory(File srcDir,File destDir) 复制文件夹
public static void deleteDirectory( File directory) 删除文件夹
public static string readFileToString(File file, string encoding) 读数据
public static void writeStringToFile(File file,String data,String charname,boolean append) 写数据
IOUtils类提供的部分方法展示 说明
public static int copy(InputStream inputstream,OutputStream outputstream) 复制文件。
public static int copy(Reader reader,writer writer) 复制文件。
public static void write(String data,outputStream output,string charsetName) 写数据

java提供的原生的IO处理类-java.io.File