浅玩IO流

Java IO流

一、基础概念

1、输入输出流的概念:

输入流: 从外部源(如磁盘、网络等)读取数据到内存。比如从文件读取字节数据到一个字节数组。
输出流: 从内存将数据写入到外部目标(如磁盘、网络等)。比如把字节数组的数据输出到一个磁盘文件。**缓存区概念:**在输入输出过程中,数据通常会先经过一个缓存区,以减少与外部设备的交互次数,提高效率。

2、字符流 vs 字节流
字节流: 处理原始字节数据,如 FileInputStream 和 FileOutputStream。

字符流: 处理字符数据,通常涉及字符编码转换,如 FileReader 和 FileWriter。有 write 和 read 方法的不一定是字符流,字节流也有 write 和 read 方法。例如,FileInputStream 和 FileOutputStream 也提供了 read 和 write 方法。

3、缓冲高效流:
通过在内存中维护一个缓冲区来减少与外部设备的交互次数,提高 I/O 操作的效率。
BufferedInputStream 和 BufferedOutputStream:分别用于字节输入流和字节输出流的缓冲。
BufferedReader 和 BufferedWriter:分别用于字符输入流和字符输出流的缓冲。

4、转换流:

用于在字节流和字符流之间进行转换,处理字符编码问题。
InputStreamReader:将字节输入流转换为字符输入流。
OutputStreamWriter:将字符输出流转换为字节输出流。
5、序列化与反序列化:
序列化:将对象转换为字节流,以便存储或传输。
反序列化:将字节流转换回对象。
ObjectOutputStream 和 ObjectInputStream:分别用于对象的序列化和反序列化。

二、字节流

2.1 字节输出流

包含的方法:

  • public void close()关闭输出流并释放与此流相关联的任何系统资源。
  • public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b):将b.length的字节从指定的字节数组写入此输出流
  • public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流
  • public abstract void write(int b):将指定的字节写入此输出流。 (一个字节)
  • close()关闭流对象,但是先刷新一次缓冲区,关闭之后,流对象不可以继续再使用了。
  • flush()仅仅是刷新缓冲区(一般写字符时要用,因为字符是先进入的缓冲区),流对象还可以继续使用。

关于flush方法

1)自动刷新:
FileOutputStream 会在每次调用 write 方法时立即将数据写入到文件中,因为它没有内置的缓冲机制。因此,即使你不调用 flush 方法,数据也会立即写入到文件中。
2)关闭流时的刷新:
当你调用 close 方法时,FileOutputStream 会自动调用 flush 方法(但一般是在1-10M左右刷一次,而不是说每write()一次就flush()一次,那样也会使效率变低),确保所有未写入的数据都被写入到文件中,然后再关闭流。

什么时候需要显式调用 flush 方法?
1)确保数据立即写入:
如果你需要确保数据立即写入到文件中,而不是等待流被关闭,可以显式调用 flush 方法。例如,在日志记录或实时数据处理中,你可能希望数据立即写入到文件中,以防止数据丢失。
2)使用缓冲流时:
当使用 BufferedOutputStream 时,数据会先写入到内存中的缓冲区,而不是立即写入到文件中。为了确保缓冲区中的数据被写入到文件中,你需要显式调用 flush 方法。

示例

 public static void outStream() throws IOException {
        byte[] bytes = {97, 98, 97, 99, 101, 102};
        FileOutputStream fos = new FileOutputStream("F:\\javaIO流Out类测试文件.txt");
        //这里会把字节数组写入输出流指定的文件
        fos.write(bytes);
//        fos.flush();
        fos.close();
    }

还可以定义字节录入位置和长度

public static void partOutsTream() throws IOException {
        byte[] bytes = {97, 98, 97, 99, 101, 102};
        FileOutputStream fos = new FileOutputStream("F:\\javaIO流Out类测试文件.txt");
        //定义字节录入位置和长度
        fos.write(bytes, 0, 2);
        fos.close();
    }

2.2 字节输入流

常用方法:

  • int read()从该输入流读取一个字节的数据。 读取完成后最后会返回一个-1
  • int read(byte[] b) 从该输入流读取最多 b.length个字节的数据为字节数组。
  • void close()关闭此文件输入流并释放与流相关联的任何系统资源。
  • 还有其他的…
public static void FileInputStreamTest() throws IOException {
        int i = 0;
        FileInputStream fis = new FileInputStream("F:\\javaIO流Out类测试文件.txt");
        //判断文件是否为空,若不空,则读取。这里其实是读取到的一个个字节 ACSLL 码值到了变量i,然后打印
        while ((i = fis.read()) != -1) {
            System.out.print(i + ",");
        }
        fis.close();
    }

一次读取多个字节并转成字符串

public static void FileInputStreamTest2() throws IOException {
        int i = 0;
        FileInputStream fis = new FileInputStream("F:\\javaIO流Out类测试文件.txt");
        //定义字节数组,大小是2的倍数最好
        byte[] bytes = new byte[1024];
        //调用read方法取字节存储到字节数组中,并将有效字节数返回并赋值给i
        while ((i = fis.read(bytes)) != -1) {
            //将字节数组中的值取出来用String的构造函数转换成字符串,取值范围是0到i(i为有效值)
            System.out.print(new String(bytes, 0, i));
        }
        fis.close();
    }

然后还可以输入输出流结合实现文件赋值

public static void inputAndOutputStream() throws IOException {
        //首先输入到字节数组,即用输入类
        FileInputStream fis = new FileInputStream("F:\\javaIO流Out类测试文件.txt");
        //其次输出到磁盘,即用输出类
        FileOutputStream fos = new FileOutputStream("F:\\javaIO流Out类测试文件2.txt");
        int i;
        byte[] bytes = new byte[1024];
        //i返回的是有效文件个数
        while ((i = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, i);
        }
        //先关文件输出流
        fos.close();
        fis.close();
        System.out.println("--------");
    }

三、字符流

3.1 、字符输入流

输入流Reader 子类 FileReader 继承自InputStreamReader,InputStreamReader继承自Reader
包含的方法:

  • int read() 读一个字符
  • int read(char[] cbuf) 将字符读入数组。
  • abstract int read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
  • abstract void close() 关闭流并释放与之相关联的任何系统资源。

示例

public static void fileReaderTest() throws IOException {
        FileReader fr = new FileReader("F:\\字符测试文件.txt");
        int len;
        while ((len = fr.read()) != -1) {
            System.out.println((char) len);
        }
        System.out.println("-------------");
    }

字符输入流 ,转字符串

public static void fileReaderTranlateTest() throws IOException {
        FileReader fr = new FileReader("F:\\字符测试文件.txt");
        int len;
        char[] chars = new char[1024];
        while ((len = fr.read(chars)) != -1) {
            System.out.println(new String(chars, 0, len));
        }
    }

3.2、字符输出流

Writer 子类FileWriter 继承 OutputStreamWriter 继承 Writer
包含的方法:

  • abstract void close() 关闭流,先刷新。
  • abstract void flush() 刷新流。
  • void write(char[] cbuf) 写入一个字符数组。
  • abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
  • void write(int c) 写一个字符。
  • void write(String str) 写一个字符串。
  • void write(String str, int off, int len) 写一个字符串的一部分。

示例

public static void fileWriteTest() throws IOException {
        FileWriter fw = new FileWriter("F:\\字符测试文件.txt");
        fw.write("知识点");
        fw.flush();//刷新缓冲区数据到文件中
        fw.close();
    }

四、缓冲流

4.1、字节输出缓冲流

BufferedOutputStream(OutputStream out) 继承 OutputStream
构造方法:

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
  • BufferedOutputStream(OutputStream out,int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的

数据写入指定的底层输出流

示例

public static void bufferOutPutStreamTest() throws IOException {
        FileOutputStream fos = new FileOutputStream("F:\\缓冲流测试.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bos.write("测试缓冲流".getBytes());
        bos.flush();
        bos.close();
    }

4.2、字节输入缓冲流

BufferedInputStream继承自InputStream

构造方法:

  • BufferedInputStream(InputStream in) 创建一个BufferedInputStream并保存其参数,输入流 in ,供以后使用。
  • BufferedInputStream(InputStream in, int size) 创建BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。

示例

public static void bufferInPutStreamTest() throws IOException {
        FileInputStream fis = new FileInputStream("F:\\缓冲流测试.txt");
        BufferedInputStream bis = new BufferedInputStream(fis);
        int len;
        byte[] bytes = new byte[1024];
        System.out.println("---------------");
        while ((len = bis.read(bytes)) != -1) {
            System.out.println(new String(bytes));
        }
        bis.close();
    }

4.3字符缓冲输出流

BufferedWriter继承自Write

构造方法:

  • BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
  • BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
  • 成员方法(特有): void newLine() 写一行行分隔符。
public static void bufferedWriterTest() throws IOException {
        FileWriter fw = new FileWriter("F:\\缓冲流测试文件.txt");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("测试缓冲字符流");
        bw.newLine();
        bw.write("shd");
        bw.flush();
        bw.close();
    }

4.4、字符缓冲输入流

BufferedReader继承自Reader

构造方法:

  • BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
  • BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
  • 特有的成员方法:String readLine() 读一行文字。以换行符(‘\n’),回传(‘\r’)或者回车后直接跟着换行(\r\n)作为结束行的标志,如果读取到的这一行无数据了,会返回null值,其他时候返回的是读取的数据(但是不会读取行的终止符合,即如果本来有换行,读取后是不会读取换行符)
public static void bufferedReaderTest() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("F:\\缓冲流测试文件.txt"));
        //读的是一行,所以只有一字符串为类型接收数据
        String str;
        while ((str = br.readLine()) != null) {
            System.out.println(str);
        }
        HashMap a = new HashMap();
    }

五、转换流

作用: 转换流(Conversion Streams)是用于处理字符编码转换的一类流,它们主要用于将字节流转换为字符流,或者将字符流转换为字节流。这在处理不同编码格式的文本文件时特别有用。Java中主要的转换流包括InputStreamReader和OutputStreamWriter。

在Java中,IO流提供了读写数据的功能。转换流(Conversion Streams)是用于处理字符编码转换的一类流,它们主要用于将字节流转换为字符流,或者将字符流转换为字节流。这在处理不同编码格式的文本文件时特别有用。Java中主要的转换流包括InputStreamReaderOutputStreamWriter

5.1、InputStreamReader

InputStreamReader是一个桥接器,它将一个字节输入流转换为一个字符输入流。它使用指定的字符集(如UTF-8, GBK等)来解码字节。如果没有明确指定字符集,那么将使用平台默认的字符集。

使用示例:

import java.io.*;

public class InputStreamReaderExample {
    public static void main(String[] args) {
        StringBuilder contentBuilder = new StringBuilder();

        try (InputStream is = new FileInputStream("input.txt");
             InputStreamReader isr = new InputStreamReader(is, "UTF-8")) { // 指定字符集为UTF-8
            int c;
            while ((c = isr.read()) != -1) {
                contentBuilder.append((char) c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 将内容转换为字符串
        String content = contentBuilder.toString();
        // 打印或进一步处理内容
        System.out.println(content); // 如果需要打印
    }
}

5.2、OutputStreamWriter

OutputStreamWriter同样是一个桥接器,但它的作用与InputStreamReader相反,它将一个字符输出流转换为一个字节输出流。它使用指定的字符集来编码字符。

使用示例:

import java.io.*;

public class OutputStreamWriterExample {
    public static void main(String[] args) {
        try (OutputStream os = new FileOutputStream("output.txt");
             OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8")) { // 指定字符集为UTF-8
            String content = "Hello, World!";
            osw.write(content);
            osw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

六、序列化与反序列化

在Java中,序列化(Serialization)和反序列化(Deserialization)是将对象的状态转换为字节流以便于存储或传输,以及从字节流恢复对象状态的过程。这是Java提供的一种强大的机制,用于在网络通信、文件存储等方面实现对象的持久化。

6.1、序列化(Serialization)

序列化是指将对象的状态信息转换为可以存储或传输的形式的过程。在Java中,可以通过实现 Serializable 接口来使一个类的对象能够被序列化。

实现 Serializable 接口

要使一个类的对象可以被序列化,该类需要实现 Serializable 接口。这个接口是一个标记接口,没有任何方法需要实现,但它告诉Java虚拟机(JVM)该类的对象可以被序列化。

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 可选,但推荐使用

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

序列化对象

使用 ObjectOutputStream 类可以将对象序列化并写入到一个输出流中,例如文件或网络连接。

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);
        //编译并运行 SerializationExample 类,将 Person 对象序列化到 person.ser 文件中。
        try (FileOutputStream fos = new FileOutputStream("person.ser");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(person);
            System.out.println("Object has been serialized.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6.2、反序列化(Deserialization)

反序列化是指将字节流恢复为对象的过程。在Java中,可以通过 ObjectInputStream 类从输入流中读取字节流并恢复对象。

反序列化对象

使用 ObjectInputStream 类可以从输入流中读取字节流并恢复对象。

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class DeserializationExample {
    public static void main(String[] args) {
        Person person = null;
        //从文件读取序列化得对象
        try (FileInputStream fis = new FileInputStream("person.ser");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            person = (Person) ois.readObject();
            System.out.println("Object has been deserialized: " + person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

注意事项

  1. serialVersionUID: 建议为每个实现了 Serializable 接口的类定义一个 serialVersionUID。这个字段用于版本控制,如果类的结构发生变化,可以通过修改 serialVersionUID 来防止反序列化时出现兼容性问题。
  2. 瞬态字段: 使用 transient 关键字可以标记某些字段不被序列化。这些字段在序列化时会被忽略,反序列化时会恢复为默认值(例如数字类型为0,引用类型为null)。
  3. 安全性: 序列化和反序列化可能会带来安全风险,特别是当处理不受信任的数据时。应确保只从可信来源读取序列化的数据,并考虑使用其他安全措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厌世小晨宇yu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值