往期推荐
IO流是什么
- Java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。
- 在Java中把不同的
输入/输出源
(键盘,文件,网络连接等)抽象表述为“流”(stream)
。 - 通过流的形式允许Java程序使用相同的方式来访问不同的输入/输出源。stram是从
起源(source)
到接收的(sink)
的有序数据。
IO流的分类
按照流的流向划分
输入流(InputStream)
: 只能从中读取数据,而不能向其写入数据。输出流(OutputStream)
:只能向其写入数据,而不能向其读取数据。
??注意:输入流和输出流是相对于程序而言的。
按照处理数据单位划分
字节流(InputStream、OutputStream)
:数据流中最小的数据单元是字节。字符流(Reader、Writer)
:数据流中最小的数据单元是字符。
按照流的角色划分
节点流
:可以从或向一个特定的地方(节点)读写数据,直接连接数据源。处理流
:不直接连接数据源,是对一个已存在的流的连接和封装,是一种典型的装饰器设计模式,使用处理流
主要是为了更方便的执行输入输出工作。
IO流的体系分类
按操作对象划分
-
文件操作:
FileInputStream、FileOutputStream、FileReader、FileWriter
等 -
管道操作:
PipedInputStream
等 -
数组操作:
- 字符数组:
CharArrayReader、CharArrayWriter
- 字节数组:
ByteArrayInputStream、ByteArrayOutputStream
- 字符数组:
-
缓冲操作:
BufferedWriter、BufferedInputStream
等 -
基本数据类型操作:
DataOutputStream、DataInputStream
-
对象序列化操作:
ObjectOutputStream、ObjectInputStream
-
转化操作:
InputStreamReader、OutputStreamWriter
-
打印控制:
PrintStream、PrintWriter
按操作方式划分
字节输入/输出流
InputStream类 和 OutputStream类
InputStream类
:是一个抽象类 ,是所有字节输入流类的父类。
OutputStream类
:是一个抽象类,是所有字节输出流的父类。
InputStream中定义的一些子类共性的成员方法
-
public abstract int read()
从输入流中读取一个数据,返回的是数据的ASCII值。 -
public int read(byte[] b)
从输入流中读取一定数量的字节,默认从第0个位置读取b.length长度的数据,参数byte[]数组表示起缓冲作用,存储每次读取的多个字节。 -
int read(byte b[], int off, int len)
从第 off 位置读取 len 长度字节的数据放到byte数组中。 -
int available()
返回可读的字节数量。 -
long skip(long n)
跳过指定个数的字节不读取。 -
public void close()
关闭此输入流并释放与该流关联的所有系统资源。
OutputStream中定义的一些子类共性的成员方法
-
void close()
关闭此输出流并释放与此流有关的所有系统资源。 -
void flush()
刷新此输出流,将缓冲区中的数据写出。 -
abstract void write(int b)
将指定的字节写入此输出流。 -
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写出。 -
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
FileInputStream类 和 FileOutPutStream类
FileInputStream
:是文件字节输入流,表示从本地文件中读取数据到内存中。
FileOutPutStream
:是文件字节输出流,表示将内存中的数据写入到本地文件中。
)
FileInputStream的使用
//1、创建一个FileInputStream对象,构造方法中传递写入数据的目的地
FileInputStream fis = new FileInputStream("C:\\Java\\test.txt");
//或通过File文件对象来创建FileInputStream
File file = new File(C:\\Java\\test.txt");
FileInputStream fis2 = new FileInputStream(file);
//2.1调用FileInputStream对象中的方法read,从该输入流中读取一个字节的数据
int s = fis.read();
System.out.println((char)s);
//2.2或循环读取整个文件数据
int data=0;
//返回数据的下一个字节,如果到达文件末尾则返回-1
while((data=fis.read())!=-1){
System.out.println((char)data);
}
//2.3或从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
byte[] b = new byte[6];
//存储到数组b中
fis.read(b);
//将字节数组转换成字符串输出
String s1 = new String(b);
System.out.println(s1);
//3.关闭流
fis.close();
复制代码
FileOutputStream的使用
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("C:\Study\aaa.tx",true);
byte[] b ={99,98,97};
//将字节数组b写入到输出流中
fos.write(b);
fos.close();
}
复制代码
ByteArrayInputStream类 和 ByteArrayOutputStream类
ByteArrayInputStream(字节数组输入流)
:是字节数组输入流,它里面包含一个内部的缓冲区(就是一个字节数组 ),该缓冲区含有从流中读取的字节。
ByteArrayOutputStream(字节数组输出流)
:是字节数组输出流,会将数据写入到定义的缓冲区中,默认缓冲区大小为32,会自动扩容。
ByteArrayInputStream的使用
public static void main(String[] args) {
byte[] bytes = {97,98,99,100};
//从字节数组的索引下标为1的位置开始读取2个元素
ByteArrayInputStream bais = new ByteArrayInputStream(bytes,1,2);
int data;
while((data =bais.read())!=-1){
System.out.print(data);
}
输出:9899
}
复制代码
ByteArrayOutputStream的使用
public static void main(String[] args) throws IOException {
//创建一个文件输入流
FileInputStream in = new FileInputStream(new File("C:\Study\aaa.txt"));
//创建一个字节数组输出流
ByteArrayOutputStream out = new ByteArrayOutputStream();
int data = 0;
//循环遍历文件输入流中的数据
while((data = in.read()) != -1){
//将数据写出到字节数组的缓冲区中
out.write(data);
}
//获取内存缓冲区中的数据
byte[] bytes = out.toByteArray();
for (int i = 0; i < bytes.length; i++) {
System.out.println((char)bytes[i]);
}
in.close();
out.close();
}
复制代码
ObjectInputStream类 和 ObjectOutpuStream类
ObjectInputStream
:是反序列化流,一般和ObjectOutputStream配合使用。
ObjectOutputStream
:将java对象序列化然后存入文件中,然后用ObjectInputStream读取出来。
class Data implements Serializable {
private int n;
public Data(int n){
this.n=n;
}
@Override
public String toString(){
return Integer.toString(n);
}
}
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Data w=new Data(2);
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out"));
//序列化对象,把对象写到worm.out里面
out.writeObject("Worm storage\n");
//序列化对象,把对象写到worm.out里面
out.writeObject(w);
out.close();
//从worm.out里面读取对象
ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out"));
//读取String对象
String s=(String)in.readObject();
//读取Data对象
Data d=(Data)in.readObject();
System.out.println(s+"Data = "+d);
}
}
复制代码
字符读取/写出流
Reader类 和 Wirter类
Reader
是用于读取字符的抽象类Writer
是用于写出字符的抽象类
Reader类常见子类有:
FileReader
:文件输入流BufferedReader
: 带缓冲区的字符输入流InputStreamReader
:实现字节流和字符流之间的转换
Writer类常见子类有:
FileWriter
:文件输出流BufferedWriter
:带缓冲区的字符输出流OutputStreamWriter
:实现字符流转换为字节
Reader一些子类共性的方法
-
int read(CharBuffer target)
读取字节到字符缓存中。 -
int read()
读取单个字符。 -
int read(char cbuf[])
读取字符到指定的 char 数组中。 -
int read(char cbuf[], int off, int len)
从 off 位置读取 len 长度的字符到 char 数组中。 -
long skip(long n)
跳过指定长度的字符数量不读取。
Writer一些子类共性的方法
-
void write(int c)
写入一个字符。 -
void write(char cbuf[])
写入一个字符数组。 -
void write(char cbuf[], int off, int len)
从字符数组的 off 位置写入 len 数量的字符。 -
void write(String str)
写入一个字符串。 -
void write(String str, int off, int len)
从字符串的 off 位置写入 len 数量的字符。 -
Writer append(CharSequence csq)
以追加的方式写入一个字符序列。
FileReader类 和 FileWriter类
FileReader的构造方法
-
FileReader(String fileName)
根据本地文件的全路径名创建一个文件字符输入流。 -
FileReader(File file)
根据一个File对象创建一个文件字符输入流。 -
FileReader(String fileName, Charset charset)
根据本地文件的全路径名创建一个文件字符输入流,并指定字符集。 -
FileReader(File file, Charset charset)
根据一个File对象创建一个文件字符输入流,并指定字符集。
FileReader的使用
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader(new File("C:\Study\aaa.txt"));
int data;
while ((data =fileReader.read()) != -1) {
System.out.println(data);
}
}
复制代码
FileWriter的构造方法
-
FileWriter(String fileName)
根据存在的文件的全路径文件名称来创建一个文件字符写出流。 -
FileWriter(String fileName, boolean append)
以上一个构造方法相比多了一个append,表示是否追加写,默认为否。 -
FileWriter(File file)
根据一个文件对象File来创建一个FileWriter文件字符写出流。 -
FileWriter(String fileName, Charset charset)
根据指定的编码和文件名称创建一个文件字符写出流。
FileWriter的使用
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter(new File("C:\Study\aaa.txt"));
fw.write("FileWriter");
fw.close();
}
复制代码
缓冲流
- 字节缓冲流:
BufferedOutputStream
字节缓冲输出流,BufferedInputStream
字节缓冲输入流. - 字符缓冲流:
BufferedReader
字符缓冲输出流,BufferedWriter
字符缓冲输入流.
BufferedInputStream类 和 BufferedOutputStream类
BufferedInputStream构造方法
-
BufferedInputStream(InputStream in)
根据InputStream输入流创建一个 BufferedInputStream。 -
BufferedInputStream(InputStream in, int size)
根据InputStream输入流和指定的缓冲区大小创建一个BufferedInputStream。
BufferedOutputStream构造和常用方法
-
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 -
BufferedOutputStream(OutputStream out, int size)
创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 -
void flush()
使用flush刷新,才会把缓冲区中的字节全部写入文件中。 -
void write(byte[] b, int off, int len)
从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。 -
void write(int b)
将指定的字节写入缓冲的输出流。
使用 BufferInputStream 和 BufferOutputStream 实现非文本文件的复制
@Test
public void testBufferedStream(){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File("test.jpg");
File destFile = new File("test4.jpg");
//2.造流
//2.1造节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//2.2造缓冲流,可以合并书写
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.文件读取、写出操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
BufferedReader类 和 BufferedWriter类
BufferedReader的构造方法
-
BufferedReader(Reader in)
创建默认大小的输入缓冲区缓冲字符输入流。 -
BufferedReader(Reader in, int sz)
创建一个使用指定大小为sz的输入缓冲区的缓冲字符输入流。
BufferedWriterr的构造方法
-
BufferedWriter(Writer out)
根据字符写出流创建一个字符缓冲写出流,缓冲区默认大小是8192个字符。 -
BufferedWriter(Writer out, int sz)
根据字符写出流和指定的缓冲区大小创建一个字符缓冲写出流。
使用 BufferedReader 和 BufferedWriter 实现文本文件的复制
@Test
public void testBufferedReaderBufferedWriter(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建文件和相应的流
br = new BufferedReader(new FileReader(new File("dbcp.txt")));
bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));
//读写操作
//方式一:使用char[]数组
// char[] cbuf = new char[1024];
// int len;
// while((len = br.read(cbuf)) != -1){
// bw.write(cbuf,0,len);
// // bw.flush();
// }
//方式二:使用String
String data;
while((data = br.readLine()) != null){
//方法一:
// bw.write(data + "\n");//data中不包含换行符
//方法二:
bw.write(data);//data中不包含换行符
bw.newLine();//提供换行的操作
}
} 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();
}
}
}
}
复制代码
转换流
-
转换流提供了在字节流和字符流之间的转换
-
Java API提供了两个转换流:
InputstreamReader
:将Inputstream
转换为Reader
OutputStreamWriter
:将Writer
转换为OutputStream
-
字节流中的数据都是字符时,转成字符流操作更高效。
-
很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
InputstreamReader类 和 OutputStreamWriter类
nputStreamReader的构造方法
-
InputStreamReader(InputStream in)
创建一个使用默认字符集的InputStreamReader流。 -
InputStreamReader(InputStream in, Charset cs)
创建一个使用给定字符集的InputStreamReader。 -
InputStreamReader(InputStream in, String charsetName)
创建一个使用命名字符集的InputStreamReader。
public static void main(String[] args) throws IOException {
//a:创建InputStreamWriter对象,构造方法中传递字节输入流InputStream和指定的编码
FileInputStream fis = new FileInputStream("C:\Study\a.txt");
InputStreamReader isw = new InputStreamReader(fis,"utf-8");
//:使用InputStreamWriter对象中的方法read读文件
int len=0;
while((len=isw.read())!=-1){
System.out.print((char)len);
}
//c:释放资源
isw.close();
fis.close();
}
复制代码
OutputStreamWriter的构造方法
-
OutputStreamWriter(OutputStream out)
创建一个使用默认字符编码的OutputStreamWriter流。 -
OutputStreamWriter(OutputStream out, Charset cs)
创建一个使用给定字符集的OutputStreamWriter流。 -
OutputStreamWriter(OutputStream out, String charsetName)
创建一个使用命名字符集的OutputStreamWriter流。
public static void main(String[] args) throws IOException{
//a:创建OutputStreamWriter对象,构造方法中传递字节输出流OutputStream和指定的编码表
FileOutputStream os = new FileOutputStream("C:\Study\a.txt",true);
OutputStreamWriter osw = new OutputStreamWriter(os,"utf-8");
//b:使用OutputStreamWriter对象中的方法write,把字符转换成字节存储到缓冲区中(编码)
osw.write("Akiang");
//c:使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
osw.flush();
//释放资源
osw.close();
}
复制代码
对象流
ObjectOutputStream
:内存中的对象—>存储中的文件、通过网络传输出去:序列化过程ObjectInputStream
:存储中的文件、通过网络接收过来 —>内存中的对象:反序列化过程
对象的序列化
-
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
-
序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
-
序列化是RMI(Remote Method Invoke-远程方法调用)过程的参数和返回值都必须实现的机制,RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础。
-
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出
NotserializableEXception
异常- Serializable
- Externalizable
-
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID
用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。故建议显式声明。
-
简单来说,Java 的序列化机制是通过在运行时判断类的
serialversionUID
来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的serialversionUID
与本地相应实体类的serialversionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException
)
实现序列化的对象所属的类需要满足
- 需要实现接口:
Serializable
(标识接口) - 当前类提供一个全局常量:
serialVersionUID
(序列版本号) - 除了当前
Person
类需要实现Serializable
接口之外,还必须保证其内部所属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
补充:ObjectOutputStream
和 ObjectInputStream
不能序列化 static
和 transient
修饰的成员变量
对象流的使用
序列化代码实现
序列化:将对象写入磁盘或进行网络传输
要求被序列化对象必须实现序列化
@Test
public void testObjectOutputStream(){
ObjectOutputStream oos = null;
try {
//1.创建对象,创建流
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
//2.操作流
oos.writeObject(new String("我爱北京天安门"));
oos.flush();//刷新操作
oos.writeObject(new Person("王铭",23));
oos.flush();
oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null){
//3.关闭流
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
反序列化代码实现
反序列化:将磁盘的对象数据源读出
@Test
public void testObjectInputStream(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
String str = (String) obj;
Person p = (Person) ois.readObject();
Person p1 = (Person) ois.readObject();
System.out.println(str);
System.out.println(p);
System.out.println(p1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
?以上就是对Java中IO流的简单介绍,如果有错误的地方,还请留言指正,如果觉得本文对你有帮助那就点个赞吧??