[本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.youkuaiyun.com/jesson20121020]
流的概念和基本分类:
流的概念:
流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据的源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流动一样”。
流的分类:
按数据方向分:输入流和输出流
输入流:InputStream/Reader
输出流:OutputStream/Writer
按数据类型分:字节流和字符流
字节流:InputSteam/OutputStream
字符流:Reader/Writer
字节流和字符流的区别:
1. 字节流读取的时候,读到一个字节就返回一个字节。字符流使用了字节流读到了一个或多个字节(中文对应的字节是两个,UTF-8码表中是三个)时,先去查指定的编码表,将查到的字符返回。
2. 字节流可以处理所有类型的数据,如图片,mp3等。而字符流只能处理字符数据。
3. 字节流输入流都是以InputStream结尾,字节流输出流都是以OutputStream结尾,在InputStream或者OutputStream前面代表这个流的作用。字符流输入流都是以Reader结尾,字符流输出流都是以WriterStream结尾,相同与字节流前面也是代表这个流的作用。实际上字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的,但是字符流在操作时会用到缓冲区,是能过缓冲区来操作文件的。
注意,只要是算是纯文本数据,就要优先考虑使用字符流,除此之外,都要使用字节流了。
常用流:
字节输入流:
InputStream类为所有字节流的父类。
三个基本的read()方法:
int read(); // 从流里读出一个字节。不推荐使用。
int read(byte[] b); // 将数据读入到字节数组中,并返回所读的字节数。
int read(byte[] b,int off,int len); // off 从哪里开始读取,len读取多少字节。将输入流中off 位置处最多len个数据字节读入到字节数组中。
其他的一些常用方法:
void close(); // 关闭此输入流并释放与该流关联的所有的系统资源。
int available(); // 返回不受阻塞地从此输入流读取的字节数。
long skip(long n); // 跳过和放弃此输入流中的n个数据字节,该方法可能失效。
boolean markSupported(); // 测试此输入流是否支持mark和reset方法。
void mark(int n); // 在此输入流中标记当前的位置。
void reset(); // 将此流重新定位到对此输入流最后调用mark方法时的位置。
字节输出流:
OutputStream类是所有字节输出流的父类。
三个基本的write()方法。
void write(int n); // 将指定的字节写入到此输出流中。
void write(byte[] b); // 将b.length个字节从指定的字节数组中写入此输出流中。
void write(byte[] b,int off,int len); // 将指定字节数组中从偏移量off开始的len个字节写入到此输出流中。
其他的一些常用方法:
void close(); // 关闭此输出流并释放与此流有关的所有系统资源。
void flush(); // 刷新此输出流并强制写出所有缓冲的输出字节。
文件输入输出流:
FileInputStream和FileOutputStream
要构造一个FileInputStream,所关联的文件必须存在而且是可读的。如:
FileInputStream fis = new FileInputStream("myfile.dat");
要构造一个FileOutputStream,而输出文件如果已经存在,则它会被覆盖。如:
FileOutputStream fos = new FileOutputStream("result.dat");
如果要想以追加的方式写入文件,则需要一个参数,即:
FileOutputStream fos = new FileOutputStream("result.dat",true); // 参数为true时,则表示输出时,以追加的方式;为false时,则同上。
字符流:
Reader和Writer是所有字符流的父类型。
java技术使用Unicode来表示字符串和字符,而且提供的16位版本的流,以便用类似的方法处理字符。
桥梁流:
InputStreamReader和OutputStreamWriter(字节流转化成字符流的桥转换器)
这两个类不是用于直接输入输出的,他是将字节流转换成字符流的桥转换器,并可以指定编解码方式。
逐行读写流:
以上两个都是过滤流,需要用其他的节点流来作参数构造对象。
BufferedReader的方法:String readLine(); // 当他的返回值是null时,就表示读取完毕了。 要注意,再写入时要注意写换行符,否则会出现阻塞。
BufferedWriter的方法:newLine();这个方法会写出一个换行符。
管道流:
线程交互的时候使用。
PipedInputStream/PipedOutputStream传送输出流可以转接到传送输入流,以创建通信管道。传送输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream对象,并由其他线程从连接的PipedInputStream读取。
注意:管道输出流和管道输入流需要对接。
数据流:
DataInputStream和DataOutputStream; 通过流来读写Java基本类,注意DataInputStream和DataOutputStream的方法是成对的。支持直接输出输入各种数据类型。
注意,使用这两个类时,需要注意写入的顺序和读取的顺序相同,否则会将没有分割写入的信息分割不正确而读取出错误的数据。
对象流:
ObjectInputStream和ObjectOutputStream(实现对象序列化)。 对象流是过滤流,需要节点流作参数来构造对象,用于直接把对象写入文件和从文件中读取对象。
只有实现了Serializable接口的类型的对象才可以被读写,Serializable接口是个标记接口,其中没有定义方法。
对象会序列化一个二进制代码,文件中保存对象的属性。
writeObject(o),readObject()这两个是对象读写操作时用的方法。如:
一个类中有其他类型的对象,那么,这个类实现了Serializable接口,在对象序列化时,也同样要求这个类中属性都能够对象序列化。
注意:
对于对象流的操作,在写对象时要一次写入完毕,如果使用追加模式写入,只会读取到上一次写入的对象,使用对象流写入 时,会先写入一个头部,然后写入数据,最后加上结束符号,如果使用追加方式写入的话,那就会在结束符号继续向下写入,但是在读取时只会读到结束符为止,以后再次写入的数据就会丢失。
包名,类名和属性可以被序列化,方法和构造器不会被序列化的。
静态属性不会被序列化的。
属性会被递归序列化的,也就是一个类中有引用类型的属性,如果这个属性对应的类实现了Serializable接口,在对象序列化时,也同样会对这个类中的属性进行对象序列化。
下面以一个例子来演示上述各种流的用法:
IODemo.java
package com.jesson.mianshi.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Reader;
import javax.sound.sampled.Line;
import org.w3c.dom.CDATASection;
public class IODemo {
private final static String path = "./testRead.dat";
private static FileInputStream fis;
private static FileOutputStream fos;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 测试read方法
byteReadFun();
// 测试write方法
byteWriteFun();
// 测试FileReader方法
charReaderFun();
// 测试桥梁流
bridgeStream();
// 测试逐行读写流
bufferedFun();
// 测试数据流
dataStream();
// 测试对象流
objectStream();
// 测试管道流
pipeFun();
}
/**
* 字节输入流的三种read方法和其他的一些常用方法测试
*/
private static void byteReadFun() {
try {
/**
* 方式1,每次读取一字节,读取整个文件,可以看出效率很底
*/
fis = new FileInputStream(path);
int readByte = fis.read();
while (readByte != -1) {
System.out.print(readByte);
readByte = fis.read();
}
fis.close();
System.out
.println("\n-----------------------------------------------------------------------------------------");
/**
* 方式2, 每次撷取指定大小字节到数组中,其中,数组的大小可以任意指定
*/
fis = new FileInputStream(path);
byte[] b = new byte[100];
while (fis.read(b) != -1) {
for (int i = 0; i < b.length && b[i] != '\0'; i++) {
System.out.print(b[i]);
}
}
fis.close();
System.out
.println("\n-----------------------------------------------------------------------------------------");
/**
* 方式3, 每次读取指定大小字节到数组中,并指定写入数组的初始位置和长度,其中,数组的大小可以任意指定
*/
fis = new FileInputStream(path);
byte[] b1 = new byte[100];
fis.read(b1, 0, 20);
for (int i = 0; i < b1.length; i++)
System.out.print(b1[i]);
fis.close();
System.out
.println("\n-----------------------------------------------------------------------------------------");
/**
* 测试skip,mark,rest方法的使用
*/
fis = new FileInputStream(path);
fis.skip(9);
int len = 10;
while (len-- > 0) {
System.out.print(fis.read());
}
// 测试是否支持mark和reset方法
System.out.println("\n是否支持mark和reset:" + fis.markSupported());
System.out
.println("-----------------------------------------------------------------------------------------");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 字节输出流的三种write方法和其他的一些常用的方法测试
*/
private static void byteWriteFun() {
try {
/**
* 方式1:将指定的字节写入此输出流
*/
fos = new FileOutputStream("./testWrite.dat");
fos.write(20);
/**
* 方式2:将指定的b.length个字节从指定的字节数组写入此输出流中
*/
String outString = "Hello world!";
byte[] b = outString.getBytes();
fos.write(b);
/**
* 方式3:将指定字节数组中从偏移量off开始的len个字节写入此输出流中
*/
String outString2 = "Hello jesson!";
byte[] b2 = outString2.getBytes();
fos.write(b2, 4, 5);
fos.close();
/**
* 方式4:如果想以追加的方式写入同一个文件,则需要一个参数,并且默认为false,即以覆盖的方式添加
*/
fos = new FileOutputStream("./testWrite.dat");
fos.write(200);
fos.flush(); // 刷新此输出流并强制写出所有缓冲的输出字节
fos.close(); // 关闭此输出流并释放与此流有关的的有系统资源
fos = new FileOutputStream("./testWrite.dat", true);
String outString3 = "Hello java!";
fos.write(outString3.getBytes());
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 字符输入输出流测试
*/
private static void charReaderFun() {
try {
FileWriter fWriter = new FileWriter("./testWriter.dat");
fWriter.write("Hello,java,hello io!\n");
fWriter.close();
FileReader fileReader = new FileReader("./testWriter.dat");
char[] cbuf = new char[50];
int len = fileReader.read(cbuf);
System.out.println(new String(cbuf, 0, len));
fileReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
/**
* 桥梁流,即 InputStreamReader和OutputStreamWriter
*/
private static void bridgeStream() {
try {
fis = new FileInputStream(path);
InputStreamReader isr = new InputStreamReader(fis);
char[] cbuf = new char[100];
isr.read(cbuf);
System.out.println(cbuf);
isr.close();
fis.close();
fos = new FileOutputStream("./testWrite.dat");
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("good night!\n");
osw.close();
fos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
/**
* 逐行读写流:BufferedReader和BufferedWriter
*/
private static void bufferedFun() {
try {
FileWriter fWriter = new FileWriter("./testWriter1.dat");
BufferedWriter bWriter = new BufferedWriter(fWriter);
bWriter.write("我好,这个是逐行读写流");
bWriter.newLine();
bWriter.write("BufferedWriter");
bWriter.newLine();
bWriter.close();
fWriter.close();
FileReader fReader = new FileReader("./testWriter1.dat");
BufferedReader bReader = new BufferedReader(fReader);
String line = bReader.readLine();
while (line != null) {
System.out.println(line);
line = bReader.readLine();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
/**
* 测试管道流,主要是用于线程交互时
*/
private static void pipeFun() {
System.out.println("管道流测试");
try {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
pis.connect(pos);
new Thread(new ReadThread(pis)).start();
new Thread(new WriteThread(pos)).start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 读线程
*
* @author jesson
*
*/
static class ReadThread implements Runnable {
PipedInputStream pis;
public ReadThread(PipedInputStream pis) {
// TODO Auto-generated constructor stub
this.pis = pis;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
byte[] buf = new byte[1024];
System.out.println("读取前......没有数据......阻塞");
int len = pis.read(buf);
System.out.println("读到数据......阻塞结束");
String s = new String(buf, 0, len);
System.out.println(s);
pis.close();
} catch (IOException e) {
throw new RuntimeException("管道读取流失败");
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
}
/**
* 写线程
*
* @author jesson
*
*/
static class WriteThread implements Runnable {
PipedOutputStream pos;
public WriteThread(PipedOutputStream pos) {
// TODO Auto-generated constructor stub
this.pos = pos;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println("开始写入数据,等待6秒后.");
Thread.sleep(6000);
pos.write("hello pipeStream!\n".getBytes());
pos.close();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 测试数据流
*/
private static void dataStream() {
Member[] members = { new Member("jesson", 24),
new Member("cherry", 23), new Member("john", 32) };
System.out.println("测试数据流");
System.out.println("操作前的数据");
// 输出还原后的数据
for (Member member : members) {
System.out.printf("%s\t%d\n", member.getName(), member.getAge());
}
try {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(
"testDataStream.txt"));
for (Member member : members) {
dos.writeUTF(member.getName());
dos.writeInt(member.getAge());
}
// 将所有的数据写至目的地
dos.flush();
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream(
"testDataStream.txt"));
for (int i = 0; i < members.length; i++) {
String name = dis.readUTF();
int age = dis.readInt();
members[i] = new Member(name, age);
}
dis.close();
System.out.println("还原后的数据");
// 输出还原后的数据
for (Member member : members) {
System.out
.printf("%s\t%d\n", member.getName(), member.getAge());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
/**
* 测试对象流
*/
private static void objectStream() {
System.out.println("测试对象流");
try {
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("testObject.txt"));
Object object = new Student("张三", 25, "西电");
oos.writeObject(object);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"testObject.txt"));
Student student = (Student) ois.readObject();
ois.close();
System.out.println(student.getName() + "\t" + student.getAge()
+ "\t" + student.getSchool());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out
.println("-----------------------------------------------------------------------------------------");
}
}
在测试数据流时,用到了Member类,现给出Member.java
Member.java
package com.jesson.mianshi.io;
public class Member {
private String name;
private int age;
public Member() {
// TODO Auto-generated constructor stub
}
public Member(String name,int age){
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;
}
}
在测试对象流时,用到了实现序列化的类
Student.java
package com.jesson.mianshi.io;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/***
* 序列化该类,用于传输保存等
* @author jesson
*
*/
public class Student implements Serializable{
private String name;
private int age;
private String school;
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String name,int age,String school){
this.name = name;
this.age = age;
this.school = school;
}
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;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
/**
* 定义writeObject方法,将对象写入流中,用于实现对象的持久化
* @param out
*/
private void writeObject(ObjectOutputStream out){
try {
out.writeUTF(name);
out.writeInt(age);
out.writeUTF(school);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 定义readObject方法,用于从流中读取对象
* @param in
*/
private void readObject(ObjectInputStream in){
try {
name = in.readUTF();
age = in.readInt();
school = in.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}