输入流和输出流是相对于内存设备而言
即将外设中的数据读取到内存中就是输入
将内存中的数据写入到外设中就是输出
字符流的由来:
其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。
再对这个文字进行操作,其实就是字节流+编码表
字节流的两个顶层父类:
1,InputStream 2,OutputStream
字符流的两个顶层父类:
1,Reader 2,Writer
这些体系的子类都以父类名作为后缀。
而子类名的前缀就是该对象的功能。
如果要操作文字数据,建议优先考虑字符流。
而且要将数据从内存写到硬盘上。要使用字符流中的输出流,Writer
输出流代码演示:
import java.io.FileWriter; import java.io.IOException; public class IODemo { private static final String LINE_SPEARATOR = System.getProperty("line.separator"); /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /** * 创建一个可以往文件中写入字符数据的字符输出流对象 * 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该对象文件(用于存储数据的目的地) * 如果文件不存在,则创建。 * 如果文件存在,则覆盖 * * 如果构造函数中加入ture,可以实现文件的续写。 */ FileWriter wf = new FileWriter("E:\\Workspaces\\MyEclipse 10\\a.txt",true); /** * 调用Writer对象中的writer(String s)方法,写入数据。 * 其实数据写入到临时存储缓冲区中。 */ wf.write("my data"+LINE_SPEARATOR+"again line "); /** * 进行刷新,将数据直接写到目的地中。 */ wf.flush(); /** * 关闭流,关闭资源。在关闭前会调用flush刷新缓冲中的数据到目的地中。 * 如果在关闭流后再进行flush方法的调用,则会导致IOException * 如需要继续写入,则使用flush进行数据写入,如果不需要再写数据则直接调用close即可。 * */ wf.close(); } }
输入流代码演示一:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo1 { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 1,创建一个能够读取字符流数据的流对象 /** * 在创建读取刘对象时,必须要明确被读取的文件,一定要确定该文件是存在的。 * * 用一个读取流关联一个已存在的文件。 */ FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); /** * 用reader中的read方法读取 * 简单读取,一个字符一个字符的读。 */ int ch = 0; while((ch=fr.read())!=-1){ System.out.println((char)ch); } fr.close(); } }
输入流代码演示二:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo2{ /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); /** * 使用read(char[] ch)读取文本文件数据。 * 先创建字符数组 */ char[] buf = new char[1024]; int len = 0; while((len = fr.read(buf))!=-1){//将读到的字符存储到数组中。 System.out.println(new String(buf)); } fr.close(); } }
复制功能实现:
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyTextDemo { private static final int BUFFER_SIZE = 1024; /** * @param args */ public static void main(String[] args) { FileReader fr = null; FileWriter fw = null; try { fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); fw = new FileWriter("E:\\Workspaces\\MyEclipse 10\\b.txt"); //创建一个临时容器,用于缓存读取到的字符 char[] buf = new char[BUFFER_SIZE]; //定义一个变量记录读取到的字符数其实就是往数组里装的字符个数。 int len = 0; while((len = fr.read(buf))!= -1){ fw.write(buf,0,len); } } catch (Exception e) { throw new RuntimeException("失败了"); }finally{ if(fw!=null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } if(fr!=null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
缓冲区的写入使用代码演示:
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; public class BufferedWriterDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("E:\\Workspaces\\MyEclipse 10\\b.txt"); //为了提高写入的效率,使用了字符流的缓冲区。 //创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联 BufferedWriter bufw = new BufferedWriter(fw); //使用缓冲区的写入方法将数据先写入到缓冲区中 bufw.write("buffered write"); bufw.newLine(); bufw.write("again line"); //使用缓冲区的刷新方法将数据刷到目的地中。 bufw.flush(); //关闭缓冲区。其实关闭的就是被缓冲的流对象。 bufw.close(); } }
缓冲区读取代码演示:
FileReader fr = new FileReader("E:\\Workspaces\\MyEclipse 10\\a.txt"); BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null){ System.out.println(line); }
BufferedReader原理代码演示:
import java.io.FileReader; import java.io.IOException; public class MyBufferedReader { /** * @param args * 自定义的读取缓冲区,模拟一个BufferedReader. * 分析: * 缓冲区中无非就是封装了一个数组, * 并对外提供了更多的方法对数组进行访问, * 其实这些方法最终操作的都是数组的角标 * * 缓冲的原理: * 其实就是从源中获取一批数据装进缓冲区中 * 在从缓冲区中不断的取出一个一个数据。 * * 当此次取完后,再从源中继续取一批数据进缓冲区。 * 当源中的数据取光时,用-1作为结束标记。 * */ private FileReader r; private char[] buf = new char[1024];//定义一个数组作为缓冲区 //定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归零。 private int pos = 0; //定义一个计数器,用于记录缓冲区中的数据个数。当该数据减到0,就从源中继续获取数据到缓冲区中。 private int count = 0; MyBufferedReader(FileReader r){ this.r = r; } /** * 该方法从缓冲区中一次取一个字符 * @return * @throws IOException */ public int myRead() throws IOException{ //从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。 /*if(count==0){ count = r.read(buf); if(count<0){ return -1; } pos = 0;//每次获取数据到缓冲区后,角标归0 char ch = buf[pos]; pos++; count--; return ch; }else if(count>0){ char ch = buf[pos]; pos++; count--; return ch; } */ //方法简化重写 if(count==0){ count = r.read(buf); pos = 0; } if(count<0){ return -1; } char ch = buf[pos]; pos++; count--; return ch; } public String myReadLine() throws IOException{ StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = myRead())!=-1){ if(ch == ‘\r‘){ continue; } if(ch == ‘\n‘){ return sb.toString(); } //将从缓冲区中读到的字符存储到缓存行数据的缓冲区中。 sb.append((char)ch); } if(sb.length()!=0){ return sb.toString(); } return null; } }
IO缓冲区之装饰设计模式:
当对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
装饰和继承都能够实现一样的特点:进行功能的扩展和增强。
区别在于:
首先有一个集成体系。
假设体系如下:
Writer
--TextWriter:用于操作文本
--MediaWriter:用于操作媒体
想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
Writer
--TextWriter:用于操作文本
--BufferTextWriter:加入了缓冲技术的操作文本的对象。
--MediaWriter:用于操作媒体
到这里其实就已经实现了,但是并不理想。
如果这个体系进行功能扩展,那么就又多了流对象。
那么这个流要提高效率,就一定要产生子类,这时就会发现只为提高功能而进行的继承就会导致继承体系越来越臃肿。就不够灵活。
那么重新进行思考:
既然加入的都是同一种技术---缓冲。
前一种是让缓冲和具体的流对象相结合。
那么我们就对缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class Buffer{
Buffer(TextWriter w){}
Buffer(MediaWriter w){}
}
class BufferWriter extends Writer{
Buffer(Writer w){}
}
那么体系就变成如下结构
Writer
--TextWriter:用于操作文本
--MediaWriter:用于操作媒体
--BufferWriter:用于提高效率的。
结论如下:
装饰比继承更为灵活。
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
装饰设计模式思想重写MyBufferedReader
import java.io.IOException; import java.io.Reader; public class MyBufferedReader extends Reader { private Reader r; private char[] buf = new char[1024]; private int pos = 0; private int count = 0; MyBufferedReader(Reader r){ this.r = r; } public int myRead() throws IOException{ if(count==0){ count = r.read(buf); pos = 0; } if(count<0){ return -1; } char ch = buf[pos]; pos++; count--; return ch; } public String myReadLine() throws IOException{ StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = myRead())!=-1){ if(ch == ‘\r‘){ continue; } if(ch == ‘\n‘){ return sb.toString(); } sb.append((char)ch); } if(sb.length()!=0){ return sb.toString(); } return null; } @Override public int read(char[] cbuf, int off, int len) throws IOException { return 0; } @Override public void close() throws IOException { r.close(); } }