一篇文章搞定IO流(超级详细!!!)

一、IO流的分类:

(1)根据流向分为输入流和输出流:

输出:将程序(内存)中的数据输出到磁盘等存储介质中。

输入:将磁盘等存储介质中的数据输入到程序(内存)中。

(2)根据传输数据单位分为字节流和字符流:

字节流:8bit,适合传输图片,视频。

字符流:16bit,适合传输文本数据。

1120165-20170514214350379-1847340179

字节流:

  • 字节输出流:OutputSteam
  • 字节输入流:InputStream

字符流:

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

上面也是Java IO流的四大基类,这四大基类都是抽象类,Java涉及IO的流共有40多个类,但是都是通过这四大基类派生的。

有这四大基类派生的子类名称都是以父类名作为子类后缀名。

(3)根据流的角色分为节点流和处理流:

节点流:直接在数据源或目的地读写数据。

image-20211016100537947

处理流:不直接连接到数据源或目的地,而是”连接“在已存在的流(节点流或者处理流)上,通过对数据的处理为程序提供更强大的读写功能。

image-20211016100548995

简单来说就是,节点流是用来发送或接受数据的,而处理流是用来增强这个传输过程的

二、IO流的四大基类

IO流的体系结构:(底色为蓝色的是重点)

image-20211016003311360

(1)InputStream

  1. 典型实现:FileInputStream

  2. 传输单位:字节(8bit)

  3. 适合读取非文本文件

相关方法:

//从输入流读取数据的下一个字节,返回0到255范围内的整数,如果到达流的末尾,则返回-1
int read();
//从输入流将最多b.length个字节的数据读入byte数组中,如果到达流的末尾,则返回-1,否则返回实际读取的字节数
int read(byte[] b)
//将输入流中最多 len 个数据字节读入 byte 数组中,尝试读取 len 个字节,但读取的字节也可能小于该值。
//以整数形式返回实际读取的字节数。如果到达流的末尾,则返回-1。
int read(byte[] b, int off, int len)
//关闭此输入流并释放与该流关联的所有系统资源
void close() throws IOException

(2 )Reader

  1. 典型实现:FileReader

  2. 传输单位:字符(16bit)

  3. 适合读取文本文件

相关方法:

//读取单个字符,作为整数读取的字符,范围在0到65535之间(2个字节),如果已到达流的末尾,则返回-1
int read();
//将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
int read(char[] cbuf)
//将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。
int read(char[] cbuf,int off,int len)
//关闭此输入流并释放与该流关联的所有系统资源。
void close() throws IOException

(3)OutputStream

  1. 典型实现:FileOutputStream
  2. 传输单位:字节(8bit)
  3. 适合传输非文本文件
//将b.length 个字节从指定的byte 数组写入此输出流。
void write(byte[] b)
//将指定byte 数组中从偏移量off 开始的len 个字节写入此输出流
void write(byte[] b,int off,int len)
//刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。
public void flush()throws IOException
//关闭此输出流并释放与该流关联的所有系统资源
public void close() throws IOException

(4)Writer

  1. 典型实现:FileWriter
  2. 传输单位:字符(16bit)
  3. 适合传输文本文件
//写入字符数组
void write(char[] cbuf)
//写入字符数组的某一部分。从off开始,写入len个字符
void write(char[] cbuf,int off,int len)
//写入字符串
void write(String str)
//写入字符串的某一部分。
void write(String str,int off,int len)
//刷新该流的缓冲,则立即将它们写入预期目标。
void flush()
//关闭输出流
void close()

三、节点流

也叫文件流。包含 FileInputStream,FileOutputStream,FileReader,FileWriter

(1)FileReader

使用FileReader读取文本文件,并输出到控制台

FileReader reader = null;

try {
    
    
    File file = new File("src\\hello.txt");
    reader = new FileReader(file);

    int data;
    while((data = reader.read()) != -1){
    
    
        System.out.print((char)data);
    }
} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    try {
    
    
        //关闭流
        if(reader != null){
    
    
            reader.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

使用数组进行缓冲:

FileReader reader = null;
try {
    
    
    File file = new File("src\\hello.txt");
    reader = new FileReader(file);

    char[] cbuf = new char[5];
    int len;
    while((len = reader.read(cbuf)) != -1){
    
    
    	//转换成String,方便输出
        String str = new String(cbuf, 0, len);
        System.out.print(str);
    }
} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    try {
    
    
        if(reader != null){
    
    
            reader.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

(2)FileWriter

使用FileWriter将字符串写到文件中

FileWriter writer = null;
try {
    
    
    File file = new File("src\\hello.txt");

    //这里为true表示在原来文件后面进行追加,而不是覆盖
    writer = new FileWriter(file,true);
    writer.write("i am ironman");
    writer.write("i am spiderman");

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    if(writer != null){
    
    
        try {
    
    
            writer.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

使用FileReader和FileWriter实现文件的复制:

Reader reader = null;
Writer writer = null;

try {
    
    
    File srcFile = new File("src\\srcFile.txt");
    File destFile = new File("src\\destFile.txt");

    reader = new FileReader(srcFile);
    writer = new FileWriter(destFile,true);

    char[] cbuf = new char[1024];
    int len;
    while((len = reader.read(cbuf)) != -1){
    
    
        writer.write(cbuf,0,len);
    }

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    try {
    
    
        if(reader != null){
    
    
            reader.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
    try{
    
    
        if(writer != null){
    
    
            writer.close();
        }
    }catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

(3)FileInputStream& FileOutStream

通过前面的学习,对于FileInputStream和FileOutStream其实也是依葫芦画瓢的做法,下面是复制一张图片的过程:

FileInputStream inputStream = null;
FileOutputStream outputStream = null;

try {
    
    
    File srcFile = new File("src\\onepunch.jpg");
    File destFile = new File("src\\newonepunch.jpg");

    inputStream = new FileInputStream(srcFile);
    outputStream = new FileOutputStream(destFile);

    byte[] cbuff = new byte[1024];
    int len;
    while((len = inputStream.read(cbuff)) != -1){
    
    
        outputStream.write(cbuff);
    }
} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    try {
    
    
        if(inputStream != null){
    
    
            inputStream.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
    try {
    
    
        if(outputStream != null){
    
    
            outputStream.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

四、处理流

(1)缓冲流

作用:提高流的读取和写入速度。

注意:在资源关闭的时候,关闭外层流(处理流)后,会自动帮我们关闭掉内层流(节点流)。如果想要手动关闭节点流的话,就要先关闭处理流,然后再关闭节点流,这个顺序不能颠倒。

示例:

FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
    
    
    File srcFile = new File("src\\srcFile.txt");
    File destFile = new File("src\\destFile.txt");

    //创建节点流
    fis = new FileInputStream(srcFile);
    fos = new FileOutputStream(destFile);

    //创建缓冲流
    bis = new BufferedInputStream(fis);
    bos = new BufferedOutputStream(fos);

    byte[] buff = new byte[1024];
    int len;
    while((len = bis.read(buff)) != -1){
    
    
        bos.write(buff);
    }

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    //可以只关闭处理流
    try {
    
    
        if(bis != null){
    
    
            bis.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
    try {
    
    
        if(bos != null){
    
    
            bos.close();
        }
    } catch (IOException e) {
    
    
        e.printStackTrace();
    }
}

关于BufferReader和BufferWriter都类似,这里也就不再赘述。不过对于字符缓冲流这里有另一种写法:

// 创建缓冲流对象:它是处理流,是对节点流的包装
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
String str;
while ((str = br.readLine()) != null) {
    
     // 一次读取字符文本文件的一行字符
bw.write(str); // 一次写入一行字符串
bw.newLine(); // 写入行分隔符
}
bw.flush(); // 刷新缓冲区

(2)转换流

  • 实现字节流和字符流之间的转换。

两个转换流:

  • InputStreamReader:将InputStream转换为Reader
  • OutputStreamWriter:将Writer转换为OutputStream

很多时候都会使用转换流来处理文本乱码问题。

BufferedReader br = null;
BufferedWriter bw = null;

try {
    
    
    FileInputStream fis = new FileInputStream("src\\dbcp.txt");
    FileOutputStream fos = new FileOutputStream("src\\dbcp_new.txt");

    InputStreamReader isr = new InputStreamReader(fis, "GBK");
    OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");

    br = new BufferedReader(isr);
    bw = new BufferedWriter(osw);

    String str = null;
    while((str = br.readLine()) != null){
    
    
        bw.write(str);
        bw.newLine();
        bw.flush();
    }

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    if(bw != null){
    
    
        try {
    
    
            bw.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    if(br != null){
    
    
        try {
    
    
            br.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

(3)输入输出流

System.in:是System中一个InputStream类型的静态变量

public final static InputStream in = null;

System.out:是System中一个PrintStream类型的静态变量,而PrintStream继承FilterOutputStream,FilterOutputStream继承了OutputStream。

public final static PrintStream out = null;

(4)打印流

打印流:PrintSystemPrintWriter

上面说的System.out就是一个PrintStream类型。

里面提供了一系列重载的print()和println()方法,用于多种数据类型的输出。

PrintSystem和PrintWriter的输出不会抛出IOException异常,并且有自动flush功能。

(5)数据流

数据流:DataInputStreamDataOutputStream

用于读取和写出基本数据类型和String类型的数据。

image-20211021000733676

示例:

DataOutputStream dos = null;
DataInputStream dis = null;

try {
    
    
    //写文件
    dos = new DataOutputStream(new FileOutputStream("src\\data.txt"));
    dos.writeUTF("oymn");
    dos.writeInt(19);
    dos.writeBoolean(true);

    //读文件(需要按照存储顺序)
    dis = new DataInputStream(new FileInputStream("src\\data.txt"));
    String name = dis.readUTF();
    int age = dis.readInt();
    boolean isMale = dis.readBoolean();
    System.out.println(name + " " + age + " " + isMale);

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    if(dos != null){
    
    
        try {
    
    
            dos.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    if(dis != null){
    
    
        try {
    
    
            dis.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

(6)对象流

对象流:ObjectInputStreamObjectOutputStream

用于存储和读取基本数据类型和对象。可以将对象写入数据源(序列化),也可以把对象从数据源中恢复回来(反序列化)。

ObjectInputStream 和 objectOutputStream 不能序列化static 和 transient 修饰的成员变量。

**要想一个对象是可序列化的,必须实现Serializable(推荐)或者 Externalizable 其中一个接口,同时提供一个全局常量:serializableVersionUID **(用于唯一标识对象)。

示例:

ObjectOutputStream oos = null;
ObjectInputStream ois = null;

try {
    
    
    //写对象
    oos = new ObjectOutputStream(new FileOutputStream("src\\object.data"));
    oos.writeObject(new String("我爱中国"));
    oos.writeObject(new Person());
    oos.flush();

    //读对象
    ois = new ObjectInputStream(new FileInputStream("src\\object.data"));
    String str = (String) ois.readObject();
    Person person = (Person) ois.readObject();

    System.out.println(str);
    System.out.println(person);

} catch (Exception e) {
    
    
    e.printStackTrace();
} finally {
    
    
    if(oos != null){
    
    
        try {
    
    
            oos.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    if(ois != null){
    
    
        try {
    
    
            ois.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
} 
public class Person implements Serializable {
    
    

    public static final long serialVersionUID = 43535635626L;

}

(7)随机存取文件流

随机存取文件流:RandomAccessFile,并不是继承于四大基类,而是实现了DataOutput,DataInput接口。

image-20211021102432532

从实现的接口也可以看出,RandomAccessFile既可以作为输入流,也可以作为输出流。

创建RandomAccessFile实例时需要指定一个mode参数,该参数指定 RandomAccessFile 的访问模式:

  • r: 以只读方式打开。
  • rw:打开以便读取和写入。
  • rwd:打开以便读取和写入;同步文件内容的更新。
  • rws:打开以便读取和写入;同步文件内容和元数据的更新。

RandomAccessFile 对象中包含一个记录指针,用于标识当前读写位置,默认是0,我们可以自定义读写位置:

//将文件指针定位到pos位置
public void seek(long pos);

//获取文件指针当前的位置
public long getFilePointer();

示例:(这里是省略来写的)

RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;

try {
    
    
    raf1 = new RandomAccessFile(new File("src\\test01.txt"),"rw");
    raf2 = new RandomAccessFile(new File("src\\test02.txt"),"rw");

    byte[] buff = new byte[5];
    int len;
    while( (len = raf1.read(buff)) != -1){
    
    
        raf2.write(buff,0,len);
    }

} catch (IOException e) {
    
    
    e.printStackTrace();
} finally {
    
    
    //关闭流操作(省略)
    
}

猜你喜欢

转载自blog.csdn.net/OYMNCHR/article/details/120888448