Java IO流体系解析

47 阅读16分钟

IO流体系

image-20240223222029763.png

字节流

字节输入流(InputStream)

★FileInputStream(文件字节输入流)

含义: 操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中

书写步骤及细节:

1. 创建字节输入流对象
	构造方法:
	   FileInputStream(文件对象):创建字节输入流管道与源文件接通
       FileInputStream(文件路径):创建字节输入流管道与源文件接通
       
       细节1:如果文件不存在,直接报错
        java为什么这么设计?
           输出流:不存在,创建
               把数据写道文件当中
           输入流:不存在,报错
               因为创建出来的文件是没有数据的,没有任何意义
               所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。
    
       程序中最重要的是:数据。
2.读数据
	读数据的三种方式:
        1) read()只读取一个字节,想要读取多个,可以写个循环
        细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
        细节2:读到文件末尾,read方法返回-1
    
        2) read(byte[] bytes): 每次使用一个字节数组去读取,数据放在数组中
        返回值是 读取 真实 数据的个数
    
        3) readAllBytes() :直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回
       
3.释放资源
        细节:每次使用完流之后都要释放资源

代码演示如下:

public static void main(String[] args) throws IOException {
        //1. 创建字节输入流对象
        FileInputStream fis  = new FileInputStream(new File("io\\a.txt"));
        //2.读数据
        //read():一次读取一个字节
       /*
        int read = fis.read();
        System.out.println((char)read);
        */
		
       /*
       //使用循环读取文件所有数据
        int len;//用于记住读取的字节
        while((len = fis.read()) != -1){
            System.out.print((char)len);
        }
        */

        //read(byte[] bytes):一次读取多个字节
    
        //byte[] buffer = new byte[5];
        /*
        int read = fis.read(buffer);
        System.out.println(read);
        */

       /*
       int len;//记住每次读取多少个字节
        while ((len = fis.read(buffer)) != -1){
            //注意:读取多少,倒出多少
            String rs = new String(buffer,0,len);
            System.out.print(rs);
        }
        */

        //读取全部字节
        //调用readAllBytes()方法读取所有字节,返回一个存储所有字节的字节数组
        byte[] buffer = fis.readAllBytes();
        System.out.println(new String(buffer));
    
        //3.释放资源
        fis.close();
    }

★BufferedInputStream(字节缓冲输入流)

字节缓冲流的作用:

​ 提高字节流读写数据的性能。

原理:

​ 字节缓冲输入流自带8KB缓冲池;字节缓冲输出流自带8KB缓冲池;

书写步骤及细节:

1.创建字节缓冲输入流对象
	构造方法:
		BufferedInputStream(InputStream is):把低级的字节输入流包装成一个高级的缓冲字节输入流。从而提高读数据的性能。
2.读取数据
	读取数据方式和FileInputStream一样
3.释放资源

代码演示如下:

 public static void main(String[] args) throws IOException {
        //1.创建字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day10\\a.txt"));      

        //2.读取操作
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
          String rs = new String(bytes,0,len);
          System.out.print(rs);
        }
      //3.释放资源
        bis.close();
    }

ObjectInputStream(反序列流/对象字节输入流)

含义: 用于存储和读取基本数据类型数据或对象的处理流

书写步骤及细节:

1.创建反序列化对象
 	 	构造方法:
 		 ObjectInputStream(InputStream out) :把基本流变成高级流
2.读数据
 	读数据方式:
 		Object readObject():把序列化到本地文件中的对象,读取到程序中来
3.打印对象
4.释放资源

代码演示如下:

public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.创建反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file_io\\d.txt"));
        //2.读取数据
        Object o = ois.readObject();
        //3.打印对象
        System.out.println(o);
        //4.释放资源
        ois.close();

    }

DataInputStream(数据输入流)

  • 允许把数据和其类型一并写出去。

image-20240224112802335.png

image-20240224112809032.png

代码演示如下:

public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try (
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("io-app2/src/out.txt"));
                ){
            int i = dis.readInt();
            System.out.println(i);

            double d = dis.readDouble();
            System.out.println(d);

            boolean b = dis.readBoolean();
            System.out.println(b);

            String rs = dis.readUTF();
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

字节输出流(OutputStream)

★FileOutputStream(文件字节输出流)

含义: 操作本地文件的字节输出流,可以把程序中的数据写到本地文件中

构造方法:

FileOutputStream(File file):创建字节输出流管道与源文件对象接通 
FileOutputStream(File file, boolean append):创建字节输出流管道与源文件对象接通,可追加 
FileOutputStream(String name) :创建字节输出流管道与源文件对象接通 
FileOutputStream(String name, boolean append):创建字节输出流管道与源文件对象接通,可追加

书写步骤及细节:

1.创建字节输出流对象
      细节1:参数是字符串表示的路径或者是File对象都是可以的
      细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
      细节3:如果文件已经存在,则会清空文件
2.写数据
     细节:write方法的参数是整数,但实际上写到本地文件中的是整数在ASCII上对应的字符
     写数据的三种方式:
           write(int b):一次写一个字节数据
           write(byte[] b):一次写一个字节数组数据
           write(byte[] b,int off,int len):一次写一个字节数组的部分数据
     存在问题:
         如何续写:
             如果想要续写,打开续写开关即可
             开关位置:创建对象的第二个参数
             默认false:表示关闭续写,此时创建对象会清空文件
             手动传递true:表示打开续写,此时创建对象不会清空文件
     
          如何换行写:
             再次写出一个换行符就可以了
             Windows: \r\n
             Linux:    \n
             Mac:      \r
           细节:
              在windows操作系统当中,Java对回车换行进行优化
              虽然完整的是\r\n,但是我们写其中一个\r或者\n
              Java也可以实现换行,因为Java在底层会补全
           建议:
              不要省略,还是写全
3.释放资源
      每次使用完流之后都要释放资源

代码演示如下:

 public static void main(String[] args) throws IOException {
        //FileOutputStream fos = new FileOutputStream("指定文件的路径");
        //FileOutputStream fos = new FileOutputStream("file_io\\b.txt");
        FileOutputStream fos = new FileOutputStream("file_io\\b.txt",true);//追加续写
        byte[] b = {'A','B','C','D','E'};
        fos.write(b);
        fos.write(b,1,3);
     
        String str1 = "abcdef";
        byte[] bytes1 = str1.getBytes();//getBytes()将字符串转换成字节数组
        fos.write(bytes1);

        //再次写出一个换行符就可以了
        String wrap = "\r\n";
        byte[] bytes2 = wrap.getBytes();
        fos.write(bytes2);

        String str2 = "666";
        byte[] bytes3 = str2.getBytes();
        fos.write(bytes3);
        fos.close();
    }

★BufferedOutputStream(字节缓冲输入流)

书写步骤及细节:

1.创建字节缓冲输入流对象
	构造方法:
		BufferedOutputStream(OuputStream is):把低级的字节输出流包装成一个高级的缓冲字节输出流。从而提高写数据的性能。
2.读取数据
	读取数据方式和FileOutputStream一样
3.释放资源

代码演示如下:( 利用字节缓冲流拷贝文件)

public static void main(String[] args) throws IOException {
        //1.创建字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day10\\a.txt"));
        //2.创建字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day10\\再别康桥.txt"));

        //3.读取操作
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
            //4.写操作
            bos.write(bytes,0,len);
        }
       //5.释放资源
        bos.close();
        bis.close();
    }

ObjectOutputStream(序列化流/对象字节输出流)

含义: 可以把Java中的对象写到本地文件中

书写步骤及细节:

 1.创建对象

 2.创建序列化对象
 	 	构造方法:
 		ObjectOutputStream(OutPutStream out):把基本流包装成高级流
 3.写数据
 	写数据方式:
 		writeObject(Object o):把对象序列化(写出)到文件中去
 4.释放资源
 
 细节汇总:
 1、使用序列化流将对象写道文件时,需要让javabean类实现Serializable接口
 	否则,会出现NotSerializableException异常
 2、序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来
 3、序列化对象后,修改JavaBean类,再次反序列化,会不会有问题?
 	解决方案:给Javabean类添加serialVersionUID(序列化、版本号)
 4、如果一个对象中某个成员变量的值不想被序列化,又该如何实现?
    解决方案:给该成员变量加transient瞬态关键字,该关键字标记的成员变量不参与序列化过程

代码演示如下:

public static void main(String[] args) throws IOException {
        //1.创建对象
        Student stu = new Student("张三", 18);
        //2.创建序列化对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file_io\\d.txt"));
        //3.写数据
        oos.writeObject(stu);

        //4.释放资源
        oos.close();
    }

DataOutputStream(数据输出流)

  • 用于读取数据输出流写出去的数据。

image-20240224112838235.png

image-20240224112842905.png

代码演示如下:

public class DataOutputStreamTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个数据输出流包装低级的字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("io-app2/src/out.txt"));
                ){
            dos.writeInt(97);
            dos.writeDouble(99.5);
            dos.writeBoolean(true);
            dos.writeUTF("你好666!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

PrintStream(打印字节流)

  • 作用:打印流可以更方便、更高效的把数据打印出去,能实现将指定的内容原封不动地打印出去的功能。

PrintStream提供的打印数据的方案

image-20240224112403775.png

image-20240224112454436.png

代码演示如下:

public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/demo.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/demo.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/demo.txt", true));
                ){
                ps.print(97);	//文件中显示的就是:97
                ps.print('a'); //文件中显示的就是:a
                ps.println("我爱你中国abc");	//文件中显示的就是:我爱你中国abc
                ps.println(true);//文件中显示的就是:true
                ps.println(99.5);//文件中显示的就是99.5

                ps.write(97); //文件中显示a,发现和前面println方法的区别了吗?

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

字符流

字符流:

  • 字符流的底层其实就是字节流
  •   字符流 = 字节流 + 字符集
    

特点:

  • 输入流:一次读一个字节,遇到中文时,一次读多个字节
  • 输出流:底层会把数据按照指定的编码方式进行编码。变成字节在写到文件中

字符输入流(Reader)

★FileReader(文件字符输入流)

含义: 是字符输入流,用来将文件中的字符数据读取到程序中来。

书写步骤及细节:

1.创建字符输入流对象
  构造方法:
     FileReader(File file) :创建字符输入流关联本地文件
     FileReader(String pathname) :创建字符输入流关联本地文件
2.读取数据
     read():读取数据,读到末尾返回-1
     read(char[] buffer):读取多个数据,读到末尾返回-1
     细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数
     细节2:读到文件末尾了,read方法返回-1
3.释放资源
	 每次使用完流之后都要释放资源

代码演示如下:

public static void main(String[] args) throws IOException {
        //1.创建字符输入流
        FileReader fr = new FileReader("day10\\a.txt");
        //2.读数据
        char[] chars = new char[2];
        int len;
        while ((len = fr.read(chars)) != -1){
            System.out.println(chars);
        }
        //3.释放资源
        fr.close();
    }

★BufferedReader(字符缓冲输入流)

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

书写步骤及细节:

1.创建字符缓冲输入流
	构造方法:
    	BufferedReader(Reader):把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符字符输入流读字符数据的性能。
2.读数据
	新增读取方式:
		readLine():读取一行数据,如果没有数据可读了,会返回null
		
	细节:readLine() 方法读取的时候,一次读一整行,遇到回车换行结束 但是他不会把回车换行读到内存当中
3.释放资源

代码演示如下:

public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("day10\\再别康桥.txt"));
        //2.读数据
        String str;
        while((str =  br.readLine()) != null){
            System.out.println(str);
        }
        //3.释放资源
        br.close();
    }

InputStreamReader(字符输入转换流)

  • 解决不同编码时,字符流读取文本内容乱码的问题
  • 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转换成字符输入流。

书写步骤及细节:

1.创建对象并指定字符编码
	构造方法:
		InputStreamReader(InputStream is):把原始数据的字节输入流,按照默认编码转成字符输入流
		InputStreamReader(InputStream is,String charset):把原始数据的字节输入流,按照指定编码转成字符输入流
	JDK新玩法:
    	FileReader jdk11的时候新增一个构造 可以传递 编码集
    	FileReader isr = new FileReader("F:\\aaa\\hhh.txt", Charset.forName("GBK"))
2.读取数
3.释放资源

代码演示如下:

 public static void main(String[] args) throws IOException {
        /*
        * 利用转换流按照指定字符编码读取
        * */
        //1.创建对象并指定字符编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream("F:\\aaa\\hhh.txt"), "GBK");
     
        //FileReader isr = new FileReader("F:\\aaa\\hhh.txt", Charset.forName("GBK"));

        //2.读取数
        int ch;
        while ((ch = isr.read()) != -1){
            System.out.print((char) ch);
        }

        //3.释放资源
        isr.close();


    }

字符输出流(Writer)

★FileWriter(文件字符输出流)

含义: 是字符输出流,用来将程序中的字符数据写入到文件中来。

书写步骤及细节:

1.创建字符输出流对象
	构造方法:
		FileWriter(File file):创建字符输出流关联本地文件
		FileWriter(String pathname):创建字符输出流关联本地文件
		FileWriter(File file,boolean append):创建字符输出流关联本地文件,续写
		FileWriter(String pathname,boolean append):创建字符输出流关联本地文件,续写
		
     细节1:参数时字符串表示的路径或者File对象都是可以的
     细节2:如果文件不存在会创建一个新的文件,但是要保证父级文件路径时存在的
     细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关
     
2.写数据
	写数据的5种方式:
		writer(int c) : 写一个字符
		writer(String str) :写一个字符串
		writer(String str,int off,int len):写出一个字符串的一部分
		writer(char[] cbuf) :写出一个字符数组
		writer(char[] cbuf,int off,int len):写出字符数组的一部分
		
     细节1:如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符
     细节2:FileWriter写完数据之后,必须刷新或者关闭,写出去的数据才能生效
           刷新flush():将缓冲区的数据刷到文件中去
           flush()和close()的区别:
           	 flush()具备刷新完,还可以继续写操作,close()执行完了就流关闭了,不能再写入
3.释放资源
     细节:每次使用完流之后都要释放资源

代码演示如下:

 public static void main(String[] args) throws IOException {
        //1.创建字符输出流对象
        FileWriter fw = new FileWriter("day10\\b.txt");
        //2.写数据
        //writer(String str) :写一个字符串
        fw.write("一二三四五六七八九十");
     
        fw.write("\r\n");//换行
     
        //writer(String str,int off,int len):写出一个字符串的一部分
        fw.write("副书记肯定会",0,3);
     
        //刷新
        fw.flush();
        //writer(int c) : 写一个字符
        fw.write(65);
        //3.释放资源
        fw.close();
    }

★BufferedWriter(字符缓冲输出流)

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

书写步骤及细节:

1.创建字符缓冲输入流
	构造方法:
    	 BufferedWriter(Writer r):把低级的字符输出流包装成字符缓冲输出流管道,从而提高字符字符输入流写字符数据的性能。
    	 
    细节:续写时true写在FileWriter里
2.读数据
	新增换行方式:
		newLine():换行
		
3.释放资源

代码演示如下:

 public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("day10\\c.txt"));
        //2.写数据
        bw.write("鹅鹅鹅");
        bw.newLine();
        bw.write("曲项向天歌");
        bw.newLine();
        bw.write("白毛浮绿水");
        bw.newLine();
        bw.write("红掌拨清波");
        bw.newLine();
        //3.释放资源
        bw.close();
    }

OutputStreamWriter(字符输出转换流)

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

书写步骤及细节:

1.创建对象并指定字符编码
	构造方法:
		OutputStreamReader(OutputStream os):把原始数据的字节输出流,按照默认编码转成字符输出流
		OutputStreamReader(OutputStream os,String charset):把原始数据的字节输出流,按照指定编码转成字符输出流
	JDK新玩法:
    	FileWriter jdk11的时候新增一个构造 可以传递 编码集
    	FileWriter osw = new FileWriter("file_io\\c.txt", Charset.forName("GBK"))	
2.读取数
3.释放资源

代码演示如下:

public static void main(String[] args) throws IOException {
        /*
        * 利用转换流按照指定字符编码写出
        * */
        //1.创建转换流的对象
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("file_io\\c.txt"),"GBK");
        FileWriter osw = new FileWriter("file_io\\c.txt", Charset.forName("GBK"));
        //2.写数据
        osw.write("你好你好");
        //3.释放资源
        osw.close();


    }

PrintWriter(打印字符流)

  • 作用:打印流可以更方便、更高效的把数据打印出去,能实现将指定的内容原封不动地打印出去的功能。

PrintWriter提供的打印数据的方案

image-20240224112523474.png

image-20240224112528764.png

代码演示如下:

public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/demo.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/demo.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/demo.txt", true));
                ){
                ps.print(97);	//文件中显示的就是:97
                ps.print('a'); //文件中显示的就是:a
                ps.println("我爱你中国abc");	//文件中显示的就是:我爱你中国abc
                ps.println(true);//文件中显示的就是:true
                ps.println(99.5);//文件中显示的就是99.5

                ps.write(97); //文件中显示a,发现和前面println方法的区别了吗?

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

PrintStream和PrintWriter的区别:

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

★IO框架(commons-io)

**本质:**是别人写好的一些字节码文件(class文件),打包成了一个jar包。我们只需要把jar包引入到我们的项目中,就可以直接用了。

FileUtils类提供的方法如下:

image-20240224105155811.png

代码演示如下:

  public static void main(String[] args) throws IOException {
        //copyFile(File srcFile,File destFile):复制文件
        FileUtils.copyFile(new File("day10\\再别康桥.txt"),new File("day10\\桥.txt"));

        //copyDirectory(File srcDir,File destDir):复制文件夹
        FileUtils.copyDirectory(new File("day10\\aaa"),new File("day10\\bbb"));


        //deleteDirectory(File directory) :删除文件夹
        FileUtils.deleteDirectory(new File("day10\\bbb"));


        //readFileToString(File file,String encoding) :读数据
        String s = FileUtils.readFileToString(new File("day10\\再别康桥.txt"), "UTF-8");
        System.out.println(s);

        //writeStringToFile(File file,String data,String charname,boolean append) :写数据

        FileUtils.writeStringToFile(new File("day10\\d.txt"),"我会尽快很快就","UTF-8");

    }

IOUtils类提供的方法如下:

image-20240224105329299.png

代码演示如下:

public static void main(String[] args) throws IOException {
        //copy(InputStream inputStream,OutputStream outputStream) :复制文件
        IOUtils.copy(new FileInputStream("day10\\再别康桥.txt"),new FileOutputStream("day10\\在康穷啊.txt"));

        //copy(Reader reader,Writer writer):复制文件
        IOUtils.copy(new FileReader("day10\\再别康桥.txt"),new FileWriter("day10\\在康穷1啊.txt"));

        //write(String data,OutputStream output,String charsetName):写数据
        IOUtils.write("回答了发哈",new FileOutputStream("day10\\f.txt"),"UTF-8");

    }

XMind体系总结图

IO流总结.png