JavaSE—IO流 ( 八千字带你快速深入理解IO流体系 )

本文详细介绍了Java中的File类的使用,包括构造方法、文件和目录操作,以及字节流(InputStream/OutputStream)和字符流(Reader/Writer)的基础知识。此外,还涵盖了对象的序列化与反序列化,如何通过Serializable接口实现对象持久化。
摘要由CSDN通过智能技术生成

📌 File类   


○ 知识概要:

 •  一个File类的对象可以表示一个具体的文件目录 .

 •  File对象可以对文件或目录的属性进行操作,如:文件名、最后修改日期、文件大小等.

 •  File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作

○ File的构造方法:

    File( String pathname) 指明详细的文件或目录的路径

        //指明详细的路径以及文件名
        File file =new File("E:/demo1.txt");
        //指明详细的路径以及目录名
        File directory =new File("E:/temp");
        //注意使用/或\\来区分路径等级

 ○ File类的常用方法:

注意:delete删除一个文件夹时,文件夹中必须是空的

○ 使用File类创建文件及目录:

 ① 创建文件   

        //指明详细的路径以及文件名
        File file =new File("E:/demo3.txt");
        if (!file.exists()){//判断指向文件是否存在
            file.createNewFile();//通过createNewFile()方法创建文件
        }
        file.delete();//删除文件

 ② 创建文件夹(单级文件夹和多级文件夹)

    mkdir( ) 创建单级文件夹    mkdirs( )创建多级文件夹

        //指明详细的路径以及目录名
        File directory =new File("E:/demo");
        directory.mkdir();//mkdir用来创建单级文件夹

        File directorise =new File("E:/demo/demo1/demo2");
        directorise.mkdirs();//mkdirs用来创建多级文件夹

○ 如何得到一个目录中的所有文件:

   思路:对目录进行遍历, 遇到文件直接输出, 遇到子目录再对子目录遍历, 直到遇见文件

import java.io.File;
import java.io.IOException;

public class Review1 {
    public static void main(String[] args) throws IOException {
        //指明详细的路径以及目录名
        File directory =new File("E:/IO");
        search(directory);
    }
    //创建一个方法用来遍历目录里的文件
    public static void search(File file){
        File[] files =file.listFiles();获取到当前给定目录的子级文件/文件夹
        for (File f:files){
            if (f.isFile()){//若是文件,直接输出
                System.out.println(f);
            }else {
                  search(f);//若是目录,继续向下查找,直到遇到文件
            }
        }
    }
    
}

📌 输入(I)与输出(O)  


    •  输入和输出是一个相对概念,输入和输出是相对于我们的程序 。

○ 输入---Input

  • 把电脑硬盘上的数据读到程序中,称为输入,即input,进行数据的read操作.

○ 输出---Output

  • 从程序往外部设备写数据,称为输出,即output,进行数据的write操作.

  ●  如图起连接作用的通道也就是我们通常所说的流 .


📌 字节流与字符流   

✰ 体系图


○ 知识概要:

   字节流:读取时以字节为单位,可以读取任意文件.

   字符流:读取时以字符为单位,只能读取文本文件.

字节流中常用类:

  • 字节输入流   InputStream
  • 字节输出流   OutputStream

字符流中常用类:

  • 字符输入流   Reader
  • 字符输出流   Writer

常用类的基本方法

📌 InputStreamOutputStream的子类都是字节流, 可以读写二进制文件,主要处理音频、图片、歌曲、字节流处理单元为1个字节。

 InputStream的基本方法:

  • 读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
  • int read() throws IOException
  • 读取一系列字节并存储到一个数组buffer, 返回实际读取的字节数,如果读取前已到输入流的末尾返回-1
  • int read(byte[] buffer) throws IOException
  • 关闭流释放内存资源
  • void close() throws IOException

 OutputStream的基本方法:

  • 向输出流中写入一个字节数据,该字节数据为参数b的低8位
  • void write(int b) throws IOException
  • 将一个字节类型的数组中的从指定位置(off)开始的 len个字节写入到输出流
  • void write(byte[] b, int off, int len) throws IOException
  • 关闭流释放内存资源
  • void close() throws IOException

📌 ReaderWriter的子类都是字符流, 主要处理字符或字符串, 字符流处理单元为1个字符。

Reader 的基本方法:

  •  读取一个字符并以整数的形式返回, 如果返回-1已到输入流的末尾。
  • int read() throws IOException
  • 读取一系列字符并存储到一个数组buffer, 返回实际读取的字符数, 如果读取前已到输入流的末尾返回-1。
  • int read( char[] cbuf) throws IOException
  • 关闭  void close() throws IOException

 Writer 的基本方法:

  • 向输出流中写入一个字符数据,该字节数据为参数b的16位
  • void write(int c) throws IOException
  • 将一个字符类型的数组中的从指定位置(off set)开始的 length个字符写入到输出流
  • void write( char[] cbuf, int off set, int length) throws IOException
  • 关闭  void close() throws IOException


✰ 字节流读写文件

① 首先们在D盘中创建一个ddd.docx文件(9.86KB),再在E盘中创建一个eee.docx文件(空)

② 代码实现 :                                                                                                                               

 public static void main(String[] args) throws IOException {
       
        FileInputStream inputStream1 =new FileInputStream("D:/ddd.docx");//输入流
        FileOutputStream outputStream1 =new FileOutputStream("E:/eee.docx");//输出流
        int a =0 ;
        while((a=inputStream1.read())!=-1){//只要a!=-1说明还未读完
            outputStream1.write(a);//写内容(以字节为单位)
        }
        //关闭通道,否则文件一直处于打开(占用)状态
        inputStream1.close();
        outputStream1.close();
    }

③ 运行程序,可以发现我们成功将ddd.docx文件内容写到了eee.docx文件中 ( 0字节➝9.86KB )

每次读写完后记得关闭通道,否则则文件一直处于打开(占用)状态.

○ 高效文件读写 :

   创建一个byte数组,一次读byte数组长度个字节,便于提高读写效率。

public static void main(String[] args) throws IOException {
        FileInputStream inputStream =new FileInputStream("D:/demo1.txt");
        FileOutputStream outputStream=new FileOutputStream("E:/IO.txt");
        byte[] bytes =new byte[10];
        //read(bytes) 一次读byte数组长度个字节,文件内容读取完后返回-1
        //size:每次实际往数组中装入的元素的个数

        int size =0;
        while((size=inputStream.read(bytes))!=-1){
        //一次向外写出一个byte数组长度个字节内容,从指定位置开始写,写size个
            outputStream.write(bytes,0,size);
        }
        inputStream.close();
        outputStream.close();
    }


✰ 字符流读写文件

字符流只能读取文本文件

① 我们先创建一个char1.txt文本, 并写入内容进行读写测试                  

② 通过代码实现将char1.txt文本的内容读写到char2.txt文本                 

 public static void main(String[] args) throws IOException {
        FileReader reader =new FileReader("E:/char1.txt");
        FileWriter writer =new FileWriter("E:/char2.txt");
        int b = 0;
        while((b=reader.read())!=-1) {
            System.out.println(b);//打印字符编码
            writer.write(b);
        }
        reader.close();
        writer.close();
    }

字符流读取时以字符为单位,会将读到字节结合编码表转换为一个字符编码 

 ③ 我们可以将每次读取到的字符对应的编码打印出来                          

④ 成功读写内容到char2.txt文本                                 


📌 上述写法的弊端:

每次运行程序会将之前所读写的内容覆盖带掉,不能做到在原内容的基础上续写

 解决方法:在字符输出流的对象路径后加上true, 表示可续写.

 此时当我们多次运行程序时, 发现之前的所读写的内容依然存在


📌 那么我们怎样进行换行操作?

public static void main(String[] args) throws IOException {
        FileReader reader =new FileReader("E:/char1.txt");
        //保留原来的内容,在原内容基础上向后追加(续写)
        FileWriter writer =new FileWriter("E:/char2.txt",true);
        BufferedReader bufferedReader =new BufferedReader(reader);
        BufferedWriter bufferedWriter =new BufferedWriter(writer);

        String line = null ;
        while((line=bufferedReader.readLine())!=null){//只要每次读取不为空,则读取一行
            bufferedWriter.write(line);//写一行
            bufferedWriter.newLine();//插入换行符
        }
        bufferedReader.close();
        bufferedWriter.flush();
        bufferedWriter.close();//记得在读写完毕后关闭通道

    }

 注意: 

由于读取一行的方法readLine( )BufferedReader中,换行方法newLine( )BufferedWriter类中, 所以需要用到缓冲字符输入输出流。

此时当我们多次运行程序时, 会将每次读取到的内容进行换行读写, 便于记录数据。



📌 节点流与处理流    

● 按 封装类型 流又分为:

  • 节点流:直接封装的是文件, 数据.
  • 处理流:封装的是其他节点流对象; 可以提供缓冲功能, 提高读写效率.

● 节点流中常用类:

  • 字节输入流 FileInputStream
  • 字节输出流 FileOutputStream
  • 字符输入流 FileReader 
  • 字符输出流 FileWriter

处理流中常用类:

  • 缓冲字节输出流 BufferedOutputStream
  • 缓冲字节输入流 BufferedInputStream
  • 缓冲字符输入流 BufferedReader
  • 缓冲字符输出流 BufferedWriter


 代码演示:

 public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("D:/demo1.txt");
        //封装的是一个节点流对象,可以提供缓冲功能,称为处理流/包装流
        BufferedInputStream bufferedInputStream =new BufferedInputStream(inputStream,20);
        FileOutputStream outputStream =new FileOutputStream("E:/IO.txt");
        BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(outputStream,20);

        int size =0;
        byte[] bytes =new byte[10];
        while ((size=bufferedInputStream.read(bytes))!=-1){
            bufferedOutputStream.write(bytes,0,size);
        }
        bufferedInputStream.close();
        bufferedOutputStream.close();
    }

 缓存区流底层代码:


📌 对象输入输出流 ( 序列化 )   

✰ 基础理论知识

📌怎么理解对象输入输出流 ?

○ 把java中的对象输出到文件中,从文件中把对象输入到程序中.

📌为什么要这样做(目的) ?

当我们创建一个对象时, 如new Student( "小张",20 );  数据存储在对象中, 对象是在内存中存储的,一旦程序运行结束, 对象就会销毁, 有时需要将对象的信息长久保存,就需要将对象输入到文件中。

( 例如系统升级,关闭服务器时将对象保存起来,升级完毕后再重新把数据还原回来.)

📌对象的序列化和反序列化:

○ 把对象输入到文件的过程也称为对象的序列化.

○ 把对象从文件输入到程序的过程称为对象的反序列化, 反序列化时会生成一个新的对象, 所以反序列化也是创建对象的一种方式.

📌注意:

当一个类的对象需要被序列化到文件时, 这个类必须要生成一个序列化编号。

如果一个类需要被序列化到文件中, 那么这个类就需要实现Serializable接口, 实现后, 会自动的为该类生成一个序列化编号.

📌关于序列化编号的意义:

○ 编号是类的唯一标识,但是自动生成的编号在类信息改变后会重新为类生成一个编号。

○ 可以在类中显示的生成一个编号,这样类信息修改后,编号也不会改变。

📌常用类及基本方法:

  • 对象的输出流: ObjectOutputStream
  • 对象的输入流: ObjectInputStream
  • 在ObjectInputStream中用readObject()方法可以直接读取一个对象。
  • 在ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。

 生成序列化ID教程

📌 如何在IDEA中设置, 使可以在类中生成序列化ID ? ( 教程 )

 ​​​​​

📌 设置成功后, 当我们把鼠标移至类名处, 点击serialVersionUID即可生成编号


代码实践与测试

我们首先创建一个学生类,需要将学生信息序列化到文件中切记需要实现Serializable接口.

import java.io.Serializable;

//如果一个类需要被序列化到文件中,那么这个类就需要实现Serializable接口
public class Student implements Serializable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

进行序列化操作,将对象数据输出到文件中,使对象的信息可以做到持久化

       //对象输出 (对象的序列化)
        Student student =new Student("小魏",20);//创建对象
        FileOutputStream fileOutputStream  = new FileOutputStream("E:/obj.txt");
        ObjectOutput output = new ObjectOutputStream(fileOutputStream);
        output.writeObject(student);
        output.flush();
        output.close();

运行程序后发现, 对象信息保存在了文件obj.txt中(格式为乱码,但不影响最后反序列化输入结果)

 进行反序列化操作, 将之前保存在文件obj.txt的对象信息输入到程序中。

  public static void main(String[] args) throws IOException, ClassNotFoundException {
        //对象输出 (对象的序列化)
        /*
        Student student =new Student("小魏",20);//创建对象
        FileOutputStream fileOutputStream  = new FileOutputStream("E:/obj.txt");
        ObjectOutput output = new ObjectOutputStream(fileOutputStream);
        output.writeObject(student);
        output.flush();
        output.close();
        */
        //对象输入 (对象的反序列化)
        FileInputStream inputStream  = new FileInputStream("E:/obj.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        Student student = (Student) objectInputStream.readObject();//强制类型转换
        System.out.println(student);//打印对象信息

        objectInputStream.close();//记得关闭流通道
    }

 ☞ 运行结果: 



  • 35
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值