基础知识之----------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中“ ”是换行。

  那如果换个系统怎么办。

 定义一个常亮,自动获取当前系统的换行符。 

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("关闭失败");
            }
        }
    }
View Code

 需要在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("甜甜");
    }
}
View Code

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();

  为什么字符流不可以复制图片。读完后再写。字符流的特点在于,读完了字节数据后并没有往目的中写,而是查询了表。

  那么如果字节数据在表里没有查到内容,文字有特定编码格式。而图片并没有,如果在码表内未查找到对的编码格式。则无法解析。会出错   

坚持就是胜利
原文地址:https://www.cnblogs.com/xiaotieblog/p/8430836.html