IO流(一)

基本概念

流是一组有序的数据序列。I/O流流提供了一条通道程序,用来处理设备之间的数据传输。java对数据的操作是通过流的方式,java用于操作流的对象都在IO包中。

流按照流向分为:输入流,输出流。
程序从指向源的输入流中读取源中的数据,源可以是文件、网络、压缩包或其他数据源。输出流的指向是数据要到达的目的地,程序通过向输出流中写入数据把信息传递到目的地。输出的目标可以是文件、网络、压缩包、压缩台和其他数据输出目标。

输入输出流相对于内存设备而言:
将外设中的数据读取到内存:输入。
将内存的数据写到外设中:输出。

流按照操作数据分为两种:字节流与字符流。
字符流的由来:
其实就是字节流读取文字字节数据后,不直接操作而先查指定的编码表。获取对应的文字,再对这个文字进行操作。简单说:字节流+编码表。

字节流的两个顶层父类(抽象基类):
1.InputStream
2.OutputStream

字符类的两个顶层父类(抽象基类)
1.Reader(输入,读入)
2.Writer(输出,写出)

这些体系的子类几乎都以父类名作为后缀,而且子类名的前缀就是该对象的功能。

字符流

示例代码1:将一些文字存储到硬盘一个文件中。
注意:如果操作文字数据,建议优先考虑字符流。
而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer
硬盘的数据基本体现是文件。希望找到一个可以操作文件的Writer。
找到了FileWriter。

public class FileWriterDemo {

    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public static void main(String[] args) throws IOException {


        //创建一个可以往文件中写入字符数据的字符输出对象
        /*
         既然是一个往一个文件中写入文件数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)

         如果文件不存在,则会自动创建
         如果文件存在,则会被覆盖

         如果构造函数中加入true,可以实现对文件进行续写

         * */
        FileWriter fw=new FileWriter("demo.txt",true);
        /*
         * 调用Writer对象中的write(string)方法,写入数据、
         * 
         * 其实数据写入到临时存储缓冲区中。
         * */
        fw.write("i'm"+LINE_SEPARATOR+"sleepy");
        fw.write("sleeping");
//      /*
//       *进行刷新,将数据直接写到目的地中
//       * 
//       * */
//      fw.flush();


        /*
         * 关闭流,关闭资源。在关闭前会先调用flush刷新缓冲中的数据到目的地。
         * */
        fw.close();

//      fw.write("sleepy!!");//java.io.IOException:Stream closed

    }

}

代码示例2:加入异常处理的字符写出流。

public class IOExceptionDemo {

    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public static void main(String[] args) {

        FileWriter fw=null;//在try-catch语句外创建引用变量
        try {
            fw=new FileWriter("demo.txt",true);

            fw.write("i'm"+LINE_SEPARATOR+"sleepy");

        } catch (IOException e) {

            System.out.println(e.toString());
//          e.printStackTrace();
        }finally{
            if(fw!=null)//当指定一个不存在路径的文件时,如果不判断fw是否为空,会出现FileNoFoundException异常和NullPointerException异常。
                try {
                    fw.close();
                } catch (IOException e) {
                    //code...
                    throw new RuntimeException("关闭失败");
                }

        }

    }

}

代码示例3:读取一个文本文件。将读取到的文件打印到控制台。

用数组:

public class FileReaderDemo {
    //需求:读取一个文本文件。将读取到的文件打印到控制台。
    public static void main(String[] args) throws IOException {

        FileReader fr=new FileReader("demo.txt");

        //使用read(char[])读取文本文件数据
        //public int read(char[] cbuf):将字符读入数组。返回读取的字符数,如果到达流的末尾则返回-1.
        /*
         * 先创建字符数组
         * 
         * */

        char[] buf=new char[1024];

        int len=0;
        while((len=fr.read(buf))!=-1)
            System.out.println(new String(buf,0,len));

        fr.close();
    }
}

不用数组:

public class FileReaderDemo2 {
    //需求:读取一个文本文件。将读取到的文件打印到控制台。
    public static void main(String[] args) throws IOException {
        //1.创建读取字符数据的流对象
        /*
         * 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件时存在的。
         * 
         * 用一个读取流关联一个已存在文件
         * 
         * */
        FileReader fr=new FileReader("demo.txt");

//      //用Reader中的方法读取字符
        //public int read():读取单个字符。返回作为整数读取的字符,范围在0到65535之间(0x00-x0ffff),如果到达流的末尾,则返回-1
        //public int read(char[] cbuf):将字符读入数组。返回读取的字符数,如果到达流的末尾则返回-1.
        int ch=0;
        while((ch=fr.read())!=-1){
            System.out.println((char)ch);
        }

        fr.close();
    }

}

练习:将C盘的一个文本文件复制到d盘。
分析(复制原理):
1.读取C盘文件的数据。
2.将这些数据写入到d盘当中。
3.连读带写。

思路:
1.需要读取源
2.将读到的源数据写入到目的地
3.既然是操作文件数据,使用字符流

代码一:

public class CopyTextTest {

    public static void main(String[] args) throws IOException {

        //1.读取一个已有的文本,使用字符读取流和文件相关联
        FileReader fr=new FileReader("D:\\MyEclipse Professional 2014\\day20e\\demo.txt");

        //2.创建一个目的,用于存储读到的数据
        FileWriter fw=new FileWriter("copydemo.txt");
        //3.频繁的读写操作
        int ch=0;
        while((ch=fr.read())!=-1){
            fw.write(ch);

        }

        //4.关闭流资源
        fw.close();
        fr.close();
    }

}

代码2:创建数组作为缓冲区,提高读写效率。

public class CopyTextTest_2 {

    private static final int BUFFER_SIZE = 1024;

    public static void main(String[] args) {

        FileReader fr=null;
        FileWriter fw=null;

        try {
            fr=new FileReader("copydemo.txt");
            fw=new FileWriter("copydemo_2");

            //创建一个临时容器,用于缓存读取到的字符
            char[] buf=new char[BUFFER_SIZE];

            //定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数
            int len=0;

            while((len=fr.read(buf))!=-1){

                fw.write(buf, 0, len);
            }

        } catch (Exception e) {
//          e.printStackTrace();
            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();
                }
        }
    }
}

字符流的缓冲区

BufferedWriter和BufferedReader

前面创建数组作为一个缓冲容器,可以提高数据的读写速率。在java里有自带的缓冲流,不需要我们自己去定义数组作为缓冲区。
缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。
对应类:
1. BufferWriter 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
常用方法: newLine();
2. BufferReader
常用方法:
readLine():使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记。将标记前的缓存数据变成字符串返回、
bufr.read():这个read是从缓冲区中取出的字符数据。所以覆盖了父类中的read方法。

代码示例1:带缓存的字符输出流

public class BufferWriterDemo {

    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public static void main(String[] args) throws IOException {

        FileWriter fw=new FileWriter("BUF.txt");

        //为了提高写入的效率,使用了字符流的缓冲区。
        //创建一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象想关联
        BufferedWriter bufw=new BufferedWriter(fw);

        //使用缓冲区的写入方法将数据先写入到缓冲区中。
//      bufw.write("good"+LINE_SEPARATOR+"night");
//      bufw.write("good");
//      bufw.newLine();
//      bufw.write("evening");

        for(int x=1;x<=4;x++){
            bufw.write("good"+x);
            bufw.newLine();//换行
            bufw.flush();

        }
        //使用缓冲区的刷新方法将数据刷到目的地中。
        bufw.flush();
        //关闭缓冲区,其实关闭的就是被缓冲的流对象
        bufw.close();
    }
}

代码示例2:带缓存的字符写入流

public class BufferedReaderDemo {

    public static void main(String[] args) throws IOException {
        //demo_1();//自己定义数组作为缓冲区

        FileReader fr=new FileReader("BUF.txt");
        BufferedReader bufr=new BufferedReader(fr);

        String line=null;
        while((line=bufr.readLine())!=null){
            System.out.println(line);
        }
    }

    public static void demo_1() throws FileNotFoundException, IOException {
        FileReader fr=new FileReader("BUF.txt");

        char[] buf=new char[1024];

        int len=0;
        while((len=fr.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }

        fr.close();
    }
}

代码示例3:用带缓存的输入输出流将C盘的一个文本文件复制到d盘。

public class CopyTextByBufferTest {

    public static void main(String[] args) throws IOException {

        FileReader fr=new FileReader("BUF.txt");

        BufferedReader bufr=new BufferedReader(fr);

        FileWriter fw=new FileWriter("buf_copy.txt");
        BufferedWriter bufw=new BufferedWriter(fw);

        String line=null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.newLine();
            bufw.flush();
        }


        /*
        int ch=0;
        while((ch=bufr.read())!=-1){

            bufw.write(ch);

        }

        */


        bufw.close();
        bufr.close();

    }
}

字符缓冲流的实现原理(代码体现)

分析:
缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
缓冲区的原理:
其实就是从源中获取一批数据装进缓冲区中,再从缓冲区中不断取出一个一个数据。 在此次取完后,再从源中继续取一批数据进缓冲区。当源中的数据取光时,用-1作为结束标记。

public class MyBufferedReader extends Reader{

    private Reader r;

//  定义一个数组作为缓冲区
    private char[] buf=new char[1024];

//  定义一个指针用于操作这个数组的元素,当操作到最后一个元素后,指针应该归零。
    private int pos=0;

//  定义一个计数器用于记录缓冲区中的数据个数。当该数据减到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;


        /*//1.从源中获取一批数据到缓冲区中
        //需要先做判断,只有计数器为0时,才需要从源中获取数据。
        if(count==0){
            count=r.read(buf);

            if(count<0)
                return -1;

            //每次获取数据到缓冲区后,角标归零
            pos=0;
            char ch=buf[pos];

            pos++;
            count--;

            return ch;
        }else if(count>0){

            char ch=buf[pos];

            pos++;
            count--;

        }
        */

    }

    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;

    }
    public void myClose() throws IOException {

        r.close();
    }
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {

        return 0;
    }
    @Override
    public void close() throws IOException {
    }

}

使用自己定义的带缓存的字符读入流:

public class MyBufferedReaderDemo {

    public static void main(String[] args) throws IOException {

        FileReader fr=new FileReader("BUF.txt");
        MyBufferedReader bufr=new MyBufferedReader(fr);

        String line=null;
        while((line=bufr.myReadLine())!=null){
            System.out.println(line);
        }

        bufr.myClose();
    }
}

装饰设计模式

装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。

装饰和继承都能实现一样的特点:进行功能的扩展增强。
装饰和继承的区别:
继承体系中,要想对体系进行功能扩展,会产生更多的流对象,再想提高效率加入缓冲,则又会产生更多的流对象,这样会导致继承体系越来越臃肿,不够灵活。
而装饰设计模式,将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联,而不是像继承一样让缓冲和具体对象相结合,这样比较灵活。
装饰设计模式的特点:装饰类和被装饰类都必须所属同一个接口或者父类。

装饰类的一个子类:LineNumberReader
这是一个跟踪行号的缓冲字符输入流。此类定义了setLineNumber(int)和getLineNumber(int),它们可以分别用于设置和获取当前行号。

public class LineNumberReaderDemo {

    public static void main(String[] args) throws IOException {

        FileReader fr=new FileReader("BUF.txt");

        LineNumberReader lnr=new LineNumberReader(fr);

        String line=null;
        lnr.setLineNumber(100);
        while((line=lnr.readLine())!=null){
            System.out.println(lnr.getLineNumber()+":"+line);

        }
        lnr.close();
    }
}

字节流

字节流的基本操作与字符流类相同。但是它不仅可以操作字符,还可以操作媒体文件。
字节流基本操作演示:

public class ByteStreamDemo {

    public static void main(String[] args) throws IOException {

        //demo_write();
        demo_read();
    }

    public static void demo_read() throws IOException {

        //1.创建一个读取流对象。和指定文件关联
        FileInputStream fis=new FileInputStream("bytedemo.txt");

    /*  
        //读取方法1
//      System.out.println(fis.available());//5
        byte[] buf=new byte[fis.available()];//此方法慎用,文件过大的时候内存会不够
        fis.read(buf);                          //文件过大的时候,可以先用available方法读取文件的大小,再分段对文件进行读取
        System.out.println(new String(buf));
        */

        //读取方法2
        //建议使用这个读取数据的方式
        byte[] buf=new byte[1024];
        int len=0;
        while((len=fis.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
        }

        /*  
         *  
        //读取方法3
        int ch=0;
        while((ch=fis.read())!=-1){
            System.out.println((char)ch);
        }*/

        /*
        //读取方法4
        //一次读取一个字节
        int ch=fis.read();
        System.out.println(ch);
        */

        fis.close();

    }

    public static void demo_write() throws IOException {

        //创建字节输出流对象,用于操作文件
        FileOutputStream fos=new FileOutputStream("bytedemo.txt");

        //写数据。直接写入到了目的地中
        fos.write("sunny".getBytes());

        //fos.flush();这个类不需要flush,没什么意义。因为字节流不需要编解码,不需要进行临时存储缓冲,直接写入源码。
        fos.close();
    }
}

代码示例2:复制一个MP3格式的文件。

public class CopyMp3Test {

    public static void main(String[] args) throws IOException {

//      copy_1();
//      copy_2();
        copy_3();
        copy_4();

    }


    //千万不要用,效率极低
    public static void copy_4() throws IOException {


        FileInputStream fis =new FileInputStream("D:\\KuGou\\马頔 - 南山南.mp3");

        FileOutputStream fos=new FileOutputStream("D:\\KuGou\\1.mp3");

        int ch =0;
        while((ch=fis.read())!=-1){
            fos.write(ch);
        }

        fos.close();
        fis.close();


    }
    //不建议
    public static void copy_3() throws IOException {

        FileInputStream fis =new FileInputStream("D:\\KuGou\\马頔 - 南山南.mp3");

        FileOutputStream fos=new FileOutputStream("D:\\KuGou\\1.mp3");


        byte[] buf=new byte[fis.available()];

        fis.read(buf);
        fos.write(buf);

        fos.close();
        fis.close();

    }

    public static void copy_2() throws IOException {

        FileInputStream fis =new FileInputStream("D:\\KuGou\\马頔 - 南山南.mp3");
        BufferedInputStream bufis=new BufferedInputStream(fis);

        FileOutputStream fos=new FileOutputStream("D:\\KuGou\\1.mp3");
        BufferedOutputStream bufos=new BufferedOutputStream(fos);



        int ch=0;
        while((ch=bufis.read())!=-1){
            bufos.write(ch);

//          bufos.flush();
        }

        bufos.close();
        bufis.close();

    }

    public static void copy_1() throws IOException {

        FileInputStream fis =new FileInputStream("D:\\KuGou\\马頔 - 南山南.mp3");

        FileOutputStream fos=new FileOutputStream("D:\\KuGou\\1.mp3");


        byte[] buf=new byte[1024];

        int len =0;
        while((len=fis.read(buf))!=-1){
            fos.write(buf,0,len);
        }

        fos.close();
        fis.close();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值