字符流的抽象基类是:Writer 和 Reader !
用于操作字符流的子类对象有:
1. FileReader 和 FileWriter .
2. 功能对象:BufferedReader 和 BufferedWriter 他们分别对应于:FileReader 和 FileWriter ,用来达到对FileReader 和 FileWriter 的高效操作,具体见下文讲解。
——————————————————————————————————————————
【FileWriter】的学习,将从如下5点展开,并通过具体示例来体现:
1. 基本方法的演示
2. 对象创建的细节。
3. 对象操作的细节:close() write() flush()
4. 续写换行
5. 文件处理是的——异常处理规范!
示例一:将一个段文字数据写入到硬盘上.
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo { public static void main(String[] args) throws IOException { /* //需求:将一个段文字数据写入到硬盘上. 思路: 1,一段文字就是字符串数据。 2,写到硬盘上,从哪到哪呢?字符串数据在内存中,写到硬盘上——将内存中的数据搞到硬盘上, 这就涉及到了设备之间的数据处理。就要用到IO技术。 既然是从内存到硬盘,应该是输出流。 3,对于文字而言,io中提供了便捷的操作,比如字符流。 4,结合两者,需要输出流,需要字符流,可以使用字符输出流。Writer 5,具体用哪个子类对象呢?硬盘上用于存储数据的体现:文件。在这个体系中有对象FileWriter 。 */ //1,通过FileWriter创建流对象。构造时,必须明确写入数据需要存储的位置。 /* * 该对象一创建,目的文件就会被创建。 * 如果该文件已经存在,会被覆盖。 * 做了什么事呢?在堆内存中创建一个对象。同时调用了系统的资源。 */ FileWriter fw = new FileWriter("demo.txt"); //2,使用字符输出流对象将字符串进行写入。调用写入方法。 //数据没有直接写入到目的文件中,而是写入到了临时缓冲中。 fw.write("abcdef"); //3,怎么把数据弄到文件中呢?发现Writer类中有一个flush()方法。刷新缓冲区,将缓冲的数据立即写入到目标中。 fw.flush(); fw.write("haha"); //4,关闭此流,关闭资源。在关闭之前,先刷一次缓冲,将数据都写入到目的中。 fw.close(); /* * flush()和close()有什么区别? * flush():仅将缓冲中的数据刷新到目的地。流对象可以继续使用。可以使用多次。 * close():将缓冲中的数据刷到目的地后,直接关闭流资源,流无法继续使用。只能使用一次。 * 在close()方法当中其实在关闭之前都会调用一次flush(); * */ } }
通过上述示例,我们必须注意以下几点:
1. 通过FileWriter创建流对象。构造时,必须明确写入数据需要存储的位置。
2. 使用字符输出流对象将字符串写入时,必须调用 writer() 方法,但是这些字符并没有直接被写到 目的地中,而是被写在了Writer类默认的缓冲区中,这可以在Java源代码中查到。
3. 将缓冲区中的数据写到目的中,需要调用flush()方法,将缓冲区刷新。
4. 在一系列操作完成后,必须要做的动作是,关闭流,释放系统资源。需调用close()。
5. 其次就是,flush()和 close()方法调用,这两者之间的区别:上文已经提到,不在赘述。
示例二:想对刚才的文件demo.txt 进行一个续写!
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo2 { private static final String LINE_SPARATOR = System.getProperty("line.separator"); public static void main(String[] args) throws IOException { /* * 需求:想要来个续写。 * 这个对象创建是不行的,因为该构造一创建,就覆盖了已有的文件。 * 可以使用另一个构造函数,加入一个boolean参数,为true,就可以实现续写。 * * 需求:想要将数据分行写入。 * * window中的特有软件比如notepad。只识别window中的特有换行 \r\n. * 为了在不同系统平台下换行。使用System类获取当前系统信息。 * * */ //该构造方式,通过第二个参数来决定是否进行续写 FileWriter fw = new FileWriter("demo2.txt",true); fw.write("xi"+LINE_SPARATOR+"xi"); fw.close(); } }
上述示例主要提示我们一下几点:
1. 如果想在原来的文本后,在继续添加文字,该怎么办?这时,可以使用FileWriter的另外一个构造方法,传入加入一个boolean参数,为true,就可以实现续写。
2. 其次,就是,当数据需要分行写入时,为了让程序在不同的系统平台上都可以运行,不能讲换行的符号写死,可以动态的获取系统的属性来取到当前系统中的换行符:
private static final String LINE_SPARATOR = System.getProperty("line.separator");
3. 无论如何都不能忘记:流不再使用的时候,必须得关闭。
在对IO系列中的FileWriter对象有了大概的了解后,我们会发现,在程序中的很多地方都会发生异常:繁多而又不同的异常。因此,我们有必要来介绍一下,Java中IO体系的异常处理规范:当然了,也是从一个示例讲起:
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo3 { public static void main(String[] args) { /* * IO异常的处理规范。 * 创建流对象—————— 在try外创建流对象的引用。 在try内对流对象进行初始化。 */ FileWriter fw = null; try { fw = new FileWriter("k:\\demo3.txt"); fw.write("abcde"); fw.flush(); } catch (IOException e) { System.out.println(e.toString()); } finally { //在关闭之间必须得判断这个流是否存在,是否被创建。 if (fw != null) try { fw.close(); } catch (IOException e) { // 相关的代码处理。比如说,将关闭失败的信息记录到日志文件中。 throw new RuntimeException("关闭失败"); } } } }
通过以上示例,我们的明白,在Java中IO异常的处理时:
1. 对象的创建方式——创建流对象时, 在try外创建流对象的引用。 在try内对流对象进行初始化。
2. 在finally的代码中,流的关闭时必须得执行的动作。但是,在关闭前,必须得判断,这个流是否创建成功,如果没有创建成功的话,何谈关闭。
3. 在处理流关闭失败后,可以进行一系列的操作:或者将关闭失败的信息记录到日志文件中。
————————————————————————————
下面我们来讲解:另一个重要的对象——FileReader。
FileReader: 这个对象的主要用途在于读取,因此,我们将着重分析,它对指定文件的读取方式,FileReader 读取字符时有两种方式:
1. 读取单个字符的方法:读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。返回值:作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1
2. 读取字符数组:将读到的字符存储到字符数组里面,并且返回字符数组的长度。如果到达流的末尾,返回-1.
下面来演示,他们的具体应用:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub FileReader fr = new FileReader("IO流.txt"); //演示读取单个字符 long time = System.currentTimeMillis(); readChar(fr); long timereadChar = System.currentTimeMillis(); System.out.println("time Read char is = " + (timereadChar-time)); //演示读取字符到缓冲区中。 long time2 = System.currentTimeMillis(); readToBuf(fr); long timeReadBuf = System.currentTimeMillis(); System.out.println("time Read to Buf is = " + (timeReadBuf-time2)); } private static void readToBuf(FileReader fr) throws IOException { //定义一个字符缓冲区,用于存放读到的字符。 char[] buf = new char[50]; //设刚开始读到的字符为0 int len = 0 ; //一直循环读取字符到缓冲区中,直到读到流的末尾。 while((len = fr.read(buf)) != -1){ //将每次读满的缓冲区中的字符,变成字符串打印出来。 System.out.println(new String(buf , 0 , len)); } } private static void readChar(FileReader fr) throws IOException { //设每个读取到的字符整数值为ch. int ch = 0; //循环读取字符,直到流的末尾 while((ch = fr.read()) != -1){ //将读取到的字符,强制转换为 char System.out.print((char) ch); } } }
上述两种读取方式,最明前的区别在于:
第二种读取字符的方式,要比第一种方式高效。到目前为止,我们已经学了FileReader, FileWrite的具体使用,以及IO异常的处理方式,下面将通过一个具体的示例,来综合的应用上述所学的知识:
题目:
* 练习:将c盘的一个文本文件复制到d盘。
* 思路:
* 1,c盘的文件是一个数据的源。
* 复制到的d盘,说明d盘是数据的存储的目的。该目的中应该有一个文件。
* 2,先读取c盘的文件。将读取到的数据写入到目的地中。
1.用缓冲区数组,来完成读写操作;
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyTextTest1 { public static void main(String[] args) { /* * * 使用缓冲区数组。 * 使用的就是可以操作数组的读写方法。 */ //1,定义字符输入流和字符输出流的引用。 FileReader fr = null; FileWriter fw = null; try { //2,对流对象进行初始化。 fr = new FileReader("demo.txt"); fw = new FileWriter("copy_demo2.txt"); //3,定义一个数组缓冲区。用于缓冲读取到的数据。 char[] buf = new char[1024]; //4,读写操作。 int len = 0; while((len = fr.read(buf))!=-1){ fw.write(buf,0,len); } } catch (Exception e) { System.out.println(e.toString()); }finally{ if(fw!=null) try { fw.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } if(fr!=null) try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
2. 用读取单个字符的方式来完成。
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyTextTest { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //1,创建字符读取流对象和源相关联。 FileReader fr = new FileReader("IO流.txt"); //2,创建字符输出流对象,明确要存储数据的目的。 FileWriter fw = new FileWriter("copy_demo.txt"); //3,进行读写操作。读取一个,就写入一个。 int ch = 0; while((ch=fr.read())!=-1){ fw.write(ch); } //4,关闭资源。 fw.close(); fr.close(); } }
缓冲区高效的原因:
流对象的read():是从目的地一次读取一个;
缓冲区的read() :是通过流对象的read( [ ] ) 将一批数据读取到缓冲数组,然后再从数组中一次取一个,所以内存操作要比硬盘操作要高效。
就在刚才,我们用的高效读取方式,是将字符全部读取到数组中,来达到高效的目的,在Java中,为了提高程序的性能,为我们专门提供了对应的类:
BufferedReader 和BufferedWriter . 接下来,我们将开始学习,这两个对象。
——————————————————————————————————
【BufferedReader 】
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。(他的构造方法有两个,一个构造方法使用的是默认大小的缓冲区,另一个构造方法,可以设定自己的缓冲区大小)。
他的牛逼之处在于,提供了一个一次读取一行的方法:
readline() ——读取一个文本行。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。返回值:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null。
下面来具体应用:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BuffereReaderDemo { public static void main(String[] args) throws IOException { /** * 演示:BufferedReader * 1. 先有字符读取流; * 2. 该类有一个特有方法。readLine(). 一次读一行。 */ //创建读取流对象 和 文件相关联。 FileReader fr = new FileReader("Demo.txt"); //创建读取缓冲区对象 和 流对象关联对其进行高效 操作; BufferedReader bufr = new BufferedReader(fr); //简写形式: //BufferedReader bufr = new BufferedReader(new FileReader("Demo.txt")); String line = null ; while((line = bufr.readLine()) != null){ System.out.println(line); } bufr.close(); } }
在上述示例中我们应当注意的是:
1. BufferedReader的简写形式。
BufferedReader bufr = newBufferedReader(new FileReader("Demo.txt"));
2. 【readLine().一次读一行的原理】
从缓冲区中取出字符存储到该方法的容器中。当取出的字符是回车符时,就将已经存储的数据作为字符串返回。就是一行数据。
ReadLine() 调用buf.read()将缓冲区中的数据存储到,自己建立的容器中。ReadLine()建立自己的容器—— StringBuilder 最合适。
下面我们来学习,BufferedWriter,作为与BufferedReader配套出现的亲兄弟,他的作用,当然显而易见。
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建一个流对象。 FileWriter fw = new FileWriter("buf.txt"); //为了提高效率。创建缓冲区对象,并和要被提高效率的流相关联。 BufferedWriter bufw = new BufferedWriter(fw); for(int x=0;x<4; x++){ bufw.write(x+"--hahaha"); //写入一个换行符。 bufw.newLine(); //要对缓冲区进行刷新。记住:一般只要使用了缓冲区,就一定要刷新。 bufw.flush(); } //关闭缓冲区。 bufw.close();//问:还用关闭fw.close()?不用,因为关闭缓冲区,其实就是在关闭缓冲区关联的流。 } }
在上述示例中,我们应当注意:
1. 写入一个新行的方法:newline();
2. 每写一次,就要讲缓冲区刷新一次。
3. 在使用完后,关闭缓冲区即可,不需要在关闭流。因为流已经与缓冲区相关联了。
至此:我们已经学习了IO体系中的四个对象:
FileReader FileWriter BufferedReader BufferedWriter