您的位置:首页 > 编程语言 > Java开发

【JAVA IO流之字符流】

2014-10-19 09:14 155 查看
[b]一、概述。[/b]

java对数据的操作是通过流的方式。
java用于操作流的对象都在IO包中。
流按照操作数据不同分为两种,字节流和字符流。
流按照流向分为输入流,输出流。

输入输出的“入”和“出”是相当于内存来说的。

字符流:字节流读取文字字节数据后,不直接操作,而是先查指定的编码表,获取对应的文字,再对这个文字进行操作。简单来说就是字节流+码表

在IO流中,字节流的顶层父类是Writer和Reader。

[b]二、java.io.FileWriter类。[/b]

public class FileWriterextends OutputStreamWriter

Writer

  |--OutputStreamWriter

    |--FileWriter

该类是操作字符文件的流,用于将数据写入到文件中。

1.方法摘要

(1).构造方法。

有两个重要的构造方法:

FileWriter(File file)

根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file,
boolean append)

根据给定的 File 对象构造一个 FileWriter 对象。
后者相对于前者来说多了一个boolean型的参数,该参数的作用是决定写入文件的方式是追加方式还是覆写方式。

默认的构造方法(前者)构造的FileWriter流对象向文件中写入的时候会默认的将文件的内容清空然后再写入,如果使用后者并将true传入该构造方法,则写入的方式就变成了追加方式。

(2).write方法。

该类没有自己的write方法,全部从父类或者超类中继承而来的write方法。

从OutputStreamWriter中继承而来的方法:

void
write(char[] cbuf, int off, int len)

写入字符数组的某一部分。
void
write(int c)


写入单个字符。
void
write(String str,
int off, int len)

写入字符串的某一部分。
从Writer中继承而来的write方法:

void
write(char[] cbuf)

写入字符数组。
void
write(String str)

写入字符串。
(3).flush方法。

void
flush()

刷新该流的缓冲。
该方法是从OutputStreamWriter中继承而来的,作用是将流中的数据数据刷到文件中。文件关闭前会默认调用此方法。如果不调用此方法,则当缓冲区满了也会自动调用该方法。

(4).close方法。

void
close()

关闭此流,但要先刷新它。
2.flush与close比较

使用flush方法和close方法均可以保存文件,使用这两者各有什么好处?

举一个形象的例子我们在使用记事本软件的时候,会常常使用“保存”按钮保存住当前内容到文件,如果没有保存,关闭文件的时候就会出现提示信息“是否要保存文件内容?”,然后再关闭文件。在这个例子中,“保存”相当于flush的功能,而关闭文件则相当于close的功能,经常点保存按钮的目的就是为了防止断电丢失和节约内存。

因此,使用flush的目的就是为了防止断电丢失和节约内存,因此在写程序的时候,尽量写入一句刷新一句;如果文件不关闭,最明显的影响就是“删不掉文件”。

3.FileWriter细节:换行和续写

在Windows字符文件中,文件的换行符是\r\n,在linux中则为\n,这样很有可能导致同一个java程序在linux中的运行结果和在windows中的运行结果不一致。解决方法是使用System类中的方法getProperty,得到当前系统中的行分隔符。具体用法:String line_sparator=System.getProperty("line.separator");

如果想要在已存在的文件末尾添加新内容,则需要使用第二种构造方法。

4.异常处理标准模板。

 class MyBufferedReader
{
private Reader r;
private int size=1024;//默认的缓冲区大小
private char buf[];//存放字符的缓冲数组
private int pos=0;//记录当前的指针
private int count=0;//记录数组的长度
public MyBufferedReader(Reader r)
{
this.r=r;
buf=new char[size];
}
public MyBufferedReader(Reader r,int size)
{
this.r=r;
this.size=size;
buf=new char[size];
}
public int read() throws IOException
{
if(count==0)
{
count=r.read(buf);
pos=0;
}
if(count<0)
return -1;
char ch=buf[pos];
pos++;
count--;
return ch;
}
public String readLine() throws IOException
{
StringBuilder sb=new StringBuilder();
int ch;
while((ch=read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0)//这里进行健壮性判断是必不可少的,否则很有可能丢失最后一行 return sb.toString();
return null;
}
publicvoid close() throws IOException
{
r.close();
}
}


View Code
此类中,有一句应当特别注意:

在readLine方法中:

if(sb.length()!=0)//这里进行健壮性判断是必不可少的,否则很有可能丢失最后一行
return sb.toString();


这句不能丢,因为丢了这个判断很有可能会丢失最后一行(如果最后一行没有回车)。

通过模拟,可以发现,BufferedReader封装了一个缓冲数组,该缓冲数组缓存r中的数据,以提高程序对数据读取效率。该数组当然也可以自定义,之前已经演示,不赘述。

六、BufferedReader和BufferedWriter类使用了装饰模式

所谓的装饰模式是针对某一类进行功能增强的类。在这里,BufferedReader类对Reader(及其子类)类进行了功能增强,BufferedWriter类对Writer类(及其子类)进行了功能增强。

使用装饰模式和使用继承都能达到增强某一类的功能的目的,但是使用装饰模式更加灵活。装饰模式是针对某一类进行功能增强,而如果使用继承设计模式,则只能针对某个特别的类进行功能增强。比如,如果使用继承达到功能增强的目的,则继承层次将会变成这样:

Reader

  |--InputStreamReader

    |--FileReader

      |--BufferedFileReader

    |--BufferedInputStreamReader

相对于原本的继承层次:

Reader

  |--InputStreamReader

    |--FileReader

  |--BufferedReader

来说,前者很明显多了一个缓冲流,但是绝不仅仅是多了一个,因为Reader的子类很多,如果针对每个具体的类进行功能增强,则整个继承体系将会变得非常臃肿。但是如果使用装饰模式进行功能增强,则可以几乎不需要改变原本的继承层次,只需要多出一个缓冲流即可。由此可见,使用装饰模式要灵活很多,因此,如果想要针对某一类进行功能增强,最好使用装饰模式而不是使用继承的方式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: