黑马程序员-java学习基础加强之IO流

来源:互联网 发布:莫奈评价知乎 编辑:程序博客网 时间:2024/05/20 13:10

-------android培训java培训、期待与您交流! ----------

IO流用来处理设备之间的数据传输。

流包括字节流和字符流。

字节流:数据流中最小的数据单元是字节;

字符流:数据流中最小的数据单元是字符。

字节流。

InputStreamOutputStream

InputStreamOutputStream是所有字节流的基类,是抽象类,不能实例化,提供一系列读数据和写数据的方法。

从输入流读取数据,有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异常

}

}

}

ByteArrayInputStreamByteArrayOutputStream

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,二进制形式为11111111int类型的二进制形式为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.

DataInputStreamDataOutputStream

DataInputStream用于从输入流读取基本类型数据,DataOutputStream用于向输出流写基本类型数据。

读方法都以“read”开头,加上基本数据类型的包装类,如:

readByte():读取18位字节,转换为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写数据的顺序相同才能保证获得正确的数据。

PipedInputStreamPipedOutputStream

PipedInputStreamPipedOutputStream必须配套使用,通常由一个线程向管道输出流写数据,由另一个线程从管道输入

流中读取数据。

示例代码:

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

会恢复运行。

FileInputStreamFileOutputStream

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();

}

注意:在构造FileInputStreamFileOutputStream对象时,指定的文件必须存在,否则会抛出FileNotFoundException异常。

BufferedInputStreamBufferedOutputStream

BufferedInputStream类和BufferedOutputStream类覆盖了被装饰流的读写数据行为,利用缓冲区来提高读写数据效率。

BufferedInputStream类先把数据读入到缓冲区,然后read()方法只需要从缓冲区内获取数据。BufferedOutputStream类先

把数据写入到缓冲区,当缓冲区满了或者强制刷新的时候,缓冲区的数据就会写到真正的数据汇。利用

BufferedInputStreamBufferedOutputStream进行文件读写,可以减少物理性读写数据的次数。

示例代码:

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();

}

注意:BufferedInputStreamBufferedOutputStream是过滤流,在构造对象的时候要指定被装饰的输入流、输出流。

BufferedOutputStream在写数据时,如果数据大小没有超过缓冲区的大小,不会自动将缓冲区中的数据写到指定数据汇

中,如示例代码中执行完dos.writeUTF("你好")方法后,in.txt文件中没有数据,执行dis.readUTF()方法将会抛出

EOFException异常。

为了保证BufferedOutputStream会把缓冲区中的数据写到指定数据汇中,一是调用flush()方法刷新缓冲区,该方法会立

即执行一次将缓冲区中的数据写到输出流的操作。二是执行完输出流的所有write()方法后,调用close()方法关闭输出

流,close()方法会先调用本身及被装饰的输出流的flush()方法。

两者比较:前者仍然可以使用输出流进行写数据操作,而后者将释放与系统资源的关联,无法再使用输出流写数据。

字符流:ReaderWriter类是字符流的基类,是抽象类,不能被实例化,可以直接操作字符数据。读写数据方法与字

节流读写数据的方法相同,只是读写的对象是字符,而字节流读写的对象是字节。

CharArrayReaderCharArrayWriter

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();

}

FileReaderFileWriter

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构造对象时,如果

指定文件不存在,则创建指定文件,再向文件中写数据。

BufferedReaderBufferedWriter

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()方法。

InputStreamReaderOutputStreamWriter

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个字节:229165189,这三个字节是字符""UTF-8字符编码;计算出字符""Unicode字符

编码为89125;为字符""分配两个字节的内存空间,分别取值为89125.

OutputStreamWriter类将OutputStream类型转换为Writer类型,OutputStream类的构造方法与InputStream类型的构造方法

相似。

如果demo.txt文件采用UTF-8字符编码,要想正确向文件写字符,可以按以下方式构造OutputStreamWriter实例:

OutputStreamWriter osw=

new OutputStreamWriter(new FileOutputStream("demo.txt"),"UTF-8");

osw.write('');

假设按以上方法向输出流写数据,将执行以下步骤:字符''在内存中占两个字节,取值分别为89125;字符''

UTF-8字符编码为229165189;向输出流写入229165189.

PrintStreamPrintWriter

PrintStreamPrintWriter类是打印输出流,可以用来装饰其他流,能输出格式化数据,写数据的方法都是以print开头,

每一个print()方法都和一个println()方法对应,如print.print(“你好”);print.println();print.println(“你好”);以及print.print

(“你好\n”);三者是等价的。所有的print()println()方法都不会抛出异常,可以通过checkError()方法来判断写数据是否

成功。打印输出流带有缓冲区,与缓冲流不同的是,打印输出流默认情况下只有在缓冲区满的时候才会执行物理写数

据的操作,但是用户可以决定缓冲区的行为,一些构造方法中需要指定一个autoFlush参数,如果为true,则表示执行

println()方法时会自动把缓冲区的数据写到输出流中。PrintWriterPrintStreamprintln(String s)方法都能写字符串,两

者的区别是,后者只能使用本地平台默认的字符编码,而前者使用的字符编码取决于被装饰的流所使用的字符编码。

流的操作过程中,选择流的基本规律:

1、明确源和目的;

源:输入流,InputStreamReader

目的:输出流,OutputStreamWriter

2、操作的数据是否是纯文本;

是,字符流;不是,字节流。

3、当体系明确后,再确定要使用哪个具体对象。

源设备:内存、硬盘、键盘;

目的设备:内存、硬盘、控制台。

需求一:将一个文本文件中的数据存储到另一个文本文件中,即复制数据。

1、源:使用InputStreamReader,操作纯文本文件,选择Reader,由于操作对象是硬盘上的文件,所以选择

FileReader。如果需要提高效率,则定义一个BufferedReader对象。即

FileReader fr=new FileReader("a.txt");

BufferedReader br=new BufferedReader(fr);

2、目的:使用OutputStreamWriter,操作纯文本文件,选择Writer,由于操作对象时硬盘上的文件,所以选择

FileWriter。如果需要提高效率,则定义一个BufferedWriter对象。即

FileWriter fw=new FileWriter("b.txt");

BufferedWriter bw=new BufferedWriter(fw);

需求二:将键盘录入的数据保存到一个文件中

序列化:静态不能被序列化。