-------android培训、java培训、期待与您交流! ----------
IO流用来处理设备之间的数据传输。
流包括字节流和字符流。
字节流:数据流中最小的数据单元是字节;
字符流:数据流中最小的数据单元是字符。
字节流。
InputStream和OutputStream
InputStream、OutputStream是所有字节流的基类,是抽象类,不能实例化,提供一系列读数据和写数据的方法。
从输入流读取数据,有3种重载形式:
(1)int read():从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数,返回-1则表明读到输入流的末尾。
(2)int read(byte [] b):从输入流读取若干字节,把它们存储到字节数组中。返回读取的字节数,返回-1则表示遇到输入流的末尾。
(3)int read(byte [] b,int off,int len):从输入流读取若干字节,保存到字节数组中,返回实际读取的字节数,返回-1则表示遇到输入流的末尾。
向输出流写数据,也有3种重载形式:
(1)void write(int b):向输出流写入一个字节。
(2)void write(byte [] b):将字节数组中的所有字节写到输出流。
(3)void write(byte [] b,int off,int len):将字节数组中的若干字节写到输出流。
注意:在向文件或控制台写数据时,后面两个write方法可以减少进行物理写文件或控制台的次数,可以提高I/O操作效率。
IO操作一般流程如下:
InputStream in;
OutputStream out;
try{
处理输入流
处理输出流
}catch(IOException e){
处理IO异常
}finally{
try{
if(in!=null)
in.close();
}catch(IOException e){
处理IO异常
}finally{
try{
if(out!=null)
Out.close();
}catch(IOException e){
处理IO异常
}
}
}
ByteArrayInputStream和ByteArrayOutputStream
ByteArrayInputStream类从内存中的字节数组中读取数据,数据源是字节数组。ByteArrayOutputStream类向内存中的字
节数组中写数据,数据目的地是字节数组。
示例代码如下:
public static void readData() throws IOException{
byte [] buf=new byte[]{2,3,5,9,-1,-9};
ByteArrayInputStream bis=new ByteArrayInputStream(buf);
int ch=0;
while((ch=bis.read())!=-1){
System.out.println(ch+" ");
}
bis.close();
}
注意:read()方法读取字节类型的元素时,先将字节类型元素转换为二进制,再转换为int类型的二进制形式,最后读
取低八位字节。如-1,二进制形式为11111111,int类型的二进制形式为00000000 00000000 00000000 11111111,因此
读取的是int类型的255,因此read()方法返回的是0~255的整数。这里所说的把byte类型转换为int类型,与赋值运算中
的类型转换不同,赋值运算中,把byte类型转换为int类型,取值是不变得。
public static void writeData() throws IOException{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
bos.write(259);
bos.write("你好".getBytes());
bos.close();
}
注意:write()方法只会向输出流写入一个字节的低8位,如259的二进制形式为:00000000 00000000 00000001 00000011,执行bos.write(259)方法后,向输出流写入的是00000011,即整数3.
DataInputStream和DataOutputStream
DataInputStream用于从输入流读取基本类型数据,DataOutputStream用于向输出流写基本类型数据。
读方法都以“read”开头,加上基本数据类型的包装类,如:
readByte():读取1个8位字节,转换为byte类型返回。
readLong():读取8个字节,转换为long类型返回。
readFloat():读取4个字节,转换为float类型返回。
readUTF():读取若干字节,转换为采用UTF编码的字符串返回。
写方法以“write”开头,加上基本数据类型的包装类,如:
writeByte(byte b):写入一个byte类型数据。
writeLong(long l):写入一个long类型数据。
readFloat(float f):写入一个float类型数据。
writeUTF(String s):写入采用UTF-8字符编码的字符处。
示例代码:
public static void writeData() throws IOException{
DataOutputStream dos=new
DataOutputStream(new FileOutputStream("demo.txt"));
dos.writeByte(-2);
dos.writeLong(12);
dos.writeUTF("你好");
dos.close();
}
public static void readData() throws IOException{
DataInputStream dis=new
DataInputStream(new FileInputStream("demo.txt"));
dis.readByte();
dis.readLong();
dis.readUTF();
dis.close();
}
注意:DataInputStream读取数据的顺序必须与DataOutputStream写数据的顺序相同才能保证获得正确的数据。
PipedInputStream和PipedOutputStream
PipedInputStream和PipedOutputStream必须配套使用,通常由一个线程向管道输出流写数据,由另一个线程从管道输入
流中读取数据。
示例代码:
import java.io.*;
import java.util.*;
class Receiver extends Thread{
private PipedInputStream pis;
public Sender(PipedInputStream pis){
this.pis=pis;
}
public void run(){
try{
byte [] buf=new byte[1024];
int len=0;
while((len=pis.read(buf)))
System.out.println(new String(buf,0,len));
pis.close();
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
class Receiver extends Thread{
private PipedOutputStream pos;
public Receiver(PipedOutputStream pos){
this.pos=pos;
}
public void run(){
try{
pos.write("你好").getBytes();
pos.close();
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
class PipedStreamDemo{
public static void main(String [] args) throws Exception{
PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new PipedOutputStream();
pis.connect(pos);
Receiver r=new Receiver(pis);
r.start();
Sender s=new Sender(pos);
s.start();
}
}
在以上示例代码中,Sender向管道输出流写数据,Receiver从管道输入流读数据,当线程Receiver执行管道输入流的
read()时,如果输入流中没有数据,该线程会阻塞,只有当线程Sender向管道输出流写入新的数据后,线程Receiver才
会恢复运行。
FileInputStream和FileOutputStream
FileInputStream类从文件中读取数据,FileOutputStream类向文件中写入数据,数据源和数据目的地都是文件,即硬
盘。所以在构造其对象的时候需要指定文件。
示例代码:
private static void rwData() throws IOException{
FileInputStream fis=new FileInputStream("in.txt");
FileOutputStream fos=new FileOutputStream("out.txt");
char [] buf=new char[1024];
int len=0;
while((len=fis.read(buf))!=-1)//从in.txt文件中读取数据到buf中
{
fos.write(buf,0,len);//将buf中的数据写入到out.txt文件中
}
fis.close();
fos.close();
}
注意:在构造FileInputStream和FileOutputStream对象时,指定的文件必须存在,否则会抛出FileNotFoundException异常。
BufferedInputStream和BufferedOutputStream
BufferedInputStream类和BufferedOutputStream类覆盖了被装饰流的读写数据行为,利用缓冲区来提高读写数据效率。
BufferedInputStream类先把数据读入到缓冲区,然后read()方法只需要从缓冲区内获取数据。BufferedOutputStream类先
把数据写入到缓冲区,当缓冲区满了或者强制刷新的时候,缓冲区的数据就会写到真正的数据汇。利用
BufferedInputStream、BufferedOutputStream进行文件读写,可以减少物理性读写数据的次数。
示例代码:
Private static void rwData() throws IOException{
//写数据
FileOutputStream fos=new FileOutputStream("out.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
DataOutputStream dos=new DataOutputStream(bos);
dos.writeUTF("你好");
dos.close();
//读数据
FileInputStream fis=new FileInputStream("in.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
DataInputStream dis=new DataInputStream(bis);
dis.readUTF();
dis.close();
}
注意:BufferedInputStream、BufferedOutputStream是过滤流,在构造对象的时候要指定被装饰的输入流、输出流。
BufferedOutputStream在写数据时,如果数据大小没有超过缓冲区的大小,不会自动将缓冲区中的数据写到指定数据汇
中,如示例代码中执行完dos.writeUTF("你好")方法后,in.txt文件中没有数据,执行dis.readUTF()方法将会抛出
EOFException异常。
为了保证BufferedOutputStream会把缓冲区中的数据写到指定数据汇中,一是调用flush()方法刷新缓冲区,该方法会立
即执行一次将缓冲区中的数据写到输出流的操作。二是执行完输出流的所有write()方法后,调用close()方法关闭输出
流,close()方法会先调用本身及被装饰的输出流的flush()方法。
两者比较:前者仍然可以使用输出流进行写数据操作,而后者将释放与系统资源的关联,无法再使用输出流写数据。
字符流:Reader和Writer类是字符流的基类,是抽象类,不能被实例化,可以直接操作字符数据。读写数据方法与字
节流读写数据的方法相同,只是读写的对象是字符,而字节流读写的对象是字节。
CharArrayReader和CharArrayWriter
CharArrayReader类从内存中的字符数组中读取字符,数据源为字符数组。
CharArrayWriter类把字符写到内存中的字符数组,数据汇为字符数组。
示例代码:
private static void rwData() throws IOException{
CharArrayWriter caw=new CharArrayWriter();
caw.write('你');
caw.write('好');
char [] buf=caw.toCharArray();
caw.close();
CharArrayReader car=new CharArrayReader(buf);
int ch=0;
while((ch=car.read())!=-1)
System.out.println((char)ch);
car.close();
}
FileReader和FileWriter
FileReader类用于从文件中读取字符数据,FileWriter类用于向文件中写字符数据。这两个类只能按照本地平台的字符
编码来读写数据,不能指定其他字符编码类型,构造其对象时需要明确指定文件。
示例代码:
private static void readData() throws IOException{
FileReader fr=new FileReader("demo.txt");
int len;
char [] buf=new char[1024];
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();
}
private static void writeData() throws IOException{
FileWriter fw=new FileWriter("demo.txt",true);
fw.write("Hello");
fw.close();
}
注意:FileReader构造对象时指定的文件如果不存在,则会抛FileNotFoundException异常。FileWriter构造对象时,如果
指定文件不存在,则创建指定文件,再向文件中写数据。
BufferedReader和BufferedWriter
BufferedReader类用来装饰其他Reader类,带有缓冲区,可以先把一批数据读到缓冲区,再从缓冲区内读取数据,避免
每次都从数据源读取数据并进行字符编码转换,从而提高读操作的效率。
BufferedWriter类用来装饰其他Writer类,可以先把数据写到缓冲区内,当缓冲区满的时候,再把缓冲区内的数据写到
字符输出流中,从而提高写操作的效率。
示例代码:
private static void readData() throws IOException{
FileReader fr=new FileReader("demo.txt");
BufferedReader br=new BufferedReader(fr);
String line=null;
while((line=br.readLine())!=null)
System.out.println(line);
br.close();
}
private static void writeData() throws IOException{
FileWriter fw=new FileWriter("demo.txt");
BufferedWriter bw=new BufferedWriter(fw);
for(int i=0;i<5;i++){
bw.write("Hello "+i);
bw.newLine();
}
bw.close();
}
注意:BufferedWriter类带有缓冲区,在写数据的过程中,只有当缓冲区满的时候才会将缓冲区内的数据写到字符输出
流中,所以应该调用flush()方法刷新缓冲区来防止缓冲区有数据,但是没满情况下,数据写入失败。BufferedReader有
一个readLine()方法,一次可以读取一行字符,在读取文本文件中的文本数据时很好用,但是BufferedWriter没有响应
的writerLine()方法。
InputStreamReader和OutputStreamWriter
InputStreamReader类将InputStream类型转换为Reader类型,有以下构造方法:
InputStreamReader(InputStream in)按照本地平台的字符编码读取输入流的字符。InputStreamReader(InputStream in,String charsetName)参数charsetName指定字符编码。
如果demo.txt文件采用UTF-8字符编码,想要正确从文件中读取字符,可以按以下方式构造InputStreamReader实例:
InputStreamReader isr=
new InputStreamReader(new FileInputStream("demo.txt"),"UTF-8");
char c=(char)reader.read();
假设按以上方法从输入流中读取"好"字符,将执行以下步骤:
从输入流中读取3个字节:229、165、189,这三个字节是字符"好"的UTF-8字符编码;计算出字符"好"的Unicode字符
编码为89和125;为字符"好"分配两个字节的内存空间,分别取值为89和125.
OutputStreamWriter类将OutputStream类型转换为Writer类型,OutputStream类的构造方法与InputStream类型的构造方法
相似。
如果demo.txt文件采用UTF-8字符编码,要想正确向文件写字符,可以按以下方式构造OutputStreamWriter实例:
OutputStreamWriter osw=
new OutputStreamWriter(new FileOutputStream("demo.txt"),"UTF-8");
osw.write('好');
假设按以上方法向输出流写数据,将执行以下步骤:字符'好'在内存中占两个字节,取值分别为89和125;字符'好'的
UTF-8字符编码为229、165、189;向输出流写入229、165、189.
PrintStream和PrintWriter
PrintStream和PrintWriter类是打印输出流,可以用来装饰其他流,能输出格式化数据,写数据的方法都是以print开头,
每一个print()方法都和一个println()方法对应,如print.print(“你好”);print.println();与print.println(“你好”);以及print.print
(“你好\n”);三者是等价的。所有的print()和println()方法都不会抛出异常,可以通过checkError()方法来判断写数据是否
成功。打印输出流带有缓冲区,与缓冲流不同的是,打印输出流默认情况下只有在缓冲区满的时候才会执行物理写数
据的操作,但是用户可以决定缓冲区的行为,一些构造方法中需要指定一个autoFlush参数,如果为true,则表示执行
println()方法时会自动把缓冲区的数据写到输出流中。PrintWriter和PrintStream的println(String s)方法都能写字符串,两
者的区别是,后者只能使用本地平台默认的字符编码,而前者使用的字符编码取决于被装饰的流所使用的字符编码。
流的操作过程中,选择流的基本规律:
1、明确源和目的;
源:输入流,InputStream、Reader。
目的:输出流,OutputStream、Writer。
2、操作的数据是否是纯文本;
是,字符流;不是,字节流。
3、当体系明确后,再确定要使用哪个具体对象。
源设备:内存、硬盘、键盘;
目的设备:内存、硬盘、控制台。
需求一:将一个文本文件中的数据存储到另一个文本文件中,即复制数据。
1、源:使用InputStream、Reader,操作纯文本文件,选择Reader,由于操作对象是硬盘上的文件,所以选择
FileReader。如果需要提高效率,则定义一个BufferedReader对象。即
FileReader fr=new FileReader("a.txt");
BufferedReader br=new BufferedReader(fr);
2、目的:使用OutputStream、Writer,操作纯文本文件,选择Writer,由于操作对象时硬盘上的文件,所以选择
FileWriter。如果需要提高效率,则定义一个BufferedWriter对象。即
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bw=new BufferedWriter(fw);
需求二:将键盘录入的数据保存到一个文件中
序列化:静态不能被序列化。