输入输出流(IO流)_字符流、字节流、节点流、处理流、缓冲流、序列化与反序列化

# 输入输出流(IO流)

1.File类

​ java.io.File,使用该类的构造函数就可以创建文件对象,将硬盘中一个具体的文件以Java对象的形式来表示。

File类常用方法

方法名 描述
public File(String pathName) 根据路径创建对象
public String getName() 获取文件名
public String getParent() 获取文件所在目录
public String getParentFile() 获取文件所在目录对应的File对象
public String getPath() 获取文件路径
public boolean exists() 判断文件是否存在
public boolean isDirectory() 判断对象是否为目录
public boolean isFile() 判断对象是否为文件
public long length() 获取文件的大小
public boolean createNewFile() 根据当前对象创建新文件
public boolean delete() 删除对象
public boolean mkdir() 根据当前对象创建目录
public boolean renameTo(File file) 重命名
//构造方法创建File对象
File file = new File("D://File .txt");
System.out.println(file);

//判断文件是否存在
System.out.println(file.exists());

//创建文件
File file1=new File("D://File2.txt");
//方法定义的异常如果直接继承自Exception,实际调用的时候需要手动处理(捕获异常\丢给虚拟机去处理)
try {
    //输出创建是否成功
    System.out.println(file1.createNewFile());
} catch (IOException e) {
    e.printStackTrace();
}

//重命名文件
file1.compareTo(new File("D://File1.txt"));
System.out.println(file1);

//删除文件
file1.delete();
System.out.println(file1.exists());

​ 注意:创建文件和重命名文件

2.IO流

IO流的分类

​ 按照方向分,可以分为输入流输出流

​ 按照单位分,可以分为字节流字符流

​ 按照功能分,可以分为节点流处理流

3.字节流

​ 按照方向可以分为输入字节流(InputStream)和输出字节流(OutputtStream)

1.输入字节流(InputStream)

public abstract class InputStream implements Closeable {
	......
}

​ 可以看出InputStream是个抽象类,不能直接使用构造方法实例化,只能实例化它的非抽象子类;还可以看出实现了Closeable接口。

2.输入字节流(InputStream)的常见方法

​ InputStream是抽象类,不能直接实例化,可以实例化它的非抽象子类FileInputStream

方法 描述
int read() 以字节为单位读取数据
int read(byte b[]) 将数据存入byte类型数组并返回数组长度
int read(byte b[],int off,int len) 将数据存入byte数组的指定区间内,返回数组长度
byte[] readAllBytes() 将所有数据存入byte类型数组并返回
int available() 返回当前数组流未读取的数据个数
void close() 关闭数据流
package iotest;

import java.io.*;

public class InputStreamDemo {
    //InputStream继承自Exception
    public static void main(String[] args) throws Exception{
        //File对象是用来映射D://File.txt文件的,将文件映射成对象才能进行操作
        File file =new File("D://File.txt");
        if(file.exists()){
            //多态,父类引用指向子类对象(InputStream是抽象类,不能直接实例化)
            InputStream inputStream=new FileInputStream(file);

           /* long length=file.length();
            for(int i=0;i<length;i++){
                //输出文中第i个字母的ASCII值
                System.out.println(inputStream.read());
            }*/

            /*int temp=0;
            //当inputStream.read()=-1时代表数据读完了
            while((temp=inputStream.read())!=-1){
                System.out.println(temp+"剩余数据"+inputStream.available()+"个");
            }*/

//            byte[] byteArray=inputStream.readAllBytes();
            byte[] byteArray=new byte[(int)file.length()];
            byteArray=inputStream.readAllBytes();
            for(int i=0;i<byteArray.length;i++){
                //将ASCII进行强制类型转换成char类型
                char c=(char)byteArray[i];
                System.out.print(c);
            }
			//关闭流
            inputStream.close();
        }
    }
}

​ 首先,我们要区分几个概念:文件、文件中的字符、File对象、InputStream对象。Java是面向对象的语言,而且是一门高级语言,所以无法直接对文件进行操作,所以需要File类通过底层将文件映射成File对象才能对文件进行操作。我们可以把File对象看成一个箱子,文件中的字符就是箱子中的球,InputStream对象可以看成球外出的管道。当你使用read()或者readAllBytes()以字节为单位读取了全部或者部分File对象中的字符之后,File对象中会不剩下字符或者只会剩下未读取的部分。

​ 当我们new一个java流对象之后,不仅在计算机内存中创建了一个相应类的实例对象。而且,还占用了相应的系统资源,比如:文件句柄、端口、数据库连接等。在内存中的实例对象,当没有引用指向的时候,java垃圾收集器会按照相应的策略自动回收,但是却无法对系统资源进行释放。所以,我们需要主动调用close()方法释放java流对象。在java7之后,可以使用try-with-resources语句来释放java流对象,从而避免了try-catch-finally语句的繁琐,尤其是在finally子句中,close()方法也会抛出异常。

3.输出字符流(OutputStream)

public abstract class OutputStream implements Closeable, Flushable {
    ......
}

​ 可以看出OutputStream是一个抽象类,所以不能直接实例化,只能实例化它的非抽象子类。还可以看出OutputStream实现了Closeable和 Flushable接口。补充:Flushable接口作用(输出的时候可以使用缓冲流,速度更快,效率更高)。

4.输出字节流(OutputStream)的常见方法

​ OutputStream是抽象类,不能直接实例化,可以实例化它的非抽象子类FileOutputStream

方法 描述
void write(int b) 以字节为单位输出数据
void write(byte b[]) 将byte数组中的数据输出
void write(byte b[],int off,int len) 将byte数组中指定区间的数据输出
void close() 关闭数据流
void flush() 将缓冲流中的数据同步到输出流中
package iotest;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class OutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //输出流自动创建文件,会自动覆盖上次创建的同名文件
        OutputStream outputStream=new FileOutputStream("D://File1.txt");
        byte[] byteArray={'j','a','v','a'};
        outputStream.write(byteArray);
        //清空缓冲区存入文件
        outputStream.flush();
        outputStream.close();
    }
}

2.字符流

​ 字节流是单位时间内处理(输入+输出)一个字节数据的IO流。

​ 字符流是单位时间内处理(输入+输出)一个字符数据的IO流。

​ 字符流同样也可以分为输入字符流(Reader)和输出字符流(Writer)。

1.输入字符流(Reader)

public abstract class Reader implements Readable, Closeable {
	private static final int TRANSFER_BUFFER_SIZE = 8192;
	......
}

​ 可以看出Reader是一个抽象类,不能直接实例化,只能实例化它的非抽象子类。可以看出Reader类实现了Readable和Closeable接口。

​ Readable接口是把数据以字符的形式读入缓冲区

2.常用方法

​ Reader是一个抽象类,不能直接实例化,我们可以实例化它的非抽象子类FileReader。需要注意的是FileReader并不是Reader的直接子类,它的父类继承自Reader的直接子类InputStreamReader。

​ InputStreamReader的功能是将字节输入流转化为字符输入流。我们知道按照功能分IO流可以分为节点流和处理流。InputStreamReader是一个处理流,他处理的节点流失InputStream。

示例1:

package iotest;

import java.io.*;

public class ReaderDemo {
    public static void main(String[] args) throws IOException {
        Reader reader=new FileReader("D://File.txt");
        int temp;
        System.out.println("字符流读取:");
        while((temp=reader.read())!=-1){
            System.out.println(temp);
        }
        reader.close();

        InputStream inputStream=new FileInputStream("D://File.txt");
        int temp1;
        System.out.println("字节流读取:");
        while((temp1=inputStream.read())!=-1){
            System.out.println(temp1);
        }
        reader.close();
    }
}

​ 在UTF-8编码下,英文和英文符号一个字符就是一个字节,汉字一个字符是三个字节。例:读取文件中是“你好”,结果如下:

image-20211127170445510

示例2:

package iotest;

import java.io.*;

public class ReaderDemo {
    public static void main(String[] args) throws IOException {
        File file=new File("D://File.txt");
        Reader reader=new FileReader("D://File.txt");
        char[] charArray=new char[(int)file.length()];
        //以字符位单位读取文件,并将字符存在字符数组中
        reader.read(charArray);
        System.out.println("字符数组:"+charArray.length);
        for(char charN:charArray){
            System.out.print(charN);
        }
		reader.close();
    }
}

​ 读取的文件中存储的是“你好Java”,可以看到字符数组长度为6,两个汉字字符和四个英文字符。而且,read()方法返回的是int,直接将字符转化为字节返回。read(char[] charArray)返回的是字符数组,不会转成字节。

image-20211127172121614

3.输出字符流(Writer)

public abstract class Writer implements Appendable, Closeable, Flushable {
	......
}

​ 可以看出Writer是一个抽象类,不能直接实例化,只能实例化它的非抽象子类。可以看出Reader类实现了Appendable接口、Closeable接口和Flushable接口。

​ Appendable接口的作用是将char类型的数据读入到数据缓冲区中。

4.常用方法

​ Writer是一个抽象类,不能直接实例化,我们可以实例化它的非抽象子类FileWriter。需要注意的是FileWriter并不是Reader的直接子类,它的父类继承自Writer的直接子类OutputStreamWriter。

​ OutPutStream同样是一个处理流,将输出字节流转成输出字符流。

package iotest;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriterDemo {
    public static void main(String[] args) throws IOException {
        Writer writer=new FileWriter("D://File1.txt");
        writer.write("你好");
        char[] charArrays={'你','好','世','界'};
        writer.write(charArrays);
        String str="HelloWord!你好世界!";
        writer.write(str,10,5);
        writer.flush();
        writer.close();
    }
}

5.InputStreamReader

package iotest;

import java.io.*;

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        File file=new File("D://File.txt");
        //基础管道,输入字节流,InputStream
        InputStream inputStream =new FileInputStream(file);
        //处理流,InputStreamReader
        InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
        char[] charArray=new char[(int)file.length()];
        //读取文件,以字符为单位存储到数组
        inputStreamReader.read(charArray);
        for(char charN:charArray){
            System.out.print(charN);
        }
        String str=new String(charArray);
        System.out.println(str);

        //关闭流
        inputStreamReader.close();
        inputStream.close();
    }
}

6.OutStreamWriter

package iotest;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class OutStreamWriterDemo {
    public static void main(String[] args) throws IOException {
        String str="你好!HelloWord!";
        //节点流,输出字节流,OutputStream
        OutputStream outputStream=new FileOutputStream("D://File1.txt");
        //处理流,OutputStreamWriter
        OutputStreamWriter outputStreamWriter=new OutputStreamWriter(outputStream);
        outputStreamWriter.write(str);
        outputStream.flush();
        outputStreamWriter.flush();
        outputStream.close();
        outputStreamWriter.close();
    }
}

​ 注意:无论是InputStreamReader还是OutStreamWriter,都是处理流,都需要对对应的节点流进行操作。

4.缓冲流

​ 无论是字节流还是字符流,使用的时候都会频繁访问硬盘,对硬盘是一种损伤,同时效率不高。

​ 可以使用缓冲流,缓冲流自带缓冲区,可以一次性从硬盘中读取部分数据存入缓冲区,再写入内存,这样就可以有效减少对硬盘的直接访问,从而减轻对硬盘的损伤。

1.节点流和处理流

​ 按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流。

​ 节点流:可以从或者向一个特定的地方(节点)读写数据。例如InputStream、OutputStream、Reader、Writer。

​ 处理流:是对一个已经存在的流的连接和封装,通过所封装的流的功能的调用实现数据的读写。处理流的构造方法总是要带一个其他的流对象做参数。例如:

​ 节点流可以直接对接到文件对象File,而处理流不能直接对接到文件对象File。

image-20211128192706300

2.缓冲流分类

​ 缓冲流是处理流。可以分为:

​ 根据方向分为输入缓冲流和输出缓冲流。

​ 根据单位分为字符缓冲流和字节缓冲流。

image-20211128194403639

3.字节输入缓冲流(BufferedInputStream)

public
class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;
    ......
}
package iotest;

import java.io.*;

public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //文件对象
        File file=new File("D://java.txt");
        //节点流
        InputStream inputStream=new FileInputStream(file);
        //缓冲流,处理流
        BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
        int temp;
        while((temp=bufferedInputStream.read())!=-1){
            System.out.print(temp+" ");
        }

        //关闭流
        inputStream.close();
        bufferedInputStream.close();
    }
}

4.字符输入缓冲流(BufferedWirter)

public class BufferedReader extends Reader {
	......
}
package iotest;

import java.io.*;

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //文件
        File file=new File("D://java.txt");
        //节点流,字符流
        Reader reader=new FileReader(file);
        //处理流,缓冲流
        BufferedReader bufferedReader=new BufferedReader(reader);
        char[] charArrays=new char[(int)file.length()];
        //读取文件,存入数组
        bufferedReader.read(charArrays);
        for (char charArray : charArrays) {
            System.out.print(charArray);
        }

        //按行读取文件
        String str=null;
        while((str=bufferedReader.readLine())!=null)
        {
            System.out.println(str);
        }

        //关闭流
        reader.close();
        bufferedReader.close();

    }
}

5.字节输出缓冲流(BufferedOutputStreamWriter)

public class BufferedOutputStream extends FilterOutputStream {
	......
}
package iotest;

import java.io.*;

public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        String str="Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1]  。\n" +
                "Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2]  。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3]  。";
        //文件
        File file=new File("D://java1.txt");
        //节点流,输出字节流
        OutputStream outputStream=new FileOutputStream(file);
        //处理流,输出字节缓冲流
        BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
        //将字符串转为byte类型数组
        byte[] byteArrays=str.getBytes();
        bufferedOutputStream.write(byteArrays);
        //清空缓冲区
        bufferedOutputStream.flush();
        //关闭流
        outputStream.close();
        bufferedOutputStream.close();

    }
}

6.字符输出缓冲流

public class BufferedWriter extends Writer {
	......
}
package iotest;

import java.io.*;

public class BufferedWirterDemo {
    public static void main(String[] args) throws IOException {
        String str="Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1]  。\n" +
                "Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2]  。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3]  。";
        //文件
        File file=new File("D://java1.txt");
        //节点流,输出字符流
        Writer writer=new FileWriter(file);
        //处理流,字符输出缓冲流
        BufferedWriter bufferedWriter=new BufferedWriter(writer);
        bufferedWriter.write(str);
        //清空缓冲区
        bufferedWriter.flush();
        //关闭流
        bufferedWriter.close();
        writer.close();

    }
}

7.注意事项

​ 1.只要是缓冲流就是处理流,不能直接与文件对接,需要对字节流进行操作。

​ 2.记得输出流写flush()方法和关闭流。

​ 3.注意字节流的基本单位是字节,UTF-8中一个英文是1个字节,一个汉字是3个字节;字符流的基本单位是字符,UTF-8中一个英文是1个字符,一个汉字是1个字符。

5.序列化与反序列化

​ 序列化就是指将Java对象转化为字节序列的过程。

​ 反序列化就是指将字节序列恢复为Java对象的过程。

1.序列化

​ 1.实体类实现序列化接口(Serializable接口)

package iotest;

import java.io.Serializable;

public class User implements Serializable {
    int id;
    String name;
    String sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public User(int id, String name, String sex) {
        this.id = id;
        this.name = name;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

​ 2.对实例化对象进行序列化处理,通过数据流(ObjectOutputStream)写入文件中。需要注意的是ObjectOutputStream是一个处理流。

package iotest;

import java.io.*;

public class ObjectOutputStreamDemo {

    public static void main(String[] args) throws IOException {
        //对象
        User user=new User(1,"张三","男");
        //文件
        File file=new File("D://test.txt");
        //节点流,输出字节流
        OutputStream outputStream=new FileOutputStream(file);
        //处理流,对象输出字节流
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(user);
        //关闭流
        outputStream.close();
        objectOutputStream.close();


    }
}

2.反序列化

​ 将字节序列恢复为Java对象,通过数据流(ObjectInputStream)读入Java程序,同样的ObjectInputStream是个处理流。

package iotest;

import java.io.*;
import java.util.function.DoubleToIntFunction;

public class ObjctInputStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //文件
        File file=new File("D://test.txt");
        //节点流,输入字节流
        InputStream inputStream=new FileInputStream(file);
        //处理流
        ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
        User user=(User)objectInputStream.readObject();
        System.out.println(user.toString());
        //关闭流
        objectInputStream.close();
        inputStream.close();


    }
}

6.IO流的应用

​ IO流就是完成文件传输(文件上传、文件下载、发朋友圈等)

1.复制文件

package iotest;

import java.io.*;
import java.util.Scanner;

public class IODemo {
    public static void main(String[] args) throws IOException {
        System.out.println("欢迎使用文件复制系统!");
        System.out.println("文件复制请按1,退出请按0");
        Scanner scanner =new Scanner(System.in);
        int i;
        i=scanner.nextInt();
        while(i==1){
                System.out.println("请输入文件原目录:");
                String str1=scanner.next();
                System.out.println("请输入想复制到的文件地址:");
                String str2=scanner.next();
                //文件复制,返回结果
                String str=copyFile(str1,str2);
                System.out.println(str);
                System.out.println("欢迎使用文件复制系统!");
                System.out.println("文件复制请按1,退出请按0");
                i=scanner.nextInt();
        }
        System.out.println("欢迎下次使用!");
    }

    public static String copyFile(String str1,String str2) throws IOException {
        //文件a
        File file1=new File(str1);
        //文件b
        File file2=new File(str2);
        //节点流,输入流,输入到Java程序,输入字节流
        InputStream inputStream=new FileInputStream(file1);
        //处理流,缓冲流,字节输入缓冲流
        BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
        byte[] byteArrays=new byte[(int)file1.length()];
        //读取文件,存入数组
        bufferedInputStream.read(byteArrays);

        //节点流,输出流,输出到硬盘,输出字节流
        OutputStream outputStream=new FileOutputStream(file2);
        //处理流,缓冲流,字节输出缓冲流
        BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
        //写入文件
        bufferedOutputStream.write(byteArrays);
        //清空缓冲区
        bufferedOutputStream.flush();
        //关闭流
        inputStream.close();
        outputStream.close();
        bufferedInputStream.close();
        bufferedOutputStream.close();
        //判断复制后的文件是否存在
        if(file2.exists()){
            return "复制成功!";
        }else{
            return  "复制失败!";
        }
    }
}

​ 我们也可以修改方法中的一段代码使他变成边读边写的形式。

//边度边写
int temp;
while((temp=bufferedInputStream.read())!=-1){
    bufferedOutputStream.write(temp);
}

​ 使用字节流是为了能传输更多类型的数据:文本、音频、视频、照片等。

​ 字符流只能去读取文本类型的数据(txt,word等),使用字符流去读非文本类型数据,能读取,但是吧把其中的结构(字节的顺序)打散了。

7.总结

​ 1,注意区分节点流和处理流,节点流是最基础的流,所以节点流类能直接接触文件对象;处理流是对节点流进行操作的,所以处理流类不能直接接触文件对象,接触节点流类对象。

​ 2.字符流只能用于文件传输文本类型的数据,而字节流能处理文本类型和非文本类型的数据。

​ 3.使用完IO流之后一定要记得关闭,JDK1.7以上支持自动关闭IO流

参考博客与感谢

感谢:

​ B站@楠哥教你学Java

参考博客:

https://zhuanlan.zhihu.com/p/88233122

posted @ 2021-11-29 20:39  Mr_WildFire  阅读(241)  评论(0编辑  收藏  举报