----------- android培训、java培训、java学习型技术博客、期待与您交流!------------
黑马程序员——38,IO流(字符流)
一:Filewriter----》
IO流用来传输数据,相关流放在java.io包中。原本数据传输都是传输字节流,但是后来因为传输字符的要求,所以,又有一个字符流,字符流是与编码表相关的流,而字节流一般都是用在图片或者媒体文件上。
字节流的抽象基类是InputStream和OutputStream,而字符流的抽象基类是:Reader和Writer
比较常见的是FileWriter和FileReader,FileWriter是一个用来写入数据的流,的使用如下例子,注意要刷新流里面的数据,这样才可以把数据传倒目的地。而字节流是没有刷新操作的,都是直接通过流输入到目的地。
FileWriter的使用步骤:
1,先建立Filewriter流对象
2,write写入数据
3,Filewriter没有自动刷新功能,记得用flush刷新
4,close关闭资源
import java.io.*;
class Ioliou
{
public static void main(String[] args)throws IOException
//IO流代码后容易抛出异常,所以一般都会在方法上抛出IOException
{
FileWriter f=new FileWriter("f:\\yyyyyyy.txt");//直接指定文件
//FileWriter是用于操作文件的Writer类的子类对象
/*
这句话可以指定路径建立对应名字的文件,
如果指定路径中已经存在同名文件,那么原本文件会被新建文件覆盖掉。
*/
f.write("jkl"); //将字符串写入流中
/*
但是此时如果打开yyyyyyy.txt文件看的话发现里面并没有写入jkl
这是为什么呢?因为其写入的仅仅是流中,不是写入文件中
*/
f.flush();//刷新流,把流里面的数据刷新到目的地
/*
此时再打开yyyyyyy.txt文件就会看见写入的jkl
*/
f.close();//这句话作用是先刷新流,接着再关闭流
f.write("rty");//由于前面关闭了流,所以这句话是写不进流里面的
System.out.println("Hello World!");
}
}
但是这种方式会导致如果有同名文件,那么就被新文件覆盖的情况,如果不想被覆盖掉的话,就可以使用另外一种FileWriter的构造函数:FileWriter(String s,boolean b) 表示可以在原有文件后面续写数据。还有如果在写入的数据中需要换行的话用到\r\n
文件的续写:
importjava.io.*;
class Ioliou3
{
public static void main(String[] args)
{
FileWriter fi=null;
try
{
fi=new FileWriter("f:\\trtrtr.txt",true);
/*
因为新建文件会覆盖原有文件,所以,FiletWriter类提供一个构造函数
FileWriter(String s,boolean b) 表示可以在原有文件后面续写数据
*/
fi.write("我是\r\n英雄");
/*
这句话表示输入数据“我是”然后换行接着输入数据“英雄”。
为什么要在\n前面加上\r呢?因为\r\n写在一起在window系统里面才表示换行,
仅仅是\n只是在java里面才表示换行。如果写的是fi.write("我是\n英雄");
那么在java里面显示的确是会换行。但是用window系统里面的工具打开,
“我是”与“英雄”之间会有一个黑块,这是window系统无法识别单独的\n的原因。
*/
}
catch(IOException e)
{
soc(e.toString());
}
finally
{
try
{
fi.close();
}
catch(IOException e)
{
soc(e.toString());
}
}
System.out.println("HelloWorld!");
}
public static void soc(Object obj)
{
System.out.println(obj);
}
}
二:FileReader----》
FileReader是用来读取数据的,读取的方式通常是用read方法来读取的。因为io流经常会抛出异常对象,所以会用一种针对异常的机制处理,这个后面会介绍。
FileReader的使用步骤:
1,先建立FileReader流对象
2,read读取数据
3,close关闭资源
import java.io.*;
class Ioliou4
{
public static void main(String[] args)
{
FileReader re=null;
try
{
re=new FileReader("f:\\trtrtr.txt");
/*
建立一个文件读取流对象,并且指向已经存在的文件!
如果文件不存在,运行会发生FileNotFoundException
*/
while(true)//无限循环,编译运行必要时候要按Ctrl+c停止
{
int i=re.read();
char ch=(char)i;
soc(ch);
if(i==-1)
break;
int i2=re.read();
/*
read方法一次读取一个字符,返回的是int型数据,可以自动往下读取,
而且如果后面读到没有数据可读的时候就会返回-1,如果被强制转成字符打印出来的是一个问号?
*/
char ch2=(char)i2;
soc(ch2);
if(i2==-1)
break;
}
}
catch(IOException e)
{
soc(e.toString());
}
finally
{
try
{
re.close();//仅仅是关闭流
}
catch(IOException e)
{
soc(e.toString());
}
}
}
public static void soc(Object obj)//打印方法
{
System.out.print(obj);
}
}
这种读取数据的方式并不高效,所以一般都是read方法配合数组来读取数据,把数据装在数组里面,装满之后读取再装再读取,直到数据读取完为止。
importjava.io.*;
class Ioliou5
{
public static void main(String[] args)
{
FileReader fr=null;
try
{
fr=newFileReader("f:\\trtrtr.txt");
char[] ch=new char[5];
int i =0;
while((i=fr.read(ch))!=-1)
{
soc(new String(ch,0,i));
/*
new String(ch,0,i)表示新建一个字符串,
其内容为字符数组ch的从0位开始长度为i的字符组成。
*/
}
}
catch(IOException e)
{
soc(e.toString());
}
finally
{
try
{
fr.close();
}
catch(IOException e)
{
soc(e.toString());
}
}
}
public static void soc(Object obj)
{
System.out.println(obj);
}
}
按照字符数组长度来读取,返回的是读取到的字符个数,例如在本题中,长度为5的字符数组,读取的时候从第一位到第三位;填充字符,填充完了就返回填充的个数;再次调用read方法的时候,会自动往下继续读取字符,又从第一位到第三位重新填充字符。
如果在某一次填充字符过程中,字符数组还没有填充完就没有字符继续填充的情况下,这一次返回的是本次填充字符的个数,返回值也许不是字符数组长度。
如果在某一次填充字符过程中,字符数组还没有填充完就文件字符换行的情况下,本次就只是填充到换行处,返回本次填充字符个数,下一次填充字符又从第一位开始填充下一行的字符。再接下来,继续调用read方法读取,在上一次已经全部读取完字符了,那么这一次,返回值-1而读取一些比较大的文件的时候,一般定义字符数组长度1024,这样是比较方便的。
有了FileReader和FileWriter的配合使用就可以达到数据传输的效果。
复制小练习:把文件从f盘复制到d盘。
一般复制文件的步骤是先建立读取流指定读取文件,指定写入流写入目的地,然后调用相关的write方法和read方法配合数组一边读取一边写入,复制完后记得关闭资源。
该习题为了配合异常处理机制的介绍,放在了后面。
三:异常处理机制----》
以上的例子就用到了异常处理机制try…catch…模块,这就是用来处理异常对象的机制,有了这个机制就不需要在方法上声明有异常。
try
{
//被检测的语句
}
catch(异常类型 异常变量)
{
//处理异常的语句
}
finally
{
//无论是否有异常都一定要执行的语句,通常用于关闭资源,但是finally块前面有System.exit(0);表示退出程序的话,那么finally块就不会执行到了,程序强制退出。
}
以下是各个语句块之间相互配合使用:
//try…catch…一般仅仅用来处理异常
try
{
//被检测语句
}
catch(异常类型 异常变量)
{
//异常处理语句
}
//分割线---------------------------------------------
try
{
//被检测语句
}
finally
{
//一定会执行的语句,通常用来关闭资源
}
需要注意的是:每一个语句块都是变量的作用域,如果有变量定义在以上某个语句块中,那么有效作用区域就只有该语句块中,所以,如果该变量在其他语句块中也要用到的话,就把该变量一开始定义在外面,复制小练习所示:
importjava.io.*;
class Ioliou6
{
public static void main(String[] args)
{
FileWriter fw=null;//把变量定义在语句块外面,这里就是局部变量,有效作用域是这个main方法
FileReader fr=null;
try
{
fw=newFileWriter("d:\\trtrtr_copy.txt");
//建立一个写入流对象,指定存储的路径的文件
fr=newFileReader("f:\\trtrtr.txt");
//建立一个读取流对象,与目的地相关联
char[] ch=new char[1024];
int i= 0;
while((i=fr.read(ch))!=-1)
{
fw.write(ch,0,i);
/*
按照字符数组ch从0位开始长度为i把字符写进流中
*/
}
}
catch(IOException e)
{
throw new RuntimeException("读写出问题了");
//抛出一个运行异常对象
}
finally
{
try
{
if(fw!=null)
fw.close(); //close也会抛出异常,这里也有try…catch处理一下
}
catch(IOException e)
{
}
try
{
if(fr!=null)
fr.close();
}
catch (IOException e2)
{
}
}
}
public static void soc(Object obj)//打印
{
System.out.println(obj);
}
}
四:BufferedWriter----》
对于一些大的文件,我们也可以使用字符流缓冲技术。缓冲区技术是在流的基础上(必须要先有流的存在)增强了流对于数据的操作效率。打个比喻就是原本是在水龙头上一滴一滴的喝水,但是现在则是拿着杯子喝水。
我们先介绍BufferedWriter。
使用BufferedWriter的一般步骤如下:
1,先建立流对象
2,建立缓冲区对象,把流流对象放进缓冲区
3,write写入数据
4,BufferedWriter没有自动刷新功能,记得用flush刷新,BufferedWriter还有一个newLine方法用来换行的,如果有需要也可以使用。
5,close关闭资源
importjava.io.*;
class Ioliou7
{
public static void main(String[] args)
{
FileWriter fw=null;
BufferedWriter bfw=null;
try
{
fw=newFileWriter("f:\\yuyuyu.txt");
bfw=newBufferedWriter(fw);
//把写入流对象传给缓冲区构造函数
bfw.write("假面骑士01号登场!");
//实际上把字符写入流的还是fw调用底层资源实现的
bfw.newLine();
//换行
bfw.write("假面骑士02号登场!");
bfw.flush();
//这里还是要刷新才把存在流的数据传倒文件里面
}
catch(IOException e)
{
throw new RuntimeException("写操作有误");
}
finally
{
try
{
if(bfw!=null)
{
bfw.close();
//关闭流,
/*
bfw.close();本质上关闭流的还是fw底层调用close()方法实现的,
所以就不用再写fw.close();了
*/
}
}
catch(IOException e)
{
throw new RuntimeException("bfw.close();出问题抛出异常了");
}
}
}
public static void soc(Object obj)//打印方法
{
System.out.println(obj);
}
}
五:BufferedReader----》
使用步骤和BufferedWriter类似:
1,建立于目的地关联的流对象
2,建立BufferedReader缓冲区,把目的地关联的流对象放入BufferedReader的构造函数中
3,用readLine读取数据
4,关闭资源
import java.io.*;
class Ioliou8
{
publicstatic void main(String[] args)
{
FileReader fr=null;
BufferedReader bfr=null;
try
{
fr=new FileReader("e:\\JAVAwenjian\\Ioliou8.java");
bfr=new BufferedReader(fr);
String s=null;
while((s=bfr.readLine())!=null)
/*
readLine()一行一行的读取,不包含任何行终止符,如果没有数据读取就返回null,
但是不返回回车符,所以输出打印或者写入的时候要记得换行。
readLine读取数据本质上还是要调用read方法一个一个读取的
*/
{
soc(s);
}
}
catch(IOException e)
{
throw new RuntimeException("读操作出问题");
}
finally
{
try
{
if(bfr!=null)
bfr.close();
}
catch (IOException e)
{
throw new RuntimeException("读操作出问题");
}
}
}
public static void soc(Object obj)
{
System.out.println(obj);
}
}
有了以上BufferedWriter和BufferedReader,就可以更加方便的复制文件:
把e:\\JAVAwenjian\\Ioliou8.java复制到d盘中
import java.io.*;
class Ioliou9
{
public static void main(String[] args)
{
FileWriter fw=null;
FileReader fr=null;
BufferedWriter bfw=null;
BufferedReader bfr=null;
try
{
fw=newFileWriter("d:\\Ioliou8_copy.java");//建立字符流写入对象
fr=newFileReader("e:\\JAVAwenjian\\Ioliou8.java");//建立字符流读取对象
bfw=new BufferedWriter(fw);//建立字符流写入缓冲区
bfr=new BufferedReader(fr);//建立字符流读取缓冲区
String s=" ";
while((s=bfr.readLine())!=null)
{
bfw.write(s);
bfw.newLine();//换行
}
}
catch (IOException e)
{
throw new RuntimeException("复制操作出问题");
}
finally
{
try
{
if(bfw!=null)
bfw.close();
}
catch(IOException e)
{
throw new RuntimeException(" bfw.close();出问题");
}
try
{
if(bfr!=null)
bfr.close();
}
catch (IOException e2)
{
throw newRuntimeException("bfr.close();出问题");
}
}
}
public static void soc(Object obj)
{
System.out.println(obj);
}
}
六:装饰类----》
装饰类,也称为装饰设计模式。通常是自定义一个类,通过构造函数传入已有的对象,增强功能,装饰类和被装饰类都有着相同的功能,只不过,装饰类具备更强的功能。
一般装饰类与被装饰类处于同一个体系中,装饰类最好要覆盖好父类的抽象方法,注意是否有异常抛出。
以下是建立MyBufferedReader的例子:
import java.io.*;
class MyBufferedReader extends Reader//继承Reader类
{
private FileReader fr=null;//设定为全局变量,作用域是整个类
MyBufferedReader(FileReader fr)//传进来一个FileReader流的对象
{
this.fr=fr;
}
StringBuffer sb=new StringBuffer();
public String readLine()throws IOException//注意异常的抛出
{
char ch=' ';
int i=0;
while((i=fr.read())!=-1)//这里就是内部调用传进来的对象的方法
{
//因为Windows系统里面认为\r\n组合起来才是一个回车符
if(i=='\r')
continue;
if(i=='\n')
return sb.toString();
ch=(char)i;
sb.append(ch);//append是在后面添加字符,没有覆盖前面字符
}
if(sb!=null)
return sb.toString();
return null;
}
public void close()throws IOException
{
fr.close();
}
public static void soc(Object obj)
{
System.out.println(obj);
}
//下面记得要覆盖Reader类中的抽象方法
public int read(char[] cbuf,int off,int len)throws IOException
{
return fr.read(cbuf,off,len);
}
}
还有一个比较常用的流LineNumberReader,这是BufferedReader的子类,基本功能和BufferedReader差不多,只是添加了setLineNumber方法可以设置行数开始计数读取的行数,然后getLineNumber方法获取当前读取数据时候的行数是第几行。
import java.io.*;
class Ioliou12
{
public static void main(String[] args)
{
FileReader fr=null;
LineNumberReader lnr=null;
try
{
fr=newFileReader("Ioliou12.java");
//建立读取流对象
lnr= new LineNumberReader(fr);
String s=" ";
lnr.setLineNumber(123);
//设置行号从123的后一行开始
while((s=lnr.readLine())!=null)
{
soc(lnr.getLineNumber()+":"+s);
//getLineNumber方法用来获取行号
}
}
catch(IOException e)
{
throw new RuntimeException("读取操作出问题");
}
finally
{
try
{
if(inr!=null)
lnr.close();
}
catch(IOException e2)
{
throw new RuntimeException("lnr.close();出问题了");
}
}
}
public static void soc(Object obj)
{
System.out.println(obj);
}
}
编译运行结果如图1所示:
图1
----------- android培训、java培训、java学习型技术博客、期待与您交流!------------