欢迎来到我的博客

将来的你一定会感激现在拼搏的自己

JavaSE基础day17 IO操作01

 


 

一. IO流

(一) IO流概述

1.IO流介绍:

        I和O,分别是Input和Output两个单词的缩写,Input是输入,Output是输出。

        流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流

        常见的应用: 文件复制、文件上传、文件下载等。

 

 

2.IO流分类:

        按照数据的流向:

                 输入流:读数据

                 输出流:写数据

        按照数据类型:

                 字节流:

                         字节输入流和字节输出流

                 字符流:

                         字符输入流和字符输出流

  字节流和字符流的使用场景:

       如果操作的是纯文本文件,优先使用字符流

       如果操作的是图片、视频、音频等二进制文件,优先使用字节流

       如果不确定文件类型,优先使用字节流.字节流是万能的流

3.IO流程序书写流程:

        (1) 在操作之前,要导包,java.io包

        (2) 在操作流对象的时候,要处理解决异常(IOException)

        (3) 在操作完流对象之后,必须关闭资源, 所有流资源的关闭 close();

 

 

(二) 字节流

2.1字节流概述

1. 字节流抽象基类:

        (1) InputStream:这个抽象类是表示字节输入流所有类的超类

        (2) OutputStream:这个抽象类是表示字节输出流所有类的超类

根据交互设备的不同,有不同的具体子类

 

2.2字节输入流FileInputStream

1、FileInputStream是InputStream一个具体子类,用于和磁盘上的文件进行交互

2、FileInputStream不仅可以一次读取一个字节,也可以一次读取很多个字节;不仅可以读取纯文本文件,也可以读取图片、视频、音频等非纯文本文件。一切数据在计算机中都是以字节的形式在存储和计算

3、构造方法:

       FileInputStream(File f):将一个File对象所表示的文件路径封装在一个字节输入流中       FileInputStream(String path):将一个字符串所表示的文件路径封装在一个字节输入流中  

       注意事项:无论是哪个构造方法,都只能封装文件的路径,封装文件夹路径没有任何意义,因为文件夹本身作为容器不是作为文件数据,所以不能使用流对象进行读写

 

4. 读取文件的方法:

1) int read():从当前的字节输入流中,读取并返回一个字节,返回值结果int类型, 表示读取到的字节对应的整数结果, 如果返回-1表示证明文件读取完毕(每一个文件都有结束标志,当流资源读取到结束标志,返回-1证明文件读取完毕)

 

2) int read(byte[] arr):从当前的字节输入流中最多读取arr.length个字节,读取到的字节放置到参数arr数组中,返回值结果int类型, 表示本次读取到的字节个数, 如果读到-1,证明文件读取完毕

注意 : 数组读取效率优于单个字节读取效能

 

3) void close():关闭该流对象

 

字节输入流单个字节读取代码

复制代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 1. 封装出一个字节输入流: 相当于绑定一个数据源
        FileInputStream fis = new FileInputStream("D:\\newFile.txt");
        // FileInputStream fis2 = new FileInputStream(new File("D:\\newFile.txt"));
        /*int first = fis.read();
        System.out.println(first);// 97
        int second = fis.read();
        System.out.println(second);// 98
        int thrid = fis.read();
        System.out.println((char)thrid);// c
        int four = fis.read();
        System.out.println((char)four);// d
        int test1 = fis.read();
        System.out.println(test1);// -1
        int test2 = fis.read();
        System.out.println(test2);*/

        // len表示每次读取到的字节结果
        int len;
        // 优化: 循环读取文件内容
        while((len = fis.read()) != -1){
            System.out.print((char)len);
        }
        fis.close();
    }
}
复制代码

 

字节数组读取

复制代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamReadArray {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\newFile.txt");
        byte[] b = new byte[4];

        /*int i = fis.read(b);
        System.out.println(i);// 4
        System.out.println(new String(b));// abcd

        int i2 = fis.read(b);
        System.out.println(i2);// 4
        System.out.println(new String(b));// 123?

        int i3 = fis.read(b);
        System.out.println(i3);// 4
        System.out.println(new String(b));// ?>qq

        int i4 = fis.read(b);
        System.out.println(i4);// 1
        System.out.println(new String(b));// 6>qq

        int i5 = fis.read(b);
        System.out.println(i5);// -1
        System.out.println(new String(b));// 6>qq*/

        // len表示每次读取到的字节的个数
        int len;
        // 优化成循环读取
        while((len = fis.read(b)) != -1){
            // new String(b,0,len) : 将字节数组的一部分转换成字符串; 将字节数组中的数值参考平台编码表,转换成符
            // 拼接字符成字符串
            // 将b数组从0索引位置开始,截取len个字节转换成目标字符串
            System.out.print(new String(b,0,len));
        }

        fis.close();
    }
}
复制代码

 

2.3字节输出流FileOutputStream

1. FileOutputStream和磁盘做交互的一个字节输出流对象,用于把内存当中的字节输出到磁盘当中去

2. 构造方法:

       FileOutputStream(File f):将f描述的路径文件封装在字节输出流对象中

       FileOutputStream(String path):将path描述的文件路径封装在字节输出流对象中

  FileOutputStream(String path,boolean append):如果第二个参数为true,则字节将写入参数path对应文件的末尾而不是开头(追加写入)

  FileOutputStream(File path,boolean append):如果第二个参数为true,则字节将写入参数path对应文件的末尾而不是开头(追加写入)

 

3. 字节流写数据的方式:

  1) void write(int b): 将指定的字节写入此文件输出流一次写一个字节数据

  2) void write(byte[] b): 将b.length字节从指定的字节数组写入此文件输出流

  3) void write(byte[] b, int off, int len): 把b数组的off索引开始的len个字节通过字节输出流写出

 

4. 字节流写数据实现换行

    (1)windows:\r\n

    (2)linux:\n

    (3)mac:\r

 

复制代码
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 注意: 使用输出流资源: 如果只提供文件名称,表示数据覆盖式写入
       /* FileOutputStream fos = new FileOutputStream("D:\\0222Java系统班\\test.txt");
        //FileOutputStream fos1 = new FileOutputStream(new File("D:\\0222Java系统班\\test.txt"));
        // 1. 写入单个字节
        fos.write(97);// a
        // 2. 写入字节数组
        byte[] b = {65,66,67,68,69};
        fos.write(b);// ABCDE
        // 3. 写入字节数组的一部分: 从指定索引位置开始,写入一定个数
        fos.write(b,0,2);// AB
        // 4. 写入字符串
        fos.write("今天下雪了!".getBytes());

        fos.close();*/

        // 输出流资源,构造方法第二个参数,设计true数值,那么表示未来写入的数据是追加写入
        FileOutputStream fos1 = new FileOutputStream("D:\\0222Java系统班\\test.txt",true);
        fos1.write("\r\n".getBytes());
        fos1.write("添加".getBytes());
        fos1.close();
    }
}
复制代码

 

2.4文件拷贝

1. 案例 : 复制一张图片或一个视频

  例如: 把“D:\1.jpg”复制到“D:\copy.jpg”。(文件可以是任意类型文件)

  实现步骤:

    复制文件,其实就把文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(数据目的)

  数据源:

    D:\1.jpg --- 读数据 --- InputStream --- FileInputStream

  目的地:

    D:\copy.jpg --- 写数据 --- OutputStream --- FileOutputStream 

 

复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyPicture {
    public static void main(String[] args) throws IOException {
        // 1. 定义出字节输入流: 绑定一个数据源
        FileInputStream fis = new FileInputStream("D:\\0222Java系统班\\day17\\斗地主分析.png");
        // 2. 定义出字节输出流: 绑定数据目的
        FileOutputStream fos = new FileOutputStream("D:\\copy.png");
        // 3. 复制图片
         int len;
         while((len = fis.read()) != -1){
             fos.write(len);
         }
         fis.close();
         fos.close();
    }
}
复制代码

 

1. 字节流拷贝效率提升

  (1) 使用一个字节一个字节拷贝的方式,效率非常低:IO的次数过多,有多少个字节,就要IO两倍的次数。

 

  (2) 提升的思路:一次多读一些数据,一次多写出一些数据

    使用FileInputStream中的read(byte[] arr)和 FileOutputStream中的write(byte[] arr)。数组的大小可以准备成和文件一样大。配合InputStream中 的available方法可以获取源文件的字节个数,用于创建数组的大小。

 

  (3) 数组是存储于内存的,内存的大小是有限的,如果文件过大,就无法创建大小相同的数组。

    只能考虑使用小一些的数组,每次拷贝源文件的一部分,多拷贝几次。涉及方法:                a: InputStream中的read(byte[] arr):将数据读取到数组中,返回本次读到的有效字节的个数,如果返回值为-1,表示本次读到的有效字节个数为0,表示到达了文件末尾

    b: OutputStream中的write(byte[] arr):将数组中的所有数据,都写出到了目标文件中

    c: OutputStream中的write(byte[] arr, int offset, int len):将数组中的指定部分的数据,写出 到目标文件中(在读取的时候,读到了多少有效字节,就将这么多有效字节写出到目标文件中),一般offset都选择0

 

(4) 注意事项:最终拷贝方案就是小数组

  a: 数组的大小可以任意选择,数组越大,拷贝次数越少,拷贝的效率越高

  b: 一般情况,数组的大小使用1024的整数倍,在jdk中喜欢使用1024*8大小作为缓冲区的大小。

 

复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyPictureUseArray {
    public static void main(String[] args) throws IOException {
        // 1. 定义出字节输入流: 绑定一个数据源
        FileInputStream fis = new FileInputStream("D:\\0222Java系统班\\day17\\斗地主分析.png");
        // 2. 定义出字节输出流: 绑定数据目的
        FileOutputStream fos = new FileOutputStream("D:\\copy1.png");
        byte[] b = new byte[1024];
        long begin = System.currentTimeMillis();
        // len表示每次读取到的字节个数
        int len;
        // 3. 图片复制
        while((len = fis.read(b)) != -1){
            fos.write(b,0,len);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - begin);// 2
        fis.close();
        fos.close();
    }
}
复制代码

 

2.5缓冲字节流

1. 字节缓冲流介绍:

        BufferedOutputStream:是OutputStream的子类, 表示高效字节输出流。流资源在创建时, 提供默认的数组缓冲区, 按照缓冲区数组大小进行写入, 实现效率

        BufferedInputStream:是InputStream的子类, 表示高效字节输入流。资源在创建时, 提供默认的数组缓冲区, 按照缓冲区数组大小进行读取, 实现效率

 

        注意:这两个流是包装类型:本身不具备读写的功能,只是在某个具体的流对象的基础上,对其进行加强,例如FileInputStream和FileOutputStream,原本效率较低,加强之后,就效率较高。

 

 

2. 构造方法

  1) BufferedOutputStream(OutputStream out): 创建字节缓冲输出流对象

  2) BufferedInputStream(InputStream in): 创建字节缓冲输入流对象

 

 

2.6缓冲字节流实现原理

1. BufferedOutputStream原理分析:

   BufferedOutputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用write方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。

 

2. BufferedInputStream原理分析:

   BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用read方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个8192个字节了。

复制代码
import java.io.*;
public class BufferedStream {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("D:\\0222Java系统班\\day17\\视频\\01.day16内容回顾.mp4"));

        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("D:\\copyday16.mp4")
        );
        long begin = System.currentTimeMillis();
        // len表示每次读取到的字节的结果
        int len;
        while((len = bis.read()) != -1){
              bos.write(len);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - begin);

        bis.close();
        bos.close();
    }
}
复制代码

 

2.7flush()方法和close()方法区别

1. close方法会先调用flush方法

2. close方法用于流对象的关闭,一旦调用了close方法,那么这个流对象就不能继续使用了

3. flush只是将缓冲区中的数据,刷新到相应文件中,而不会将流对象关闭,可以继续使用这个流对象。但是如果flush方法使用过于频繁,那么丧失了缓冲区的作用。

复制代码
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFlushAndClose {
    public static void main(String[] args) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("abc.txt")
        );

        // BufferedOutputStream底层是带8192大小字节数组,写数据时候,没有达到8192,存储在
        // 底层数组缓冲区,没有向文件中自动同步
        bos.write("abcdef".getBytes());
        // 1. flush() : 刷新的效果就是将数组缓冲区中的数据, 同步到文件中
        // bos.flush();
        // 2. close() : 表示流资源关闭,在关闭资源之前,先调用一次flush方法,将缓冲区中的数据进行同步,
        // 后关闭资源
        bos.close();
    }
}
复制代码

 

2.8IO中保证流对象关闭的标准格式

1. 捕获异常和声明异常的原则

  1) 如果知道如何处理异常,那么就使用try...catch来进行处理;如果不知道如何处理异常,那么就使用throws来对异常进行声明,或者将当前的异常包装成其他的异常类型,进行声明,因为越到代码的高层,拥有更多的资源、更高的位置和权力,知道如何处理,底层被调用的方法,虽然是出现异常的位置,但是并不知道如何处理。

 

  2) 如果你希望程序出现异常之后,继续运行下去,那么就使用try...catch;如果出现异常之后,希望当前方法的代码停止运行,那么就使用throws

 

2. IO中保证流对象关闭的格式(jdk1.7之前)

       try{

  流对象的使用;

  }catch(异常类名 变量名){

  异常的处理代码;

  }finally{

  关闭流资源;

  }

 

 

3. IO中保证流对象关闭的格式(jdk1.7之后)

      try (

            流对象的创建;

  ) {

  流对象的使用;

  }catch(IOException e){

       异常处理方式;

  }

特点:

        流对象使用之后,不需要手动关闭,因为这个格式会在流资源使用完毕后,自动关闭了流对象.

 

JDK1.7之前异常处理

复制代码
mport java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class IOExceptionDeal {
    public static void main(String[] args) {
        byte[] b = new byte[1024];
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try{
            fis = new FileInputStream("D:\\0222Java系统班\\day17\\斗地主分.png");
            fos = new FileOutputStream("D:\\copy1.png");
            // len表示每次读取到的字节个数
            int len;
            // 3. 图片复制
            while((len = fis.read(b)) != -1){
                fos.write(b,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                if(fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    if(fos != null){
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
复制代码

 

JDK1.7之后异常处理

复制代码
import java.io.*;
public class JDK7_IOException {
    public static void main(String[] args) {
        /*
            try(
               流资源创建;
            ){
               可能会发生异常代码;
            }catch(预计发生异常类型 变量名){
                异常解决方案;
            }

            在JDK1.7版本时, 推出了针对于IO流资源处理新的格式,try小括号中进行资源创建,同时在资源使用完毕之后会自动关闭流资源
         */
         try(
                 FileInputStream fis = new FileInputStream("abc.txt");
                 BufferedInputStream bis = new BufferedInputStream(fis);
                 FileOutputStream fos = new FileOutputStream("abcCopy.txt");
                 BufferedOutputStream bos = new BufferedOutputStream(fos);

                 ){
             // len表示每次读取到的字节个数
             int len;
             // 3. 图片复制
             while((len = bis.read()) != -1){
                 bos.write(len);
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
    }
}
复制代码

 

3.2编码表

1. 什么是字符集:

  是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

  计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。

2. 常见字符集有: ASCII字符集、GBXXX字符集、Unicode字符集等。 

  (1) ASCII字符集:

    ASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)

基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

  (2) GBXXX字符集:

    GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完 全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等

  (3) Unicode字符集:是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转 换、处理的要求。

  (4) UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码.

 

 

3.3字符输出流

1. 字符流输出流介绍:

        Writer: 用于写入字符流的抽象父类

        FileWriter: 用于写入字符流的常用子类

 

2. FileWriter构造方法

  1) FileWriter(File file): 根据给定的 File 对象构造一个 FileWriter 对象

  2) FileWriter(File file, boolean append): 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象

  3) FileWriter(String fileName): 根据给定的文件名构造一个 FileWriter 对象

  4) FileWriter(String fileName, boolean append)

        根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象

 

3. 写的方法:

  1) void write(int c): 写一个字符

  2) void write(char[] cbuf): 写入一个字符数组

  3) void write(char[] cbuf, int off, int len): 写入字符数组的一部分

  4) void write(String str): 写一个字符串

  5) void write(String str, int off, int len): 写一个字符串的一部分

 

复制代码
import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("chinese.txt");

        //P天a金天下天ok的,我很好k的,我
        // 1)void write(int c): 写一个字符
        fw.write('P');
        fw.write('天');
        fw.write(97);

        // 2)void write(char[] cbuf): 写入一个字符数组
        char[] ch = {'金','天','下'};
        fw.write(ch);

        // 3) void write(char[] cbuf, int off, int len): 写入字符数组的一部分
        fw.write(ch,1,1);//// 4)void write(String str): 写一个字符串
        String s = "ok的,我很好";
        fw.write(s);

        // 5) void write(String str, int off, int len): 写一个字符串的一部分
        fw.write(s,1,4);// k的,我
        fw.close();
    }
}
复制代码

 

 

练习:

  将键盘录入的用户名和密码保存到本地实现持久化存储

实现步骤:

  1.获取用户输入的用户名和密码

  2.将用户输入的用户名和密码写入到本地文件中

  username = ?

  password = ?

  3.关流,释放资源

复制代码
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class FileWriterTest {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.next();
        System.out.println("请输入密码:");
        String pssword = sc.next();
        FileWriter fw = new FileWriter("userMessage.txt");
        fw.write("username = " + name);
        fw.write("\r\n");
        fw.write("psssword = " + pssword);
        fw.close();
    }
}
复制代码

 

posted @   晴天般的微  阅读(127)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示

目录导航