黑马程序员——Java基础—IO流(一)
2015-04-11 18:49
417 查看
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
一概述
二IO流的区分
三IO流常用基类
一Writer类
二Reader类
三字符流练习
四字符流缓冲区
五装饰设计模式
IO流概述
按流向分为:输入流和输出流。
2.按操作数据分
流按操作数据分为两种:字节流与字符流。
小扩展:
字符流的出现是为了方便处理文本字符。英语有英文字符集ASCII码;中文字符集由原来的GB2312,扩展为现在的GBK。之后,国际标准化组织计划将世界上所有国家的文字都进行编排,形成了国际标准码表,UNICODE;优化之后,形成了UTF-8字符编码表。那么,如果一个电脑用GBK编码存储文本,另一个电脑用UTF-8编码读取,就会出现乱码。Java就这个情况,在字节流的基础上,增加了字符流。字符流内部融合了编码表,并可以指定查询的编码表,处理文本字符更加方便,避免乱码。
Reader
Writer
2.字节流的抽象基类
InputStream
OutputStream
IO流用于操作数据,数据的最常见体现形式是文件。那么以操作文件为主,从字符流开始,来演示IO流程序。
IO流(一)
字符流
2.
1)文件创建并写入
第一步:创建
注意:
-创建文件会报出异常,此处先抛出,后期会有专门处理异常的代码;
-如果指定文件夹下有同名文件,该文件将被覆盖,所以要小心创建操作
第二步:写入自定义数据
注意:
-writer()方法不是将文本内容直接写入文件,而是先写入流的缓冲区;
-flush()方法将缓冲区中的内容写入到文件;每次调用完writer()方法,都要调用flush()
第三步:关闭流资源
注意:
-由于每个系统创建文件写入内容的方式不同,Java需要调用系统底层的功能来实现文件的创建和写入;使用完系统资源的之后,一定要调用close()方法,关闭该资源;
-close()方法与flush()方法的区别在于,close()方法刷新缓冲区之后,关闭了该输出流资源,之后不能再写入数据;而flush()之后,可以继续写入数据,流资源依然存在;
第四步:针对处理IO异常
注意:
-用try catch捕捉并处理异常;
-关闭资源的代码一定要放在finally中;
-close()方法同样会抛出IO异常,同样需要用try catch捕捉并处理;
-如果创建文件的路径有误(例如电脑上没有k盘,创建时却写fw = new FileWriter(“k:\”demo.txt”);),那么会报出FileNotFoundException文件无法找到异常,同时会报出NullPointerException空指针异常,因为路径找不到,fw对象没有建立,而finally中还要调用close()方法,会发生空指针异常;所以需要在finally中建立判断;
-无论读、写数据还是关闭资源操作,都有可能发生IO异常
2)文件的续写
在原有数据的基础上,续写自定义数据。
第一步:修改构造方法
第二步:写入自定义数据
2.FileReader类
1)read()方法读取文件
第一步:创建FileReader对象
注意:
创建FileReader对象时关联的读取文件必须已经存在,不然会发生FileNotFoundException
第二步:读入单个字符并关闭资源
注意:
read()方法读到文件末尾没有字符的时候,会返回-1;可以利用这个机制,循环读取所有数据
第三步:一次性读取全部数据
2)read(ch[] ch)方法读取文件
第一步:定义一个字符数组用于储存读到的字符
第二步:读取字符并存入字符数组
注意:
read(ch[] ch)方法返回的是读取了多少个字符;独到文件末尾,返回-1;可以利用这一机制,循环读取全部字符
第三步:一次性读取全部数据
注意:
-缓冲区字符数组通常定义1024的整数倍长度;
-read(ch[] ch)方法的效率优于read()方法;由于read()方法读一个字符,写一个字符,效率低下;而raead(ch[] ch)方法读取一段字符到缓冲区,然后一起写入,效率较高
读取一个
示例代码:
注意:
打印在控制台上不要用println方法,否则会出现不必要的换行
2.复制文件
将指定文件从一个目录复制到另一个目录。
原理:
将一个文件的内容写入到另一个目录下的另一个文件中
示例代码:
缓冲区的出现,提高了字符流对数据的读写效率。缓冲区可以实现先读出一部分数据,再一起写入,避免了硬盘在读与写之间频繁操作,提高读写速度。缓冲区必须结合流才能使用,在流的基础上对流的功能进行了增强。开发时通常都有缓冲区。
2.缓冲区对应类
缓冲区对应两个类,分别是
3.BufferedWriter类
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。在创建
1)文件创建并写入
第一步:创建字符流输出流对象
第二步:创建缓冲字符输出流对象
第三步:调用方法进行写入
注意:
只要用到缓冲区,就要调用flush()方法进行刷新
第四步:写入完毕,关闭资源
注意:
正真进行写入操作的是字符输出流FileWriter;所以关闭缓冲区其实就是关闭FileWriter对象
4.BufferedReader类
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。在创建
1)文件读取
第一步:创建字符输入流对象
第二步:创建缓冲字符输入流对象
第三步:调用方法读取数据
第四步:关闭缓冲区
5.缓冲区练习
1)通过缓冲区复制一个
示例代码:
注意:
使用readLine()方法读取时,不会读取每一行的回车符;所以在写入时,每写一行要调用newLine()方法换行
6.readLine()方法
1)原理
2)自定义readLine()方法
模拟一个
示例代码:
程序输出结果:
7.LineNumberReader类
1)输出带行号文本
示例代码:
程序输出部分结果:
装饰设计模式是当要对已有对象进行功能增强时,可以定义一个新类,将已有对象传入;基于已有对象的功能提供加强功能;那么自定义的这个类就叫做装饰类。如第四部分自定义
2.装饰和继承的区别
装饰类增强了原有类的功能,问题是为什么不直接继承原有类,并复写方法,在方法里增强该方法的功能?以一个例子说明装饰和继承的区别。
现在有一个继承体系,
MyReader
MyTextReader
MyMediaReader
MyDataReader
现发现三个子类的功能不够强大,要要加缓冲技术;按照继承的思想,那么就在三个子类下面再各自创建三个具备缓冲功能的子类,继承结构变为:
MyReader
MyTextReader
MyBufferedTextReader
MyMediaReader
MyBufferedMediaReader
MyDataReader
MyBufferedDataReader
这样做看似没有问题,但是缺点在于拥有缓冲功能的代码没有复用性,并导致继承体系结构庞大臃肿。因此,做好的做法不是创建子类,而是保持原有继承体系不变,将具备缓冲功能的代码提取出来,将需要增强的对象传递进来即可:
这样的做法已经简化了继承体系,同时增强了功能;但是后期
新的继承结构如下:
MyReader
MyTextReader
MyMediaReader
MyDataReader
MyBufferedReader
装饰设计模式比继承灵活,避免了继承体系的复杂臃肿。
3.装饰设计模式特点
装饰设计模式灵活性强,在扩展原有类功能的前提下,不失原有类的方法,即后期开发中,扩展功能和原有功能都可以视情况使用。
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
一概述
二IO流的区分
三IO流常用基类
一Writer类
二Reader类
三字符流练习
四字符流缓冲区
五装饰设计模式
IO流概述
一、概述
Java对数据的处理都是通过流的方式,称为IO(Input-Outpu)流。IO流用于处理设备上的数据传输,如硬盘上储存的数据,内存中驻留的数据。
二、IO流的区分
1.按流向分按流向分为:输入流和输出流。
2.按操作数据分
流按操作数据分为两种:字节流与字符流。
小扩展:
字符流的出现是为了方便处理文本字符。英语有英文字符集ASCII码;中文字符集由原来的GB2312,扩展为现在的GBK。之后,国际标准化组织计划将世界上所有国家的文字都进行编排,形成了国际标准码表,UNICODE;优化之后,形成了UTF-8字符编码表。那么,如果一个电脑用GBK编码存储文本,另一个电脑用UTF-8编码读取,就会出现乱码。Java就这个情况,在字节流的基础上,增加了字符流。字符流内部融合了编码表,并可以指定查询的编码表,处理文本字符更加方便,避免乱码。
三、IO流常用基类
1.字符流的抽象基类Reader
Writer
2.字节流的抽象基类
InputStream
OutputStream
IO流用于操作数据,数据的最常见体现形式是文件。那么以操作文件为主,从字符流开始,来演示IO流程序。
IO流(一)
字符流
一、Writer类
1.概述Writer类可以在硬盘上创建一个文件,并写入或添加数据。该类的子类还能实现写入过程中的不同功能。
2.
FileWriter类
FileWriter类专门用于操作文件。该对象中只有构造方法,并且没有空参数的构造方法,因为初始化的时候必须要有文件对象,才能进行写入操作。在示例代码中,将对每个环节做详细注释。
1)文件创建并写入
第一步:创建
FileWriter对象,传入要操作的文件名
class FileWriterDemo { public static void main(String[] args) throws IOException { /* 在当前目录创建一个名为demo.txt的文件作为文本写入的目标 */ FileWriter fw = new FileWriter("demo.txt"); } }
注意:
-创建文件会报出异常,此处先抛出,后期会有专门处理异常的代码;
-如果指定文件夹下有同名文件,该文件将被覆盖,所以要小心创建操作
第二步:写入自定义数据
class FileWriterDemo { public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("demo.txt"); /* 将字符串写入缓冲区 */ fw.write("abcde"); /* 将缓冲区中的内容写入文件 */ fw.flush(); } }
注意:
-writer()方法不是将文本内容直接写入文件,而是先写入流的缓冲区;
-flush()方法将缓冲区中的内容写入到文件;每次调用完writer()方法,都要调用flush()
第三步:关闭流资源
class FileWriterDemo { public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("demo.txt"); /* 将字符串写入缓冲区 */ fw.write("abcde"); /* 将缓冲区中的内容写入文件 */ fw.flush(); /* 关闭流资源,关闭之前会刷新缓冲区一次 */ fw.close(); } }
注意:
-由于每个系统创建文件写入内容的方式不同,Java需要调用系统底层的功能来实现文件的创建和写入;使用完系统资源的之后,一定要调用close()方法,关闭该资源;
-close()方法与flush()方法的区别在于,close()方法刷新缓冲区之后,关闭了该输出流资源,之后不能再写入数据;而flush()之后,可以继续写入数据,流资源依然存在;
第四步:针对处理IO异常
class FileWriterDemo { public static void main(String[] args) throws IOException { FileWriter fw = null; try { fw = new FileWriter("demo.txt"); fw.write("abcde"); fw.flush(); } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fw != null) fw.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
注意:
-用try catch捕捉并处理异常;
-关闭资源的代码一定要放在finally中;
-close()方法同样会抛出IO异常,同样需要用try catch捕捉并处理;
-如果创建文件的路径有误(例如电脑上没有k盘,创建时却写fw = new FileWriter(“k:\”demo.txt”);),那么会报出FileNotFoundException文件无法找到异常,同时会报出NullPointerException空指针异常,因为路径找不到,fw对象没有建立,而finally中还要调用close()方法,会发生空指针异常;所以需要在finally中建立判断;
-无论读、写数据还是关闭资源操作,都有可能发生IO异常
2)文件的续写
在原有数据的基础上,续写自定义数据。
第一步:修改构造方法
class FileWriterDemo { public static void main(String[] args) throws IOException { /* 传递一个布尔型参数,true表示不覆盖已有文件 * 从文件内容末尾开始添加新内容;如果文件不存在 * 则创建一个新文件 */ FileWriter fw = new FileWriter("demo.txt", true); } }
第二步:写入自定义数据
class FileWriterDemo { public static void main(String[] args) throws IOException { FileWriter fw = null; try { fw = new FileWriter("demo.txt", true); /* windows中判断换行是用\r\n来判断,\n只适用于linux系统 */ fw.write("nihao\r\nxiexie"); fw.flush(); } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fw != null) fw.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
二、Reader类
1.概述Reader类用于高效读取字符流。该类的子类可以实现读取过程中的不同功能。
2.FileReader类
FileReader类是用于读取字符文件的便捷类。该类包含默认的字符编码和默认的字节缓冲区大小。
1)read()方法读取文件
第一步:创建FileReader对象
class FileReaderDemo { public static void main(String[] args) { try { /* 创建一个文件读取流对象,并与指定名称的文件相关联 */ FileReader fr = new FileReader("demo.txt"); } catch(IOException e) { System.out.println(e.toString()); } } }
注意:
创建FileReader对象时关联的读取文件必须已经存在,不然会发生FileNotFoundException
第二步:读入单个字符并关闭资源
class FileReaderDemo { public static void main(String[] args) { FileReader fr = null; try { fr = new FileReader("demo.txt"); /* read()方法一次读一个字符,第二次调用会自动读取下一个字符 */ int ch1 = fr.read(); System.out.print("ch1 = " + (char)ch1); } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fr != null) fr.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
注意:
read()方法读到文件末尾没有字符的时候,会返回-1;可以利用这个机制,循环读取所有数据
第三步:一次性读取全部数据
class FileReaderDemo { public static void main(String[] args) { FileReader fr = null; try { fr = new FileReader("demo.txt"); int ch = 0; /* 当ch不等于-1的时候,继续读文件,直到文件末尾 */ while((ch = fr.read()) != -1) { System.out.print("ch = " + (char)ch); } } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fr != null) fr.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
2)read(ch[] ch)方法读取文件
第一步:定义一个字符数组用于储存读到的字符
class FileReaderDemo { public static void main(String[] args) { try { FileReader fr = new FileReader("demo.txt"); /* 创建长度为10的字符数组 */ char[] buff = new char[10]; } catch(IOException e) { System.out.println(e.toString()); } } }
第二步:读取字符并存入字符数组
class FileReaderDemo { public static void main(String[] args) { FileReader fr = null; try { fr = new FileReader("demo.txt"); char[] buff = new char[10]; /* 读取字符并存入字符数组 */ int num = fr.read(buff); System.out.print(new String(buff)); } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fr != null) fr.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
注意:
read(ch[] ch)方法返回的是读取了多少个字符;独到文件末尾,返回-1;可以利用这一机制,循环读取全部字符
第三步:一次性读取全部数据
class FileReaderDemo { public static void main(String[] args) { FileReader fr = null; try { fr = new FileReader("demo.txt"); char[] buff = new char[1024]; int num = 0; /* 当返回值不等于-1时,继续读文件 */ while((num = fr.read(buff)) != -1) { /* 读取了多少个字符,就打印多少个字符 */ System.out.print(new String(buff, 0, num)); } } catch(IOException e) { System.out.println(e.toString()); } finally { try { if(fr != null) fr.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
注意:
-缓冲区字符数组通常定义1024的整数倍长度;
-read(ch[] ch)方法的效率优于read()方法;由于read()方法读一个字符,写一个字符,效率低下;而raead(ch[] ch)方法读取一段字符到缓冲区,然后一起写入,效率较高
三、字符流练习
1.读取文件并打印读取一个
.java文件,打印在控制台上。
示例代码:
package com.heisejiuhuche.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderDemo { public static void main(String[] args) { // print_1(); print_2(); } /* 用read(ch[] ch)方法读取所有数据 */ private static void print_1() { /* 创建FileReader引用 */ FileReader fr = null; try { /* 创建FileReader对象,关联文件 */ fr = new FileReader( "C:\\RuntimeDemo.java"); /* 创建字符串缓冲区 */ char[] ch = new char[1024]; int len = 0; /* 当read方法返回值不等于-1,继续读文件 */ while ((len = fr.read(ch)) != -1) { System.out.print(new String(ch, 0, len)); } } catch (FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch (IOException e) { throw new RuntimeException("操作失败"); } finally { try { if (fr != null) { /* 关闭资源 */ fr.close(); } } catch (IOException e) { throw new RuntimeException("输入流关闭失败"); } } } /* 用read()方法读取所有数据 */ private static void print_2() { /* 创建FileWriter引用,关联文件 */ FileReader fr = null; try { /* 创建FileWriter对象,关联文件 */ fr = new FileReader( "C:\\RuntimeDemo.java"); int ch = 0; /* 当read方法返回值不等于-1,继续读文件 */ while ((ch = fr.read()) != -1) { System.out.print((char) ch); } } catch (FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch (IOException e) { throw new RuntimeException("操作失败"); } finally { try { if (fr != null) { fr.close(); } } catch (IOException e) { throw new RuntimeException("输入流关闭失败"); } } } }
注意:
打印在控制台上不要用println方法,否则会出现不必要的换行
2.复制文件
将指定文件从一个目录复制到另一个目录。
原理:
将一个文件的内容写入到另一个目录下的另一个文件中
示例代码:
package com.heisejiuhuche.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyFileTest { private static FileReader fr; private static FileWriter fw; public static void main(String[] args) { copy_1(); // copy_2(); } /* 用read(ch[] ch)方法复制文件 */ private static void copy_1() { try { /* 创建目的文件 */ fw = new FileWriter( "C:/Users/jeremy/Documents/javaTmp/Runtime.java"); /* 关联需要复制的文件 */ fr = new FileReader( "C:\\RuntimeDemo.java"); /* 创建字符数组缓冲区 */ char[] buff = new char[1024]; /* 循环控制 */ int len = 0; /* 将需要复制的文件写入缓冲区,并将缓冲区文件写入目的文件 */ while ((len = fr.read(buff)) != -1) { fw.write(buff, 0, len); } fw.flush(); } catch (FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch (IOException e) { throw new RuntimeException("操作失败"); } finally { try { if (fw != null) { fw.close(); } if (fr != null) { fr.close(); } } catch (IOException e) { throw new RuntimeException("流资源关闭失败"); } } } /* 用read()方法复制文件 */ private static void copy_2() { try { /* 步骤与上述方法相近 */ fw = new FileWriter( "C:/Users/jeremy/Documents/javaTmp/Runtime.java"); fr = new FileReader( "C:\\RuntimeDemo.java"); int ch = 0; while((ch = fr.read()) != -1) { fw.write(ch); } fw.flush(); } catch(FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch(IOException e) { throw new RuntimeException("操作失败"); } finally { try { if(fw != null) { fw.close(); } if(fr != null) { fr.close(); } } catch(IOException e) { throw new RuntimeException("流资源关闭失败"); } } } }
四、字符流缓冲区
1.概述缓冲区的出现,提高了字符流对数据的读写效率。缓冲区可以实现先读出一部分数据,再一起写入,避免了硬盘在读与写之间频繁操作,提高读写速度。缓冲区必须结合流才能使用,在流的基础上对流的功能进行了增强。开发时通常都有缓冲区。
2.缓冲区对应类
缓冲区对应两个类,分别是
BufferedWriter和
BufferedReader。它们提供的方法,可以实现对文本字符数据的高效读写。
3.BufferedWriter类
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。在创建
BufferedWriter类时,必须先有流对象。
1)文件创建并写入
第一步:创建字符流输出流对象
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输出流对象 */ FileWriter fw = new FileWriter("Demo.txt"); } }
第二步:创建缓冲字符输出流对象
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输出流对象 */ FileWriter fw = new FileWriter("Demo.txt"); /* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */ BufferedWriter buffWriter = new BufferedWriter(fw); } }
第三步:调用方法进行写入
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输出流对象 */ FileWriter fw = new FileWriter("Demo.txt"); /* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */ BufferedWriter buffWriter = new BufferedWriter(fw); for(int x = 1; x < 5; x++) { buffWriter.write("abcde" + x); /* 缓冲区提供的新方法,能跨平台换行 */ buffWriter.newLine(); buffWriter.flush(); } } }
注意:
只要用到缓冲区,就要调用flush()方法进行刷新
第四步:写入完毕,关闭资源
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输出流对象 */ FileWriter fw = new FileWriter("Demo.txt"); /* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */ BufferedWriter buffWriter = new BufferedWriter(fw); for(int x = 1; x < 5; x++) { buffWriter.write("abcde" + x); /* 缓冲区提供的新方法,能跨平台换行 */ buffWriter.newLine(); buffWriter.flush(); } /* 关闭缓冲区 */ buffWriter.close(); } }
注意:
正真进行写入操作的是字符输出流FileWriter;所以关闭缓冲区其实就是关闭FileWriter对象
4.BufferedReader类
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。在创建
BufferedReader类时,必须先有流对象。该缓冲区提供了
readLine()方法,使获取文本数据更高效。
1)文件读取
第一步:创建字符输入流对象
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输入流对象并关联目标文件 */ FileReader fr = new FileReader("Demo.txt"); } }
第二步:创建缓冲字符输入流对象
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输入流对象并关联目标文件 */ FileReader fr = new FileReader("Demo.txt"); /* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */ BufferedReader buffReader = new BufferedReader(fr); } }
第三步:调用方法读取数据
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输入流对象并关联目标文件 */ FileReader fr = new FileReader("Demo.txt"); /* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */ BufferedReader buffReader = new BufferedReader(fr); /* 调用readLine()方法读取数据,如果独到最后一行,该方法返回null */ String line = null; while((line = buffReader.readLine()) != null) { System.out.println(line); } } }
第四步:关闭缓冲区
class BufferedReaderDemo { public static void main(String[] args) throws IOException { /* 创建一个字符输入流对象并关联目标文件 */ FileReader fr = new FileReader("Demo.txt"); /* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */ BufferedReader buffReader = new BufferedReader(fr); /* 调用readLine()方法读取数据,如果独到最后一行,该方法返回null */ String line = null; while((line = buffReader.readLine()) != null) { System.out.println(line); } /* 关闭缓冲区 */ buffReader.close(); } }
5.缓冲区练习
1)通过缓冲区复制一个
.java文件
示例代码:
package com.heisejiuhuche.io; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyByBuffTest { private static BufferedWriter buffw; private static BufferedReader buffr; public static void main(String[] args) { copy(); } private static void copy() { try { /* 分别创建缓冲区对象,并关联文件 */ buffw = new BufferedWriter(new FileWriter( "C:/Users/jeremy/Documents/javaTmp/SystemDemo.java")); buffr = new BufferedReader(new FileReader( "C:\\Users\\jeremy\\java\\workspaces\\test\\src\\com\\heisejiuhuche\\api\\SystemDemo.java")); /* 循环控制,记录读取的每一行 */ String line = null; /* 读取数据,直到最后一行 */ while((line = buffr.readLine()) != null) { buffw.write(line); buffw.newLine(); buffw.flush(); } } catch(FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch(IOException e) { throw new RuntimeException("操作失败"); } finally { /* 关闭缓冲区 */ try { if(buffw != null) { buffw.close(); } if(buffr != null) { buffr.close(); } } catch(IOException e) { throw new RuntimeException("流资源关闭失败"); } } } }
注意:
使用readLine()方法读取时,不会读取每一行的回车符;所以在写入时,每写一行要调用newLine()方法换行
6.readLine()方法
1)原理
readLine()方法的实现是基于
read()方法的。如图,
readLine()方法在内存中创建了一个字符数组作为缓冲,每次读取操作都调用
read()方法,读取一个字符。步骤
1,
2,
3,
4,
5读取了五次,并分别将这五个字符存入字符数组。当读到换行附的时候,
readLine()方法不会将换行字符存入数组,而将字符数组中的字符组成字符串返回,再由输出流调用写入方法,在第
6步写入目标文件。
2)自定义readLine()方法
模拟一个
BufferedReader类,要求有
readLine()功能的方法和
close()功能的方法。
示例代码:
package com.heisejiuhuche.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class MyBufferedReaderTest { /* 在main方法中读取一个文件,并在控制台打印,测试MyBufferedReader类 */ public static void main(String[] args) { MyBufferedReader buffr = null; try { FileReader fr = new FileReader("Demo.txt"); buffr = new MyBufferedReader(fr); String line = null; while ((line = buffr.readLine()) != null) { System.out.println(line); } } catch (FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch (IOException e) { throw new RuntimeException("操作失败"); } finally { try { if(buffr != null) buffr.close(); } catch(IOException e) { throw new RuntimeException("资源关闭失败"); } } } } class MyBufferedReader { /* 持有FileReader的引用,因为readLine()方法基于read()方法 */ private FileReader fr; /* 初始化时需要传入字符流对象 */ MyBufferedReader(FileReader fr) { this.fr = fr; } /* 创建readLine()方法,标识异常 */ public String readLine() throws IOException { /* 用StringBuilder代替数组,完成字符串拼接 */ StringBuilder sb = new StringBuilder(); int ch = 0; /* 调用read()方法,读取数据 */ while ((ch = fr.read()) != -1) { /* 如果读到'\r',跳过,继续读下一个字符 */ if (ch == '\r') continue; /*如果读到'\n',标识一行结束,返回这行的字符串 */ if (ch == '\n') return sb.toString(); else /* 如果是字符,就加入StringBuilder */ sb.append((char)ch); } /* 这行代码排除了行末没有'\n'而整行字符串不会被打印的情况 */ if (sb.length() != 0) { return sb.toString(); } /* 如果读到文件最后,返回null */ return null; } /* 创建关闭缓冲区方法,标识异常 */ public void close() throws IOException { fr.close(); } }
程序输出结果:
abcd1 abcd2 abcd3
7.LineNumberReader类
LineNumberReader类是可以跟踪行号的缓冲字符输入流。
LineNumberReader是
BufferedReader的子类,其使用也必须结合流。该类的用法和
BufferedReader基本一致。
1)输出带行号文本
示例代码:
package com.heisejiuhuche.io; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class LineNumberReaderDemo { public static void main(String[] args) { LineNumberReader lnr = null; try { lnr = new LineNumberReader(new FileReader("SystemDemo.java")); String line = null; while((line = lnr.readLine()) != null) { /* 调用getLineNumber()方法现实行号 */ System.out.println(lnr.getLineNumber() + ":" + line); } } catch(FileNotFoundException e) { throw new RuntimeException("文件不存在"); } catch(IOException e) { throw new RuntimeException("操作失败"); } finally { try { if(lnr != null) lnr.close(); } catch(IOException e) { throw new RuntimeException("资源关闭失败"); } } } }
程序输出部分结果:
6:public class SystemDemo { 7: public static void main(String[] args) { 8: Properties prop = System.getProperties();
五、装饰设计模式
1.定义装饰设计模式是当要对已有对象进行功能增强时,可以定义一个新类,将已有对象传入;基于已有对象的功能提供加强功能;那么自定义的这个类就叫做装饰类。如第四部分自定义
readLine()方法练习中,自定义的
MyBufferedReader就是一个装饰类。
2.装饰和继承的区别
装饰类增强了原有类的功能,问题是为什么不直接继承原有类,并复写方法,在方法里增强该方法的功能?以一个例子说明装饰和继承的区别。
现在有一个继承体系,
MyReader类是基类,用于读取数据;该基类有数个子类,
MyTextReader,
MyMediaReader,
MyDataReader,分别用于读取文本、多媒体和数据库数据,已有继承体系结构如下:
MyReader
MyTextReader
MyMediaReader
MyDataReader
现发现三个子类的功能不够强大,要要加缓冲技术;按照继承的思想,那么就在三个子类下面再各自创建三个具备缓冲功能的子类,继承结构变为:
MyReader
MyTextReader
MyBufferedTextReader
MyMediaReader
MyBufferedMediaReader
MyDataReader
MyBufferedDataReader
这样做看似没有问题,但是缺点在于拥有缓冲功能的代码没有复用性,并导致继承体系结构庞大臃肿。因此,做好的做法不是创建子类,而是保持原有继承体系不变,将具备缓冲功能的代码提取出来,将需要增强的对象传递进来即可:
class MyBufferedReader { MyBufferedReader(MyTextReader text) {} MyBufferedReader(MyMediaReader media) {} MyBufferedReader(MyDataReader data) {} }
这样的做法已经简化了继承体系,同时增强了功能;但是后期
MyReader基类下继续添加新的子类时,这个
MyBufferedReader类还需修改;因此,应该使用多态的特性,在
MyBufferedReader中只需接收
MyReader类的对象即可;由于
MyBufferedReader增强的是
MyReader子类的功能,这些功能都是趋同的,因此最好让
MyBufferedReader作为
MyReader的子类,加入继承体系:
class MyBufferedReader extends MyReader{ MyBufferedReader(MyReader r) {} }
新的继承结构如下:
MyReader
MyTextReader
MyMediaReader
MyDataReader
MyBufferedReader
装饰设计模式比继承灵活,避免了继承体系的复杂臃肿。
3.装饰设计模式特点
装饰设计模式灵活性强,在扩展原有类功能的前提下,不失原有类的方法,即后期开发中,扩展功能和原有功能都可以视情况使用。
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
相关文章推荐
- 黑马程序员---Java基础--19天(IO流之二)
- 黑马程序员-Java语言基础 – IO流 第20-21天
- 黑马程序员 java基础<四>--IO流(2)
- 黑马程序员-JAVA基础-IO流之File 类
- 黑马程序员-JAVA基础-IO流之字符流和字符流缓冲区
- 黑马程序员---Java基础--20天(IO流之三)
- 黑马程序员_java基础视频第18天_与系统交互的类及IO流
- 黑马程序员 java基础<三>--IO流(1)
- 黑马程序员-JAVA基础-IO流之字节流和字节流缓冲区
- 黑马程序员--JAVA基础教程第18天(下)IO流
- 黑马程序员-Java语言基础– IO流 第18天
- 黑马程序员-java基础8--IO流(一)
- 黑马程序员_Java基础_其它对象和IO流(字符流)
- 黑马程序员 java基础<五>--IO流(3)
- 黑马程序员—10、Java基础&IO流
- 黑马程序员-Java语言基础– IO流 第19天
- 黑马程序员_Java基础_IO流(File类和properties类相关操作)
- 黑马程序员_Java基础_IO流(对象序列化和字符编码)
- 黑马程序员-JAVA基础-IO流之流操作规律及读写转换流
- 黑马程序员 java基础<六>--IO流(4)