概述
流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流分类
按流的方向分类:
输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)
输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)
按处理的数据单元分类:
字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream
字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter
按处理对象不同分类:
节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等
处理流(包装流):不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,
如BufferedInputStream、BufferedReader等
java流类图结构
学习IO流注意把握四大抽象基类:InputStream、OutputStream、Reader、Writer
InputStream
所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。PipedInputStream 是从与其它线程共用的管道中读取数据
ObjectInputStream 和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。意思是FileInputStream类可以通过一个String路径名创建一个对象,FileInputStream(String name)。而DataInputStream必须装饰一个类才能返回一个对象,DataInputStream(InputStream in)
OutputStream
所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据,
ObjectOutputStream 和所有FilterOutputStream的子类都是装饰流。
Reader
所有的输入字符流的父类,它是一个抽象类。
CharReader、StringReader是两种基本的介质流,它们分别将Char 数组、String中读取数据。PipedReader 是从与其它线程共用的管道中读取数据。
BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号
InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。
Writer
所有的输出字符流的父类,它是一个抽象类。
CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。
PipedWriter 是向与其它线程共用的管道中写入数据,
BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
常用类:
FileInputStream/FileOutputStream
节点流:以字节为单位直接操作“文件”
FileReader/FileWriter
节点流:以字符为单位直接操作“文本文件”(注意:只能读写文本文件)
BufferedInputStream/BufferedOutputStream
处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率
BufferedReader/BufferedWriter
处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率
InputStreamReader/OutputStreamWriter
处理流:将字节流对象转化成字符流对象(转换流)
DataInputStream/DataOutputStream
处理流:以字节为单位直接操作“基本数据类型与字符串类型”
ObjectInputStream/ObjectOutputStream
处理流:以字节为单位直接操作“对象”
PrintStream
处理流:将OutputStream进行包装,可以方便地输出字符,更加灵活
操作步骤:
- 1.创建源
- 2.选择流
- 3.操作(读取或写出)
- 4.关闭流
Java IO流对象
1.字节输入流InputStream
常用方法:
read() 读取单个字节 返回值为读取内容 返回类型int 结束返回-1
read(byte[]) 读取一段byte数组长度的字节 返回值为读取长度 返回类型int 结束返回-1
demo 单字节读取
package IO;
/**
* 字节输入流
* 单字节读取
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class InputStreamTest {
public static void main(String[] args) {
//1.创建源
File src = new File("d:/1.txt"); //内容是abc def
//2.选择流
InputStream is = null;
try {
is = new FileInputStream(src);
StringBuilder sb = new StringBuilder(); //读取的字节存到sb中
//3.操作(读取)
int temp = 0;
while((temp=is.read())!=-1) {
sb.append((char)(temp));
}
System.out.println(sb); //abc def
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//4.关闭流
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
第一个异常是在is = new FileInputStream(src)创建流时可能出现FileNotFoundException
第二个异常是在temp=is.read()读取时可能无法读取或者流已经关闭等原因出现IOException
第三个异常是关闭流时可能出现IOException(注意之前可能会发生流没创建成功就报异常,关流
也就没有意义 所以判断下流不为null)
*/
demo 一段一段的读(其他部分都和一个字节一个字节读取相同)
//3.操作(读取)
byte[] buf = new byte[1024]; //1024个字节即1kb
int len = -1; //接收长度
while((len=is.read(buf))!=-1) {
//字节数组到字符串(解码)
String newstr = new String(buf,0,len);
System.out.println(newstr); //abc def
}
2.字节输出流OutputStream
常用方法:
write() 写出单个字节 无返回值
write(byte[],int off,int len) 从off开始写出len长度字节
demo 字节数组 也可以一个字节一个字节读 加个循环即可
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 字节输入流
* 还是创建源 选择流 操作 关流
* @author chen
*
*/
public class OutputStreamTest {
public static void main(String[] args) {
File dest = new File("d:/2.txt"); //不存在会自动创建
OutputStream os = null;
try {
os = new FileOutputStream(dest); //默认是覆盖模式
//OutputStream os = new FileOutputStream(dest,true);追加模式
String str = "hello java"; //要写的数据
//字符串转字节数组(编码)
byte[] buf = str.getBytes();
os.write(buf, 0, buf.length);
os.flush(); //写出时注意刷新
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(os!=null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制文件
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 字节流复制文件(mp3)
* @author chen
*
*/
public class CopyFileStream {
public static void main(String[] args) {
File src = new File("d:/1.mp3");
File dest = new File("e:/2.mp3");
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(src);
os = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int len = -1;
while((len=is.read(buf))!=-1) {
os.write(buf,0,len);
os.flush(); //写一次刷一次
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(os!=null) {
os.close();
}
if(is!=null) {
is.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
字节流可以处理如文本、图片、音频、视频等所有格式的文件,但是在处理涉及中文的文本时由于编码格式的问题可能会出现乱码问题,所以处理纯文本时使用字符流。
3.字符输入流Reader
Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致
常用方法
read() 读取单个字符 返回值为读取内容 返回类型int 结束返回-1
read(char[]) 读取一段char数组长度的字符 返回值为读取长度 返回类型int 结束返回-1
read(char[] cbuf, int off, int len)
demo 字符数组 也可以 单个字符
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
* 字符输入流
* @author chen
*
*/
public class FileReaderTest {
public static void main(String[] args) {
File src = new File("d:/1.txt");
FileReader fr = null;
try {
fr = new FileReader(src);
char[] buf = new char[1024];
int len = -1;
while((len=fr.read(buf))!=-1) {
String str = new String(buf,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
if(fr!=null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.字符输出流Writer
常用方法
write(int c) 写出单个字符 无返回值
write(byte[]) 写出字符数组 无返回值
write(byte[],int off,int len)
//与字节输出流相比增加了写字符串方法
write(String str) 写一个字符串
write(String str, int off, int len)
demo
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* 字符输出流
* @author chen
*
*/
public class FileWriterTest {
public static void main(String[] args) {
File dest = new File("d:/3.txt");
FileWriter fw = null;
try {
fw = new FileWriter(dest);
String str = "你好呀 java";
char[] buf = str.toCharArray();
fw.write(buf);
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fw!= null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
常用处理流
字节缓冲流BufferedInputStream/BufferedOutputStream
与字节流方法一样,只不过增加了缓冲区,提高了效率 注意关闭流时关闭bis/bos即可
demo 复制文件
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteBufferedCopy {
public static void main(String[] args) {
File src = new File("d:/1.mp3");
File dest = new File("e:/3.mp3");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buf = new byte[1024];
int len = -1;
while((len = bis.read(buf)) != -1) {
bos.write(buf);
bos.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bis != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符缓冲流BufferedReader/BufferedWriter
与字符流基本形同,只不过增加了readLine读一行的方法和newLine换行方法,注意关闭流时关闭br/bs即可
//BufferedReader中:
read() 读取单个字符 返回读取内容int 结束返回-1
read(char[] cbuf, int off, int len)
//增加了readline()方法 可以一次读取一行
readLine() 读取一行数据 返回数据 返回类型String
BufferedWriter中
write(int c)
write(char[] cbuf, int off, int len)
write(String s)
write(String s, int off, int len)
//增加了换行
newLine()
demo 复制文件
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharBufferCopy {
public static void main(String[] args) {
File src = new File("d:/1.txt");
File dest = new File("d:/4.txt");
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(src));
bw = new BufferedWriter(new FileWriter(dest));
String str = null;
while((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流转字符流InputStreamReader/OutputStreamWriter
作用:
(1)将字节流转换为字符流方便操作(前提是字节流是纯文本的情况)
(2)处理过程中可以指定编码格式(创建时可以指定字符集InputStreamReader(InputStream in, Charset cs) )
demo:键盘录入输出到控制台
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* 转换流
* InputStreamReader
* OutputStreamWriter
* 键盘录入直接输出到控制台
* @author chen
*
*/
public class InputStreamReaderTest {
public static void main(String[] args) {
//操作字节输入输出流System.in和System.out
InputStreamReader isr = new InputStreamReader(System.in); //键盘录入字节流 转换为字符流
OutputStreamWriter osr = new OutputStreamWriter(System.out); //输出
BufferedReader br = new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(osr);
//循环获取键盘输入(over结束)
String str = null;
try {
while(!(str=br.readLine()).equals("over")) {
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
其他处理流
数据流DataInputStream/DataOutputStream
数据流将“基本数据类型与字符串类型”作为数据源,实现不仅可以读写数据,还可以同时把数据的类型进行读写
注意:要先写出后读取并且读取的顺序与写出的顺序一致
demo
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataStreamTest {
public static void main(String[] args) {
File src = new File("d:/data.txt");
File dest = new File("d:/data.txt");
DataInputStream dis = null;
DataOutputStream dos = null;
try {
dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(src)));
dos = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(dest)));
//将以下数据写入到文件中
dos.writeChar('a');
dos.writeInt(10);
dos.writeUTF("陈陈陈"); //utf8编码格式写汉字
dos.flush();
//直接读取数据,读取的顺序与写入的顺序一致。否则不能读取
char c = dis.readChar();
int i = dis.readInt();
String str = dis.readUTF();
System.out.println(c);
System.out.println(i);
System.out.println(str);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(dos!=null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(dis!=null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对象流(ObjectInputStream/ObjectOutputStream)
除了像数据流那样读写基本数据类型和字符串外还可以读取写出任意对象,包含自定义对象
序列化: 把Java对象转换为字节序列
反序列化: 把字节序列恢复为Java对象的过程
对象序列化的作用:
(1)持久化: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,
比如:休眠的实现。以后服务器session管理,hibernate将对象持久化实现
(2)网络通信:在网络上传送对象的字节序列
比如:服务器之间的数据通信、对象传递
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
注意:
要先写出后读取并且读取的顺序与写出的顺序一致
只有实现了Serializable接口的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用
demo
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class ObjectStreamTest {
public static void main(String[] args) {
File src = new File("d:/9.txt");
File dest = new File("d:/9.txt");
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
ois = new ObjectInputStream(new FileInputStream(src));
oos = new ObjectOutputStream(new FileOutputStream(dest));
//序列化
oos.writeObject("chencc");
oos.writeObject(new Person("chen",20));
oos.flush();
//反序列化
Object strobj = ois.readObject();
Object perobj = ois.readObject();
if(strobj instanceof String) {
System.out.println((String)strobj);
}
if(perobj instanceof Person) {
Person p = (Person)perobj;
System.out.println(p.getName()+" "+p.getAge());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(oos!=null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(ois!=null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//自定义对象 要想序列化必须实现Serializable接口
class Person implements Serializable{
//产生一个随机固定的id
private static final long serialVersionUID = 1L;
private String name;
private transient int age; 被transient修饰的属性不参与序列化
public Person(String name,int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
打印流PrintStream
可以把内容打印到文件或者输出流中,增强了输出流的功能而已
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public class PrintStreamTest {
public static void main(String[] args) {
//System.out就是打印流
PrintStream ps = System.out;
ps.println("java"); //和System.out.println("java") 一样
ps.println("你好");
//也可以打印到文件,指定编码
try {
ps = new PrintStream("d:/haha.txt","utf-8");
ps.println("java");
ps.println("你好");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}finally {
if(ps!=null) {
ps.close();
}
}
//可以打印到输出流
try {
ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("d:/heihei.txt")),true);//true表示自动flush
ps.println("java");
ps.println("你好");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if(ps!=null) {
ps.close();
}
}
//重定向输出端(也可以重定向输入端)
System.setOut(ps); //重定向到上面的文件heihei.txt了
System.out.println("abc");
}
}
CommonsIO
Apache-commons工具包中提供了IOUtils/FileUtils,封装了大量的IO流操作,可以让我们非常方便的对文件和目录进行操作
下载地址:http://commons.apache.org/proper/commons-io/ 用的时候直接查API即可
demo
import java.io.File;
import org.apache.commons.io.FileUtils;
public class TestCommons01 {
public static void main(String[] args) {
//文件大小
//自己写的话还要用递归 这个已经封装好了
long len = FileUtils.sizeOf(new File("d:/1.txt"));
System.out.println(len);
}
}