FileWriter & FileReader
字符流和字节流:
字节流两个基类:InputStream OutputStream
字符流两个基类:Reader Writer
【Writer】:
既然IO流 是用于操作数据的,而数据最常见体现形式是:文件。
【需求】:在硬盘上创建一个文件写入一些文字数据。
存在一个专门用于操作文件的Writer子类对象:FileWriter
public void write(String str) throws IOException
public abstract void flush() throws IOException
public abstract void close() throws IOException
......
多个成员函数都抛出IOException,因此需要对异常进行处理。
【异常处理方式示例】
import java.io.*;
class FileWriterDemo
{
public static voidmain(String[] args) throws IOException
{
FileWriter fw =null;
try
{
//传递true参数,不覆盖已有的文件,并在已有文件末尾续写
fw = newFileWriter("demo.txt",true);
fw.write("\r\nyuwneian\r\n09009232");//\r\n是windows下换行符
}
catch(IOException e){
System.out.println(e.toString());
}
finally{//当指定文件不存在,抛出异常后对象建立失败,这时fw为空指针
if(fw!=null){//这时需要判定,否则又会抛出空指针异常。
try{
fw.close();
}catch (IOException e){
System.out.println(e.toString());
}
}
}
}
}
* // *
【Reader】:
【两种读取数据方法】:
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException{
/ *
//创建一个文件读取流对象,和指定名称的文件相关联。
//要保证文件已存在,否则会抛出异常FileNotFoundException
FileReader fr =new FileReader("demo.txt");
//read方法一次读一个字符,且读取字符编码自动往后移
int ch = 0;
while ((ch =fr.read())!=-1)
{
System.out.println((char)ch);
}
fr.close();
* /
FileReader fr =new FileReader("demo.txt");
//定义一个字符数组,用于存储读到的字符
//该read(char[])返回的是读到字符个数。
char[] buf = new char[1024];//一般定义成2K(2*1024)的整数倍
int num = 0;
while ((num=fr.read(buf))!=-1)
{
System.out.println("num="+new String(buf,0,num));//只打印有效部分
}
fr.close();
}
}
【练习】将F盘的一个文本文件复制到其他盘。
import java.io.*;
class FileCopyTest
{
public static voidmain(String[] args){
FileReader fr =null;
FileWriter fw =null;
String file ="\\SystemDemo.java";
try{
//先读取进数组
fr = newFileReader("F:\\Courseware\\JAVA\\MyJava3"+file);
fw = newFileWriter("G:"+file);
char[]buff = new char[1024*4];
int num = 0;
while((num = fr.read(buff))!=-1){
//从数组中写入流
fw.write(buff,0,num);//只需要写入有效内容部分
}
}
catch(IOException e){
//删除对应盘文件后抛出运行时异常
(newFile("G:"+file)).deletOnExit();
throw newRuntimeException("Failed");
}
finally{
try{
if(fr!=null)
fr.close();
}catch(IOException e){
}
finally{
try{
if(fw!=null){
fw.close();
System.out.println("Finished");
}
}
catch (IOException e){
}
}
}
}
}
第一部分:BufferedWriter & BufferedReader
/ *
缓冲区的出现是为了提高流的操作效率而出现的。
所以在创建缓冲区之前,必须要先有流对象。
该缓冲区提供了一个跨平台的换行方法:newLine()方法。
* // *
import java.io.*;
class BufferedWriterDemo
{
public static voidmain(String[] args) throws Exception
{
//创建一个字符写入流对象
FileWriter fw =new FileWriter("buf.txt");
//为了提高字符写入效率。加入了缓冲技术。
//只要将需要的被提高效率的流对象作为参数传递给缓冲区的构造函数即可
BufferedWriterbufw = new BufferedWriter(fw);
for (inti=1;i<5 ;i++ ){
bufw.write("abcde");
bufw.newLine();//跨平台的换行方法
bufw.flush();
}
//谨记:只要用到缓冲区,就要进行刷新。
//bufw.flush();
//实际上关闭缓冲区,就是关闭了操作该缓冲区的流对象。
bufw.close();
}
}
* // *
【字符读取流缓冲区】
该缓冲区提供了一个读行的方法,方便与对文本数据的获取:readLine();
包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
由于不包含任何行终止符(回车符),写入时可以使用newLine()进行换行操作。
原理:无论是读一行还是读取多个字符,最终使用的依然是read方法一次读一个字符。
当读到换行符时,才把之前存入数组中的数据一次读取(但是换行符只用于判断,没有放于数组中)
* // *
import java.io.*;
class BufferDemo
{
public static voidmain(String[] args) throws Exception{
FileReader fr =new FileReader("buf.txt");
BufferedReaderbufr =new BufferedReader(fr);
String str =null;
while ((str =bufr.readLine())!=null)//读取到流结尾,readLine()会返回null
{
System.out.println(str);
}
bufr.close();
}
}
* // *
【练习】:定义自己的BufferedReader代理类,实现readLine方法
* /
import java.io.*;
class MyBufferedReader
{
private FileReader r =null;
MyBufferedReader(FileReader r){
this.r= r;
}
public StringmyReadLine() throws IOException{
StringBuilder sb= new StringBuilder();
int ch ;
while((ch =this.r.read())!=-1){
if((char)ch=='\r')
continue;
if((char)ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
returnsb.toString();
return null;
}
public void myClose()throws IOException{
r.close();
}
}
class BufferDemo
{
public static voidmain(String[] args) throws IOException{
FileReader fr =new FileReader("buf.txt");
MyBufferedReadermbr = new MyBufferedReader(fr);
String line=null;
while ((line =mbr.myReadLine())!=null)
{
System.out.println(line);
}
mbr.myClose();
}
}
/ *
第二部分:装饰设计模式以及LineNumberReader
【装饰设计模式】:
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,
基于已有的功能,并提供加强功能。
自定义的该类就称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象
并基于被装饰对象的功能,提供更完善的功能。
【继承和装饰的区别】:
MyReader//专门用于读取数据的类
|--MyTextReader
|--MyBufferedTextReader
|--MyMediaReader
|--MyBufferedMediaReader
|--MyDataReader
|--MyBufferedDataReader
class MyBufferedReader
{
MyBufferedReader(MyTextReader text){}
MyBufferedReader(MyMediaReadermedia){}
}
以上类扩展性很差。找到其参数的共同类型,通过多态可以提高扩展性
class MyBufferedReader extends MyReader
{
private MyReader r;
MyBufferedReader(MyReader r){}
}
MyReader(优化后的体系:装饰)
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferedReader
装饰模式比继承更加灵活,避免了继承体系的臃肿。
而且降低了类与类之间的耦合性。
装饰类因为增强已有对象,具备的功能和已有对象是相同的,只是提供了完善
因此装饰类与被装饰类通常同属于一个体系中。
【练习】:模拟一个带行号的缓冲区对象。
import java.io.*;
class MyLineNumberReader extends MyBufferedReader
{
private Reader in ;
private int linenum =0;
MyLineNumberReader(Reader in){
super(in);
}
public intmyGetLineNumber() throws IOException{
return linenum;
}
public voidmySetLineNumber(int linenum){
this.linenum =linenum;
}
public StringmyReadLine() throws IOException{
linenum++;
returnsuper.myReadLine();
}
public voidmyClose()throws IOException{
super.myClose();
}
}
class LineNumberReaderDemo
{
public static voidmain(String[] args) throws IOException
{
FileReader fr =new FileReader("buf.txt");
MyLineNumberReader linenr = new MyLineNumberReader(fr);
String line ;
linenr.mySetLineNumber(100);
while ((line =linenr.myReadLine())!=null)
{
System.out.println(linenr.myGetLineNumber()+":"+line);
}
linenr.myClose();
}
}
* /