基础知识之----------IO流(1)
1、IO流
用于将存储设备中的数据(硬盘、内存等)读入到固定设备中进行处理。
IO流分为字节流和字符流。
之前是没有字符流的,有ASICC编码表,里面固定的数字代表各个字母。但是每个国家的信息是不同的,就造成各个国家有各自的码表,不利于信息的互通。
后来产生了国际通用码表,可以识别很多国家的文字。Unicode码表。特点是:不论什么字符都用两个字节表示。
一个中文在GBK中数字和Unicode中可能并不一样。
Unicode对所有文字进行了重新编码。所以在GBK中编写了,使用Unicode码表查看可能就不是原来的文字了。
字符流:是指字节流读取文字字节数据后,不直接操作而是先查询指定的编码表然后获取对应的文字。再对这个文字进行操作。简单说就是:字节流+编码表。
字节流的两个顶层父类:1、InputStream 2、OutputStream
字符流的两个顶层父类:1、Writer 2、Reader
如果要操作文字数据,优先考虑字符流。
而且要将数据从内存写到硬盘上。要使用字符流中的输出流。
1.1测试流----写
/** * 创建一个可以往文件中写入字符数据的字符输出流对象 * 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地) * 如果文件不存在,则会自动创建 * 如果文件存在,则会被覆盖 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileWriter writer=new FileWriter("d://demo.txt"); /** * 调用Writer对象中的write(String)方法,写入数据。 * 数据在write方法执行后,其实已经写入到了临时存储缓冲区中,流里面。 * flush,刷新该流的缓冲。如果该流已保存缓冲区中各种write()方法的所有字符,则立即将它们写入预期目标。然后,如果该目标是另一个字符或字节流,则将其刷新。 * 因此,一次刷新调用将刷新writer和OutputStream链中的所有缓冲区。 * 刷新,将数据直接写入到目的地中。 */ writer.write("测试"); writer.write("123");
writer.flush(); writer.close(); }
关闭和刷新有什么区别呢?close是先刷新后关闭资源。flash是不关闭资源。
闭关就是无法再刷入文件了,而刷入可以再刷。
可以关联Windows的文本文件的打开、编辑、关闭。
关闭后再打开,将会是一个新的流。其实java流写入也是调用Windows的写入。
(2)字符流FileWriter的换行、续写。
在Windows中“\r”是换行。
那如果换个系统怎么办。
定义一个常亮,自动获取当前系统的换行符。
private static final String LINE_SEPARATOR =System.getProperty("line.separator") ;
writer.write("测试"+LINE_SEPARATOR);
(3)续写
在一个流写入完之后,怎么在上一个文件的基础上增加呢?而不是覆盖掉原来的内容。
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
在构造的时候添加true
FileWriter writer=new FileWriter("d://demo.txt",true);
就可以开启续写功能。
(4)IO异常处理
public static void main(String[] args) { FileWriter writer=null; try { writer = new FileWriter("ds://demo.txt", true); /** * 调用Writer对象中的write(String)方法,写入数据。 * 数据在write方法执行后,其实已经写入到了临时存储缓冲区中,流里面。 * flush,刷新该流的缓冲。如果该流已保存缓冲区中各种write()方法的所有字符,则立即将它们写入预期目标。然后,如果该目标是另一个字符或字节流,则将其刷新。 * 因此,一次刷新调用将刷新writer和OutputStream链中的所有缓冲区。 * 刷新,将数据直接写入到目的地中。 */ writer.write("测试" + LINE_SEPARATOR); writer.write("1231"); }catch (IOException e){ System.out.println(e.toString()); }finally { if (writer!=null) try { writer.close(); } catch (IOException e) { throw new RuntimeException("关闭失败"); } } }
需要在try/catch外先声明FileWriter,在try/catch内初始化。
close要独立捕获异常。
(5)FileReader字符流读取方式
找到FileReader
数据在存储的时候都有头和尾标识。
read读取单个字符。在字符可用、发送I/O错误或者已达到流的末尾前,此方法一直阻塞。
用于支持高效的单字符输入的子类应重写此方法。
返回:作为整数读取的字符,范围在0到6 5535之间,如果已到达流的末尾,则返回-1.
//创建一个读取字符流对象,在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。 //用一个读取流关联一个已存在文件。 FileReader fr = new FileReader("d:/demo.txt"); int ch = fr.read(); System.out.println( ch); int ch1 = fr.read(); System.out.println((char) ch1); fr.close();
以上方法只能一次读取一个字符。
//创建一个读取字符流对象,在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。 //用一个读取流关联一个已存在文件。 FileReader fr = new FileReader("d:/demo.txt"); int ch = 0; while ((ch=fr.read())!=-1){ System.out.print((char)ch);
遍历读取,但是依然是一个个的读取。
读取方式二:
num返回的是读取字符的个数。
第一个buf读取前三个,第二次读取读最后两个,但是依然是使用buf,此时只有前两个改变。最后一个字符是上次读取的。第三次并没有读取。
read取肯定是一个个取的。
2、缓冲区
提高效率。
缓冲区在创建对象时必须有被缓冲的对象,就好像(没有饭要碗有什么用)。缓冲区的对象为流。
方法摘要:newLine写入一个行分隔符,write(int c)写入单个字符。write(String s,int off,int len)写入字符串的一部分
BufferedWriter的方法close,其实是把流的close方法封装了。
public static void main(String[] args) throws IOException { FileWriter fw=new FileWriter("d://dem.txt"); BufferedWriter bw=new BufferedWriter(fw); bw.write("你哈啊 啊啊 啊"); bw.newLine(); bw.write("1"); bw.flush(); bw.close();
(2)BufferedReader
其中有一个readLine ,是按行读取。
public static void main(String[] args) throws IOException { FileReader fr=new FileReader("d://dem.txt"); BufferedReader br=new BufferedReader(fr); String line=null; while ((line=br.readLine())!=null){ System.out.println(line); } }
注意readLine最好先付给一个string变量,不然会显示不了第一行。
bufr.read()这个是从缓冲区中的取出的字符数据。所以覆盖了父类中的read方法。
例子:去厨房拿馒头,使用流是一个个拿。现在使用缓冲区,就是相当于拿了一个框去拿馒头,一次拿一筐。那么取馒头的时候当然要从框(缓冲区)里取出来。
已经不需要从硬盘读,而是读内存中的数据。所以已经高效了。buffuedreader的read方法覆盖了父类的read方法,从内存读。
readLine();使用了读取缓冲区中的read方法,将读取到的字符进行缓冲并判断换行标记。将标记前的缓存数据变成字符串返回。
这个容器也可以是StringBuilder,因为最终返回的是字符串。
流创建,缓冲区关联流。
while ((line=br.readLine())!=null){ bw.write(line); bw.newLine(); bw.flush(); }
3、缓冲区之装饰设计模式
缓冲区,将数据进行了缓存并对其数组进行了操作。提高了效率。
装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
package test; public class Person { public static void main(String[] args) { Per person=new Per(); // person.chifan(); NewPer p1=new NewPer(person); p1.chifan(); NewPer2 p2=new NewPer2(); p2.chifan(); } } class Per{ void chifan(){ System.out.println("吃饭"); } } class NewPer{ private Per pe; NewPer(Per pe){ this.pe=pe; } public void chifan(){ System.out.println("开胃酒"); pe.chifan(); System.out.println("甜点"); } } class NewPer2 extends Per{ public void chifan(){ System.out.println("开胃酒"); super.chifan(); System.out.println("甜甜"); } }
4、装饰设计模式和继承很相似,但是区别有哪些呢?
首先有一个继承体系。
想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
比如要新增一个小功能。那么可以使用继承,添加一个方法。
但是如果这个体系进行了功能扩展,又多了一个功能。
那么这个功能要提高效率,是不是也要产生子类呢?是,这个时候就会发现只为提高功能,进行的继承。导致继承体系越来越臃肿。不够灵活
重新思考:
既然加入的都是同一种技术---缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的 封装,那个对象需要缓冲就将那个对象和缓冲关联。
装饰比继承更为灵活,不需要产生关系。
特点:装饰类和被装饰类都必须所属于同一个接口或是父类。
5、
LineNumberReader是缓冲区的子类,具备了装饰功能。覆盖了父类和父类的一些功能。
6、以上说的基本都是字符流,字节流与字符流类相同,但它不近可以操作字符,还可以操作其他媒体文件。
字节流不需要编解码,不需要进行临时缓冲的。而是直接写入到目的地中。
public static void main(String[] args) throws IOException { //1、创建字节流输出流对象,用于操作文件 FileOutputStream fos=new FileOutputStream("demo.txt"); // 2、写数据 fos.write("测试信息".getBytes()); //关闭资源动作要完成 fos.close(); }
public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("d://dem.txt"); byte[] buf=new byte[1024]; int len=0; while ((len=fis.read(buf))!=-1){ System.out.println(new String(buf,0,len)); } }
fis.available();获取文件的 大小
示例:
public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("d://dem.txt"); byte[] buf=new byte[fis.available()]; fis.read(buf); System.out.println(new String(buf) );
一个刚刚好的数组
使用缓冲区的时候不要忘记flash();
为什么字符流不可以复制图片。读完后再写。字符流的特点在于,读完了字节数据后并没有往目的中写,而是查询了表。
那么如果字节数据在表里没有查到内容,文字有特定编码格式。而图片并没有,如果在码表内未查找到对的编码格式。则无法解析。会出错