Java 的IO 流 是实现输入和输出的基础,Java 中把不同的输入、输出源抽象表述为“流” (Streem)。按照不同的分类方式,可以将流分为不同的类型:输入流和输出流、字节流和字符流、字点流和处理流。
一.字符流
1.1 字符流基类:
Writer 和 Reader 抽象类,通过这两个抽象类提供的方法可以对文件进行操作。
Writer 中包含的方法:
> void write(String str) : 将str 字符串中包含的字符输出到指定输出流。
> void write(String str, int off, int len) : 写入字符串的某一部分。
> void write(char[] cbuf) : 写入字符数组。
> abstract void write(char[] cbuf, int off, int len) : 写入字符数组的某一部分 。
> abstract void flush() 刷新该流的缓冲。
> abstract void close() :关闭此流,但要先刷新它。
练习:在硬盘上,创建一个文件并写入一些文字数据:
1 import java.io.*; 2 public class FileWriterDemo { 3 public static void main(String[] args) throws IOException{ 4 // 创建一个FileWriter 对象。该对象一被初始化就必须要明确被操作的文件。 5 // 而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖。 6 // 其实该步就是明确数据的存放的目的地 7 FileWriter fw = new FileWriter("D:\\HelloWorld.txt"); 8 9 // 调用父类 Writer 的 wirter 方法 10 // 将字符串写到内存中。 11 fw.write("HelloWrold!") ; 12 13 // 刷新流对象中的缓冲中的数据。 14 // 将数据存到目的文件。 15 fw.flush() ; 16 // 关闭流资源,但是关闭之前会刷新一次内部缓冲中的数据。 17 // 将数据刷到目的文件中。 18 fw.close(); 19 } 20 }
Reader 中包含的方法:
> int read() 读取单个字符。
> int read(char[] cbuf) 将字符读入数组。
> abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
1 public class ReadDemo1 { 2 public static void main(String[] args) throws IOException { 3 // 创建一个文件读取流对象,和指定的文件相关联。 4 // 要保证文件存在,否则会出现 FileNotFoundException ; 5 FileReader fr = new FileReader("D:\\HelloWorld.txt") ; 6 7 // 调用读取流对象read方法。 8 // read():一次只读一个字符,而且会自动往下读。 9 int n1 = fr.read() ; 10 System.out.println((char) n1); 11 12 int n2 = fr.read() ; 13 System.out.println((char) n2); 14 15 // 定义一个字符数组。用于存储字符。 16 // read(char[] ch) 返回的是读取的字符个数。 17 char[] ch = new char[3] ; 18 int num = fr.read(ch) ; 19 System.out.println(num+"...."+ new String(ch)); 20 } 21 }
上面代码演示了FileReader 的两种读取方法。
> read():一次只读一个字符,而且会自动往下读。
通过学习,在处理IO流是都要抛出IOException 异常。为了使得代码更安全,在处理IO异常要有必要的手段。
1 import java.io.*; 2 public class IOExceptionTest { 3 public static void main(String[] args){ 4 5 // 在外面建立引用,在try 中进行初始化:不然引用在finally不存在。 6 FileWriter fw = null ; 7 try{ 8 fw = new FileWriter("HelloWorld") ; 9 fw.write("hello world") ; 10 fw.flush() ; 11 } 12 catch(IOException e) { 13 e.printStackTrace(); 14 } 15 finally { 16 try{ 17 // 如果有多个引用,则要分开try。 18 if(fw != null) 19 fw.close() ; 20 } 21 catch(IOException e){ 22 e.printStackTrace() ; 23 } 24 } 25 } 26 }
上面的代码是IO处理异常方式。
练习:读取一个.Java 文件,并在控制台上打印。
1 import java.io.*; 2 public class ReadDemo2 { 3 public static void main(String[] args) { 4 // 创建读取流对象: 5 FileReader fr = null ; 6 try { 7 fr = new FileReader("D:\\workspace\\heima\\src\\io\\ReadDemo1.java") ; 8 // 创建一个存储字符 9 char[] ch = new char[1024] ; 10 int num ; 11 while((num = fr.read(ch)) != -1) 12 { 13 System.out.println(new String(ch,0,num)); 14 } 15 }catch(IOException e) { 16 e.printStackTrace() ; 17 }finally{ 18 try{ 19 if (fr != null ) 20 fr.close() ; 21 }catch(IOException e) { 22 e.printStackTrace() ; 23 } 24 } 25 } 26 }
创建存储字符数组时,大小最好是1024的整数倍(为什么?)
练习: 将一个文件复制到另一个地方。
复制的原理:
其实就是将文件数据存储到另一个文件中。
步骤:
1、在目标地点创建一个文件,用于存储被复制的数据。
2、定义读取流和被复制文件关联。
3、通过不断的读写完成数据存储。
4、关闭资源。
1 public class ReadDemo3 { 2 public static void main(String[] args) { 3 CopyMethod("D:\\ReadDemo2.txt" , "D:\\workspace\\heima\\src\\io" + 4 "\\IOExceptionTest.java") ; 5 } 6 7 public static void CopyMethod(String w , String r) { 8 // 创建Writer 流对象和Reader 流对象。 9 FileReader fr = null ; 10 FileWriter fw = null ; 11 try { 12 // 在D盘下创建一个文件,用于存储被复制的数据。 13 fw = new FileWriter(w) ; 14 // 读取要被复制的文件。 15 fr = new FileReader(r) ; 16 17 // 创建字符数组:存储读取文件数据。 18 char[] ch = new char[1024] ; 19 int num = 0 ; 20 // 往目标文件写入数据。 21 while ((num = fr.read(ch))!=-1) { 22 fw.write(ch, 0, num) ; 23 fw.flush() ; 24 } 25 }catch(IOException e){ 26 System.out.println(e.toString()); 27 }finally { 28 // 分开try. 29 if (fw != null) 30 try { 31 fw.close() ; 32 }catch(IOException e) { 33 System.out.println(e.toString()); 34 } 35 if (fr != null) 36 try{ 37 fr.close() ; 38 }catch(IOException e) { 39 System.out.println(e.toString()); 40 } 41 } 42 } 43 }
1.2 字符流缓冲区:
从内部结构上来看,Buffer 就像一个数组,它可以保存多个类型相同的数据。例如:如果将IO流比作水管滴水,那么使用字符缓冲区,便像用杯子先将水滴存储起来。
特点:
1、缓冲区的出现提高了对数据读写的效率。
2、缓冲区要结合流才可以使用。
3、在流的基础上对流的功能的进行增强。
字符流缓冲区对应类:BufferedReader 和 BufferedWriter 。
1 public class BufferedWriterDemo { 2 public static void main(String[] args){ 3 // 创建一个字符写入流引用。 4 FileWriter fw = null ; 5 // 为了提高字符写入流效率,加入缓冲技术; 6 BufferedWriter bufw = null ; 7 try{ 8 fw = new FileWriter("D:\\HelloWorld1.txt") ; 9 // 只要将需要被提高效率的流对象最为参数传递给缓冲区的构造函数即可。 10 bufw = new BufferedWriter(fw) ; 11 bufw.write("afwefw ") ; 12 bufw.flush() ; 13 }catch(IOException e){ 14 e.printStackTrace(); 15 }finally{ 16 if(bufw != null) 17 try{ 18 // 只需要关闭缓冲区 便可。 19 bufw.close() ; 20 }catch(IOException e){} 21 } 22 } 23 }
缓冲区的基本操作如上。
对于字符流读取缓冲区,该缓冲区提供了一个一次读一行的方法readLine , 方便于对文本数据的获取,当返回null 时,表示读到文件末尾。
注意:readLine 方法返回的是回车符之间的字符,不包括回车符。
> String readLine() : 读取一个文行。
1 import java.io.*; 2 public class BufferedReaderDemo { 3 public static void main(String[] args){ 4 // 创建一个读取流对象和文件相关联 5 FileReader fr = null ; 6 // 加入缓冲技术: 7 BufferedReader bufr = null ; 8 try { 9 fr = new FileReader("D:\\ReadDemo2.txt") ; 10 bufr = new BufferedReader(fr) ; 11 12 String line = null ; 13 // 一次读取一行 14 while ((line = bufr.readLine())!= null){ 15 System.out.println(line); 16 } 17 }catch(IOException e){ 18 e.printStackTrace() ; 19 }finally{ 20 if(bufr != null) 21 try{ 22 bufr.close() ; 23 }catch(IOException e){ 24 e.printStackTrace() ; 25 } 26 } 27 } 28 }
练习:使用字符流缓冲区技术,复制一个文本。
1 public class BufferedReaderDemo2 { 2 public static void main(String[] args){ 3 BufferedReader bufr = null ; 4 BufferedWriter bufw = null ; 5 try { 6 // 使用缓冲区 7 bufr = new BufferedReader(new FileReader("D:\\ReadDemo2.txt")) ; 8 bufw = new BufferedWriter(new FileWriter("D:\\BufferedReader.txt")) ; 9 10 // 读写文件。 11 String line = null ; 12 while ((line = bufr.readLine()) != null){ 13 bufw.write(line) ; 14 // 由于不包括换行符,所以写入数据时要另开一行 15 bufw.newLine() ; 16 bufw.flush() ; 17 } 18 }catch(IOException e){ 19 e.printStackTrace() ; 20 }finally{ 21 if(bufr != null) 22 try{ 23 bufr.close() ; 24 }catch(IOException e) { 25 e.printStackTrace() ; 26 } 27 if(bufw != null) 28 try{ 29 bufw.close() ; 30 }catch(IOException e) { 31 e.printStackTrace() ; 32 } 33 } 34 } 35 }