黑马程序员——java基础之IO流

来源:互联网 发布:大数据对教育的影响 编辑:程序博客网 时间:2024/06/11 14:04

——- android培训、java培训、期待与您交流! ———-


IO流


一 初识IO流

IO: Input Output的缩写

IO流的特点:

  1. IO流是用来处理设备之间的数据传输
  2. java对数据的操作是通过流的方式
  3. java用于操作流的对象都在IO包中
  4. 流按操作数据分为两种: 字节流和字符流
  5. 流按流向分为: 输入流, 输出流

IO流常用基类:

  1. 字节流的抽象基流:InputStream和OutputStream
  2. 字符流的抽象基流:Reader和Writer

注意:
—由着四个类派生出来的子类名称都是以其父类名作为子类名的后缀

如:
—InputStream的子类 FileInputStream
—Reader的子类FileReader


二 字符流

一 字符流的写入:


字符流两个基类
——–Reader 和 Writer
既然IO流是用于操作数据的
那么数据的最常见的体现形式是:文件
我们从一个小例子入手:


//需求:在硬盘上创建一个文件并写入一些文字数据import java.io.*;class FileWriterDemo{    public static void main(String[] args) throws Exception    {        //第一步 创建一个Filewriter对象,该对象一被初始化就必须要明确被操作的文件        //而且该文件会被创建到指定目录下,如果该目录下已有同名文件,将被覆盖        //其实该步就是在明确数据要存放的目的地(要写字 要先有纸)        FileWriter fw = new FileWriter("D:/demo.txt");        //第二步 调用write(),将字符串写入到流中        fw.write("duang duang");        //刷新流对象中的缓冲区中的数据        //将数据刷新到目的地中去        fw.flush();        //关闭流资源 但是关闭之前会刷新一次缓冲中的数据        //将数据刷到目的地中        //和flush的区别:flush刷新后,流可以继续使用;close 关闭了        fw.close();    }}

运行结果:


这里写图片描述


总结
字符流的写入规律:

  1. 第一步 创建一个Filewriter对象,该对象一被初始化就必须要明确被操作的文件
  2. 第二步 调用write(),将字符串写入到流中
  3. 刷新流对象中的缓冲区中的数据,将数据刷到目的地中
  4. 关闭流close();

注意事项:

1.其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。
2.由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。

IO异常处理方式:
看下面的实例代码:

/*    IO异常处理方式*/import java.io.*;class FileWriterDemo2{    public static void main(String[] args)     {        //在try外面定义  在里面new        FileWriter fw = null;        try            {                   fw = new FileWriter("v:/demo.txt");//没有执行成功,创建对象失败            fw.write("java");            }        catch(IOException e)                {                    System.out.println(e.toString());                }        finally{            if(fw!=null)//流还存在,则关闭            try{                    fw.close();                }            catch(IOException e)                {                    System.out.println(e.toString());                }            }    }}

二 字符流的读取:

先看一个简单的例子:

/*    FileReader  单字符读取方式.*/import java.io.*;class FileReaderDemo{    public static void main(String[] args) throws Exception    {        //创建一个文件读取流对象  和指定名称的文件相关联        //要保障该文件是已经存在的,如果不存在,则异常        FileReader fr = new FileReader("D:/demo.txt");        //调用读取流对象的read方法        //read 一次读取一个字符,而且会自动往下读        int ch =0;        while((ch=fr.read())!=-1)        {            System.out.println((char)ch);            }}                   fr.close();    }}

运行结果:
这里写图片描述


读取规律总结:
1. 在创建一个文件时,如果目录下有同名文件将被覆盖。
2. 在读取文件时,必须保证该文件已存在,否则出异常。


三 字符流读写练习:

需求: 文本的复制: 将c盘一个文本文件复制到D盘

/*    将c盘一个文本文件复制到Dpan    原理    其实就是将c盘下的文件数据存数到D盘的一个文件中    1 在D盘创建一个文件,用于存储C盘文件中的数据    2 定义读取流和C盘文件关联    3 通过不断的读写完成数据的存储    4 关闭资源*/import java.io.*;class CopyText{    public static void main(String[] args) throws IOException    {        //copy_2;        copy_1();    }    //第二种方式读写: 一行行读写    public static void copy_2()    {    FileWriter fw = null;    FileReader fr = null;    try    {        fw = new FileWriter("D:/demo1.txt");        fr = new FileReader("D:/demo.txt");         char [] buf = new char[1024];        int len = 0;        while((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)        {        }                if(fw!=null)        try        {            fw.close();        }        catch(IOException e)        {        }    }    }    //第一种方式读写:从C盘读一个写一个    public static void copy_1() throws IOException    {        //创建目的地        FileWriter fw = new FileWriter("D:/demo2.txt");        //与已有文件关联        FileReader fr = new FileReader("D:/demo.txt");        int ch = 0;        while((ch=fr.read())!=-1)        {            fw.write(ch);        }        fw.close();        fr.close();     }}

运行结果:
这里写图片描述


三字符流缓冲区——BufferedReader和BufferedWriter

1.缓冲区的出现是为了提高流的操作效率而出现的
2.所以在创建缓冲区之前 必须先有流对象
3.该缓冲区中提供了一个跨平台的换行符 newLine();

使用BufferedWriter步骤:

  1. 创建一个字符写入流对象。
  2. 为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
  3. 调用write方法写入数据到指定文件
  4. 其实关闭缓冲区,就是在关闭缓冲区中的流对象。

如下面的示例代码:

/*    缓冲区的出现是为了提高流的操作效率而出现的    所以在创建缓冲区之前 必须先有流对象    该缓冲区中提供了一个跨平台的换行符 newLine();*/import java.io.*;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<8;i++)        {            bufw.write("asdfg"+i);            bufw.newLine();            bufw.flush();        }        //只要用到缓冲区 就一定要刷新        //bufw.flush();        //关闭缓冲区就是关闭对应的流对象        bufw.close();           }}

运行结果:
这里写图片描述


使用BufferedWriter步骤:
1.创建一个读取流对象和文件相关联
2.了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。
3.调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null
4.关闭流资源

示例代码:

/*    字符读取缓冲区    该缓冲区提供了一个一次读一行的方法readLine 方便对文本数据的获取    当返回null 时 表示读到文件末尾    readLine()方法返回的时候只返回回车符之前的额数据内容,并不返回回车符*/import java.io.*;class BufferedReaderDemo{    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.println(line);        }           bufr.close();    }}

运行结果:
这里写图片描述


缓冲区手机实际应用的小练习
需求: 练习:通过缓冲区复制一个.java文件

/*    练习:通过缓冲区复制一个.java文件*/import java.io.*;class CopyTextByBuf{    public static void main(String[] args)     {        BufferedReader bufr = null;        BufferedWriter bufw = null;        try        {            bufr = new BufferedReader(new FileReader("BufferedReaderDemo.java"));               bufw = new BufferedWriter(new FileWriter("bufwiter_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("写入关闭失败");            }           }       }}

运行结果:
这里写图片描述


四 装饰设计模式

1.装饰类:
当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
2.装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
3.装饰和继承的区别:

—1.装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。

—2.装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。

—3.从继承结构转为组合结构。

装饰者模式设计范例:

/*需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader*/import java.io.*;//自定义缓冲类class MyBufferedReader extends Reader{    private Reader r;//定义接收的流对象    MyBufferedReader(Reader r)    {        this.r=r;    }    //自定义整行读取    public String myReadLine()throws IOException    {        //创建一个容器,用来存储一行的字符        StringBuilder sb =new StringBuilder();        //一个字符一个字符读取        for (int ch=0;(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 read(char[] cbuf, int off, int len) throws IOException    {        return r.read(cbuf,off,len);    }    //复写父类的close方法    public void close()throws IOException    {        r.close();    }}//测试MyBufferedReaderclass  MyBufferedReaderDemo{    public static void main(String[] args)     {        MyBufferedReader mbr=null;        try        {            mbr=new MyBufferedReader(new FileReader("BufferedReaderDemo.java"));            for (String line=null;(line=mbr.myReadLine())!=null ; )            {                System.out.println(line);//显示效果            }        }        catch (IOException e)        {            throw new RuntimeException("读取数据失败");        }        finally        {            try            {                if(mbr!=null)                    mbr.close();            }            catch (IOException e)            {                throw new RuntimeException("读取流关闭失败");            }        }       }}

运行结果:
这里写图片描述


小结:
在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
上面讲到的MyBufferedReader的例子就是最好的装饰设计模式的例子。



五 字节流

  1. 字节流包括: InputStream 输入流(读) OutputStream 输出流(写)
  2. 字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。
  3. 由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

示例代码:

/*    练习: 复制一张图片    步骤    1 用字节流读取流对象和图片关联    2 用字节写入流对象创建一个文件,用于存储获取到的图片数据    3 通过循环读写 完成数据的存储    4 关闭资源*/import java.io.*;class CopyPic{    public static void main(String[] args)     {        FileOutputStream fos = null;        FileInputStream fis =null;        try{        //指定复制的路劲        fos = new FileOutputStream("D:\\2.jpg");        //关联要复制的图片        fis = new FileInputStream("D:\\1.jpg");        byte [] buf = new byte[1024];        int len = 0;        while((len=fis.read(buf))!=-1)            {                fos.write(buf,0,len);            }        }        catch(IOException e)        {            throw new RuntimeException("读取图片失败");           }        finally        {            try                 {                    if(fis!=null)                    fis.close();                }               catch(IOException e)                {                    throw new RuntimeException("写入失败");                 }            try                 {                    if(fos!=null)                    fos.close();                }               catch(IOException e)                {                    throw new RuntimeException("读取失败");                 }        }    }}

运行结果:


这里写图片描述



六 字节流缓冲区

1.字节流缓冲区的出现也是为了提高读写效率.
2.字节流的读写原理特点:

  • 将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
  • 循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。
  • 每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。
  • 取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。
  • 当文件中的全部数据都被读取出时,read()方法就返回-1。

3.自定义读取字节流缓冲区
示例代码:


/*自定义字节流读取缓冲区思路:1、定义一个固定长度的数组2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为03、每次将字节数据存入元素要先将数组中的元素取完*/import java.io.*;class MyBufferedInputStream{       private InputStream in;       private byte[] by=new byte[1024];       private int count=0,pos=0;       MyBufferedInputStream(InputStream in)       {              this.in=in;       }       //自定义读方法,一次读一个字节       public int myRead()throws IOException       {              //通过in对象读取硬盘上数据,并存储by中。              //存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据              if(count==0)              {                     count=in.read(by);                     if(count<0)//文件数据全部被读取出来了                            return-1;                     pos=0;//初始化指针                     byte b=by[pos];                     count--;//每被读一个字节,表示数组中的字节数少一个                     pos++;//指针加1                     return b&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。                                          //通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。                                          //而在输出字节流写入数据时,只写该int类型数据的最低8位。              }              else if(count>0) //如果数组中的数据没被读取完,则继续读取              {                     byte b=by[pos];                     count--;                     pos++;                     return b&0xff;              }              return-1;       }       //自定义关闭资源方法       public void close()throws IOException       {              in.close();       }}//测试自定义输入字节流缓冲区class MyBufferedCopyMp3{       public static void main(String[] args)        {              long start=System.currentTimeMillis();              //利用字节流的缓冲区进行复制              copy_2();              long end=System.currentTimeMillis();              System.out.println("复制共用时:"+(end-start)+"毫秒");       }       //使用字节流的缓冲区进行复制       public static void copy_2()       {              BufferedOutputStream bout=null;              MyBufferedInputStream bin=null;              try              {                     //关联复制文件输入流对象到缓冲区                     bin=new MyBufferedInputStream(new FileInputStream("C:/Users/asus/Desktop/一样的月光.mp3"));                     //指定文件粘贴位置的输出流对象到缓冲区                     bout=new BufferedOutputStream(new FileOutputStream("C:/Users/asus/Desktop/一样的月光2.mp3"));                     int by=0;                     while((by=bin.myRead())!=-1)                     {                            bout.write(by);//将缓冲区中的数据写入指定文件中                     }              }              catch(IOException e)              {                     throw new RuntimeException("MP3复制失败");              }              finally              {                     try                     {                            if(bin!=null)                                   bin.close();//关闭输入字节流                     }                     catch(IOException e)                     {                            throw new RuntimeException("读取字节流关闭失败");                     }                     try                     {                            if(bout!=null)                                   bout.close();//关闭输出字节流                     }                     catch(IOException e)                     {                            throw new RuntimeException("写入字节流关闭失败");                     }              }       }}

注意事项:

1.字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。 所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。

2.byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。



七 流的操作规律总结

一 基本分析思路:

1.明确目的和源
- 源:键盘录入。
- 目的:控制台。
2.明确需求: 想把键盘录入的数据存储到一个文件中。
- 源:键盘
- 目的:文件。
- 使用字节流通向字符流的转换流(桥梁):InputStreamReader
3.明确需求: 想要将一个文件的数据打印在控制台上。
- 源:文件
- 目的:控制台
- 使用字符流通向字节流的转换流(桥梁):OutputStreamWriter


二 基本操作规律:

三个明确:

  1. 明确源和目的。

    • 源:输入流。InputStream Reader
    • 目的:输出流。OutputStream Writer
  2. 明确操作的数据是否是纯文本。

    • 是:字符流
    • 否:字节流
  3. 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:

    • 源设备:内存,硬盘,键盘
    • 目的设备:内存,硬盘,控制台

分析小例子:

1 . 需求: 将一个文本文件中的数据存储到另一个文件中 复制文件

  • 源 因为是源 所以使用读取流 InputStream Reader
  • 是不是操作文本文件?
  • 是!这时选择Reader

这样体系就明确了

接下来明确该体系中的那个对象?

  • 明确设备:硬盘上的一个文件
  • Reader体系中可以操作文件的对象 FileReader
  • 是否需要提高效率 是 BufferedReader

  • FileReader fr = new FileReader(“a.txt”);

  • 目的:OutputStream Writer
    是不是纯文本
    是!Writer
    设备:硬盘上的文件
    Writer中可以操作文件的对象时FileWriter

    是否需要提高效率 是 BufferedWriter
    FileWriter fr = new FileWriter(“b.txt”);

代码实现如下:

import java.io.*;class InputStreamReaderDemo{    public static void main(String[] args) throws IOException    {//      //获取键盘录入对象//      InputStream in = System.in;//      //      //将字节流对象转换成字符流对象 使用转换流 InputStreamReader//      InputStreamReader isr = new InputStreamReader(in);//      //      //为了提高效率 将字符串进行缓冲区的技术高效操作 使用BufferedReader//      //      BufferedReader bufr = new BufferedReader(isr);        //把上面三句话变成一句话        BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("InputStreamReaderDemo.java")));//键盘录入最常见写法 记下来        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));        String line = null;        while((line=bufr.readLine())!=null)        {            if ("over".equals(line))                break;                bufw.write(line.toUpperCase());                bufw.newLine();                bufw.flush();           }    }}


——- android培训、java培训、期待与您交流! ———-


0 0