Java IO(一)
字节流的两个基类:
- InputStream
- OutputStream
字符流的两个基类:
- Reader
- Writer
Writer
先学习一下字符流的特点。
既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。那么先以操作文件为主来演示。
例,需求:在硬盘上,创建一个文件并写入一些文字数据。
分析:
找到一个专门用于操作文件的Writer子类对象——FileWriter,后缀名是父类名,前缀名是该流对象的功能。
步骤:
- 创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。其实该步骤就是在明确数据要存放的目的地。
- 调用write(),将字符串写入到流中。
- 刷新流对象中的缓冲区中的数据,将数据刷到目的地中。
- 关闭流资源,但是关闭之前会刷新一次内部的缓冲区中的数据。将数据刷到目的地中。和flush()区别:flush刷新后,流可以继续使用,close()刷新后,会将流关闭。
示例代码如下:
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo0 { public static void main(String[] args) throws IOException { //创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件 //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。 //其实该步骤就是在明确数据要存放的目的地。 FileWriter fw = new FileWriter("demo.txt"); //调用write(),将字符串写入到流中 fw.write("abcde"); //刷新流对象中的缓冲区中的数据,将数据刷到目的地中。 //fw.flush(); /* * 关闭流资源,但是关闭之前会刷新一次内部的缓冲区中的数据。 * 将数据刷到目的地中。 * 和flush()区别:flush刷新后,流可以继续使用,close()刷新后,会将流关闭。 */ fw.close(); fw.write("haha"); } }
IO异常的处理方式:
示例代码:
/* IO异常的处理方式。 最后无论如何都应关闭资源,所以应放在finally代码块中 */ import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo1 { public static void main(String[] args) { FileWriter fw = null; try { fw = new FileWriter("demo.txt"); fw.write("abcdefg"); } catch (IOException e) { System.out.println("catch:" + e.toString()); } finally { try { if(fw != null) fw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
对已有文件的数据续写:
示例代码:
/* 演示对已有文件的数据续写。 */ import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo2 { public static void main(String[] args) throws IOException { /* * 传递一个true参数,代表不覆盖已有的文件。 * 并在已有文件的末尾处进行数据续写。 * \r\n在windows中表示行终止符 */ FileWriter fw = new FileWriter("demo.txt", true); fw.write("nihao\r\nxiexie"); fw.close(); } }
Reader
例,需求:从硬盘的一个文件中读取内容
代码:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo0 { public static void main(String[] args) throws IOException { /* * 创建一个文件读取流对象,和指定名称的文件相关联。 * 要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException。 */ FileReader fr = new FileReader("demo.txt"); /* * 调用读取流对象的read() * read():一次读取一个字符,会自动往下读。 */ int ch = 0; while((ch = fr.read()) != -1) { System.out.println((char)ch); } /* while(true) { int ch = fr.read(); if(ch == -1) break; System.out.println("ch = " + (char) ch); } */ fr.close(); } }
图示:
第二种方式:通过字符数组进行读取。
示例代码如下:
/* 第二种方式:通过字符数组进行读取。 */ import java.io.FileReader; import java.io.IOException; public class FileReaderDemo1 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); /* * 定义一个字符数组,用于存储读到的字符 * 该read(char[])返回的是读到的字符个数。 */ char[] buf = new char[1024];//字符数组大小为2KB int num = 0; while((num = fr.read(buf)) != -1) { System.out.println(new String(buf, 0, num)); } fr.close(); } }
图示:
练习:读取一个.java文件,并打印在控制台上。
代码如下:
import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("FileWriterDemo2.java"); char[] buf = new char[1024]; int num = 0; while((num = fr.read(buf)) != -1) { System.out.println(new String(buf, 0, num)); } fr.close(); } }
字符流缓冲区
缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。
BufferedWriter
该缓冲区中提供了一个跨平台的换行符:newLine()。
示例代码如下:
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 i = 0; i < 5; i++) { bufw.write("abcde" + i); bufw.newLine();//bufw.write("\r\n") bufw.flush();//写一次刷新一次。 } /* * 记住,只要用到缓冲区,就要刷新。 */ //bufw.flush(); /* * 其实关闭缓冲区,就是在关闭缓冲区中的流对象。 * 所以fw.close();就不用写了。 */ bufw.close(); //fw.close(); } }
BufferedReader
字符读取流缓冲区,该缓冲区提供了一个一次读一行的方法:readLine(),方便于对文本数据的获取。当返回null时,表示读取到文件末尾。
readLine()方法返回的时候只返回回车符之前的数据内容,并不返回回车符(行终止符)。
示例代码:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BufferedReaderDemo0 { public static void main(String[] args) throws IOException { /* * 创建一个读取流对象和文件相关联 */ FileReader fr = new FileReader("buf.txt"); /* * 为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲区对象的构造函数 */ BufferedReader bufr = new BufferedReader(fr); String line = null; while((line = bufr.readLine()) != null) { System.out.print(line); } bufr.close(); } }
需求:明白了BufferReader类中特有方法readLine()的原理后,可以自定义一个类中包含一个功能和readLine()一致的方法,来模拟一下BufferReader。
示例代码如下:
import java.io.FileReader; import java.io.IOException; import java.io.Reader; class MyBufferReader extends Reader { /* private FileReader fr = null; MyBufferReader(FileReader fr) { this.fr = fr; } */ private Reader fr = null; MyBufferReader(Reader fr) { this.fr = fr; } /* * 可以一次读一行数据的方法 */ public String myReadLine() throws IOException { /* * 定义一个临时容器,原BufferReader封装的是一个字符数组 * 为了演示方便,定义一个StringBuilder容器,因为最终还是要将数据变为字符串。 */ StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = fr.read()) != -1) { if(ch == '\r') continue; if(ch == '\n') return sb.toString(); else sb.append((char) ch); } if(sb.length() != 0) {//如果文本数据最后的行终止符故意去掉,那么StringBuilder里面还是有数据的 ,也要给予返回 return sb.toString(); } return null;//如果已到达流末尾,则返回 null } /* * 覆盖Reader类中的抽象方法。 */ @Override public void close() throws IOException { fr.close(); } @Override public int read(char[] cbuf, int off, int len) throws IOException { return fr.read(cbuf, off, len); } public void myClose() throws IOException { fr.close(); } } public class MyBufferReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); MyBufferReader myBuf = new MyBufferReader(fr); String line = null; while((line = myBuf.myReadLine()) != null) { System.out.println(line); } myBuf.myClose(); } }
LineNumberReader
一个带行号的缓冲区。
示例代码如下:
import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; public class LineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("PersonDemo.java"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; lnr.setLineNumber(100);//设置行号从100开始,实际打印是从101开始的 while((line = lnr.readLine()) != null) { System.out.println(lnr.getLineNumber() + " " + line); } lnr.close(); } }
练习:模拟一个带行号的缓冲区对象。
代码:
import java.io.FileReader; import java.io.IOException; import java.io.Reader; class MyLineNumberReader extends MyBufferReader { private int lineNumber;//行号 MyLineNumberReader(Reader r) { super(r); } public String myReadLine() throws IOException { lineNumber++;//myReadLine()方法读一次自增一次 return super.myReadLine(); } public int getLineNumber() { return lineNumber; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } } /* class MyLineNumberReader { private Reader r; private int lineNumber;//行号 MyLineNumberReader(Reader r) { this.r = r; } public String myReadLine() throws IOException { lineNumber++;//myReadLine()方法读一次自增一次 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = r.read()) != -1) { if(ch == '\r') continue; if(ch == '\n') return sb.toString(); else sb.append((char) ch); } if(sb.length() != 0) { return sb.toString(); } return null; } public int getLineNumber() { return lineNumber; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public void myClose() throws IOException { r.close(); } } */ public class MyLineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("bufWriter_Copy.txt"); MyLineNumberReader my = new MyLineNumberReader(fr); String line = null; //my.setLineNumber(100); while((line = my.myReadLine()) != null) { System.out.println(my.getLineNumber()+ " " +line); } my.myClose(); } }
由此引出装饰设计模式。
装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
举例说之:
class Person { public void chifan() { System.out.println("吃饭"); } } class SuperPerson { private Person p = null; SuperPerson(Person p) { this.p = p; } public void superChifan() { System.out.println("开胃酒"); p.chifan(); System.out.println("甜点"); System.out.println("来一根"); } } public class PersonDemo { public static void main(String[] args) { Person p = new Person(); //p.chifan(); SuperPerson sp = new SuperPerson(p); sp.superChifan(); } }
装饰模式与继承之间的区别:
通过以下分析:
MyReader//专门用于读取数据的类。 |---MyTextReader |---MyBufferTextReader |---MyMediaReader |---MyBufferMediaReader |---MyDataReader |---MyBufferDataReader //该类扩展性很差,找到其参数的共同类型,通过多态的形式,可以提高扩展性 class MyBufferReader { MyBufferReader(MyTextReader text) { } MyBufferReader(MyMediaReader media) { } } ------------------------------------------------------------------ class MyBufferReader extends MyReader { private MyReader r; MyBufferReader(MyReader r) { } } MyReader//专门用于读取数据的类。 |---MyTextReader(被装饰类) |---MyMediaReader(被装饰类) |---MyDataReader(被装饰类) |---MyBufferReader(装饰类)
可知:
装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常都是属于一个体系中的。
练习:将c盘一个文本文件复制到d盘中。
分析:
复制原理:其实就是将c盘下的文件数据存储到d盘的一个文件中。
步骤:
- 在d盘创建一个文件,用于存储c盘文件中的数据。
- 定义读取流和c盘文件关联。
- 通过不断的读写完成数据存储。
- 关闭资源。
代码:
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CoptTest { public static void main(String[] args) throws IOException { //copy_1(); copy_2(); } public static void copy_2() { FileWriter fw = null; FileReader fr = null; try { fw = new FileWriter("FileWriterDemo2_copy.java"); fr = new FileReader("FileWriterDemo2.java"); char[] buf = new char[1024]; int len = 0; if((len = fr.read(buf)) != -1) { fw.write(buf, 0, len); } } catch (IOException e) { throw new RuntimeException("读写失败"); } finally { if(fr != null) try { fr.close(); } catch (IOException e) { e.printStackTrace(); } if(fw != null) try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } /* * 从c盘读一个字符,就往d盘写一个字符 */ public static void copy_1() throws IOException { //创建目的地 FileWriter fw = new FileWriter("FileWriterDemo2_copy.txt"); //与已有文件关联 FileReader fr = new FileReader("FileWriterDemo2.java"); // int ch = 0; while((ch = fr.read()) != -1) { fw.write(ch); } fw.close(); fr.close(); } }
用缓冲区技术优化代码:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyTextByBuf { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java")); bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt")); String line = null; while((line = bufr.readLine()) != null) { bufw.write(line); bufw.newLine(); bufw.flush(); } } catch (IOException e) { throw new RuntimeException("读写失败"); } finally { try { if(bufr != null) bufr.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } try { if(bufw != null) bufw.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } } } }
图示: