浅玩IO流
Java IO流
文章目录
- Java IO流
- 一、基础概念
- 二、字节流
- 2.1 字节输出流
- 2.2 字节输入流
- 三、字符流
- 3.1 、字符输入流
- 3.2、字符输出流
- 四、缓冲流
- 4.1、字节输出缓冲流
- 4.2、字节输入缓冲流
- 4.3字符缓冲输出流
- 4.4、字符缓冲输入流
- 五、转换流
- 5.1、InputStreamReader
- 5.2、OutputStreamWriter
- 六、序列化与反序列化
- 6.1、序列化(Serialization)
- 6.2、反序列化(Deserialization)
- 反序列化对象
- 注意事项
一、基础概念
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方法取字节存储到字节数组中,并将有效字节数返回并赋值给iwhile ((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中主要的转换流包括InputStreamReader
和OutputStreamWriter
。
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-8int 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-8String 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 Setterspublic 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;}@Overridepublic 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();}}
}
注意事项
serialVersionUID
: 建议为每个实现了Serializable
接口的类定义一个serialVersionUID
。这个字段用于版本控制,如果类的结构发生变化,可以通过修改serialVersionUID
来防止反序列化时出现兼容性问题。- 瞬态字段: 使用
transient
关键字可以标记某些字段不被序列化。这些字段在序列化时会被忽略,反序列化时会恢复为默认值(例如数字类型为0,引用类型为null)。 - 安全性: 序列化和反序列化可能会带来安全风险,特别是当处理不受信任的数据时。应确保只从可信来源读取序列化的数据,并考虑使用其他安全措施。