黑马程序员java学习—流的操作

来源:互联网 发布:js中向下取整 编辑:程序博客网 时间:2024/05/06 17:19

一、装饰设计模式

装饰设计模式。

解决的问题:给已有的对象提供增强额外的功能。还不用对原有对象进行修改。 

比继承更为灵活。

 Writer

|--TextWriter

|--MediaWriter

现有一个体系用于各种数据的写入。

但是,发现写入效率有点低。想要对其进行效率的提高。

可以使用缓冲技术来完成的。

已有对象中的写入方法,不够高效,可以通过派生子类的形式对其进行复写,定义高效的写入动作。

 Writer

|--TextWriter

|--BufferTextWriter

  |--MediaWriter

|--BufferMediaWriter

|--DataWriter

|--BufferDataWriter

通过继承的方式提高了效率。

但是对于扩展性是一个问题。而且所需的功能越多,子类就越多。

如何解决这个问题呢?优化!

既然都需要缓冲,对数据写入效率进行提高 。

可以转变一下思想,这样来做,将缓冲技术单独进行封装。

哪个对象需要缓冲,就把哪个对象传递给缓冲对象即可。 

 class Buffer{

Buffer(TextWriter w){

       }

Buffer(MediaWriter w){

      }

}

 体系就变成了这样:

 Writer

|--TextWriter

  |--MediaWriter

  |--BufferWriter

BufferWriter的出现,增强了Writer体系中的功能。

这种设计方式比原理更为灵活,避免了继承的臃肿。

将这样解决方式就定义了一个名称方便于后人使用:装饰设计模式。

记住:装饰类和被装饰类都所属于同一个体系。 

 二、行号装饰类

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

 {

      FileReader fr = new FileReader("tempfile\\demo.java");

      LineNumberReader lnr = new LineNumberReader(fr);

      String line = null;

      lnr.setLineNumber(100);

      while((line=lnr.readLine())!=null)

     {

      System.out.println(lnr.getLineNumber()+":"+line);

     }

    lnr.close();

}

三、字符流:

    FileReader

    FileWriter

    BufferedReader

    BuffereWriter

字节流。

    InputStream  OutputStream

    FileInputStream FileOutputStream

    BufferedInputStream BufferedOutputStream

转换流:

    字节流--->字符流的桥梁。InputStreamReader 

    字符流--->字节流的桥梁。OutputStreamWriter

需求:将一个字符串写入一个文件中。使用FileOutputStream来演示。

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

 {

           FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt");

           fos.write("abcdef".getBytes());//字节流的直接操作文件写入时,直接将数据写入到目的地。

          为啥FileWriter就需要刷呢?

          那是因为FileWriter底层使用了字节流在操作字符数据时,会先将这些数据进行临时存储,并查指定的编码表。按照指定的编码表中的内容写入到目的地。

         "你好"FileWriter ---> 编码表 GBK--->数字字节--->目的地。

         而字节流处理的数据不一定都是文字数据,所以是不需要指定查表的。 

         直接在操作文件数据时,就将具体的字节数据写入到目的地。

         fos.flush();//使用的是父类OutputStream的flush。该方法什么都没有做,只有OutputStram某一些子类定义了该方法的具体内容。 

         fos.close();//必须有,因为要关闭资源。

}

需求:读取一个文件。用字节流。

    int ch = fis.read();

    System.out.println("ch="+ch);//一次读一个,打一个。

    int ch = 0;

   while((ch=fis.read())!=-1){

            System.out.println((char)ch);

    }//读取并存储到数组,将数组打印。以该种方式。

    byte[] buf = new byte[1024];

    int len = 0;

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

          System.out.println(new String(buf,0,len));

    }//直接创建一个刚刚好的大小的数组缓冲区。

    byte[] buf = new byte[fis.available()];//如果文件过大,内存会溢出。

   fis.read(buf);//将数据存储到刚刚好的数组中。

   System.out.println(new String(buf));

   fis.read();

   System.out.println(fis.available());//获取流关联的文件的字节数。

   fis.close();

四、复制mp3

1、自定义数组缓冲区的方式。 

public static void copy_1() throws IOException 

{

      FileInputStream fis = new FileInputStream("C:\\0.mp3");//读取流对象,和mp3关联。

     FileOutputStream fos = new FileOutputStream("c:\\1.mp3");//写入流对象,明确存储mp3数据的目的。

     byte[] buf = new byte[1024*8];//定义一个字节缓冲区。

 

    int len = 0;

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

            fos.write(buf,0,len);

     }

    fos.close();

   fis.close();

}

2、不建议。使用刚刚好的缓冲区。因为文件过大会溢出。

public static void copy_3() throws IOException

 {

      FileInputStream fis = new FileInputStream("C:\\0.mp3");

      FileOutputStream fos = new FileOutputStream("c:\\3.mp3");

 

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

     fis.read(buf);

     fos.write(buf);

     fos.close();

     fis.close();

}

3、使用字节流已有的缓冲区。 

public static void copy_2() throws IOException {

       FileInputStream fis = new FileInputStream("C:\\0.mp3");

       FileOutputStream fos = new FileOutputStream("c:\\2.mp3");

      BufferedInputStream bufis = new BufferedInputStream(fis);

      BufferedOutputStream bufos = new BufferedOutputStream(fos);

      int by = 0;

     while((by=bufis.read())!=-1){

     bufos.write(by);

     }

    bufos.close();

   bufis.close();

}

4、读一个 写一个

public static void copy_4( ) throws IOException 

{

       FileInputStream fis = new FileInputStream("C:\\0.mp3");

       FileOutputStream fos = new FileOutputStream("c:\\4.mp3");

       int by = 0;

       while((by=fis.read())!=-1){

              fos.write(by);

       }

      fos.close();

      fis.close();

}

五、键盘录入

读取键盘录入。 

public static void readKey() throws IOException

{

    InputStream in = System.in;//获取到读取键盘录入的流对象。类型是InputStream

    int ch = in.read();//用流读取数据。如果没有数据录入,那么控制台会一致等待输入。read方法就是一个阻塞式的方式。 

    System.out.println("ch="+ch);

    int ch1 = in.read();

    System.out.println("ch1="+ch1);

    int ch2 = in.read();

   System.out.println("ch2="+ch2);

    in.close();//不需要关闭。 

                  //注意:系统中获取的流对象都是唯一的,如果将其关闭,就不能在使用了。

                 //想要在使用只有重新再次运行这个程序才可以。 

                //所以一般情况下,从System获取到的流对象,一般不需要关闭。随着程序的停止而结束。 

                // InputStream in2 = System.in;这里会出错 。

               // int ch4 = in2.read();

              // System.out.println("ch4="+ch4);

}

六、键盘录入练习

 需求:读取键盘录入的数据,将这些数据转成大写打印在屏幕上,

如果录入的是 over, 程序结束。 

 思路:

1,通过键盘录入读取字节数据。

2,将这些读到的字节数据进行存储以变成字符串。

3,对这个字符串进行判断。如果不是over就将其转成大写打印。如果是over就结束。 

public static void readKey2() throws IOException 

{

     InputStream in = System.in;//获取键盘录入流对象。 

    StringBuilder sb = new StringBuilder();//定义一个容器用于存储读取到的字节。

    int ch = 0;//循环读取键盘。

    while((ch=in.read())!=-1){

    //需要对读取到的字节进行判断。

    //如果是/r 或者 /n,不存储,并视为一次录入内容结束符。对之前的录入数据进行判断。

             if(ch=='\r')

                    continue;

             if(ch=='\n'){

                    String s = sb.toString();

             if("over".equals(s)){

                    break;//记住:如果要使用键盘录入,一定要自定义结束标记。

              }

           else{

                   System.out.println(s.toUpperCase());

           sb.delete(0, sb.length());//清空缓冲区。

           }

          }

         else

         sb.append((char)ch);

   }

}

七、转换流 

既然键盘录入转成大写输出,并判断over结束的程序中

使用了到对字节数据的存储,并对回车符进行判断。

 发现这个功能和readLine方法一致。

因为readLine是字符流BufferedReader对象中过的方法。

而键盘录入是字节流。

能不能将这个字节读取流转成字符流呢?因为BufferedReader只能对字符流进行装饰 。

这就用到了转换流

转换流:

字节流--->字符流的桥梁。InputStreamReader 

字符流--->字节流的桥梁。OutputStreamWriter

需求:从键盘读取数据,转成大写,显示在屏幕上。 

 

InputStream in = System.in;//获取键盘录入,数据源。

InputStreamReader isr = new InputStreamReader(in);//为了处理文字数据方便。将字节数据转成字符数据.这个功能在转换流中。

BufferedReader bufr = new BufferedReader(isr);//为了提高了读取的效率。使用缓冲区。 

 

OutputStream out = System.out;//打印到显示器上。目的。

OutputStreamWriter osw = new OutputStreamWriter(out);

//因为要打印到显示器上的数据都是文字数据。所以必须将这些文字数据转成字节数据。

//具备这个功能的对象是OutputStreamWriter.

BufferedWriter bufw = new BufferedWriter(osw);//为了提高写入的效率。

String line = null;//频繁的读写操作。 

while((line=bufr.readLine())!=null){

         if("over".equals(line))

               break;

         bufw.write(line.toUpperCase());

         bufw.newLine();//newLine()需要有BufferedWriter对象

         bufw.flush();

        }

        bufw.close();

        bufr.close();//因为是从System获取的流可以不关闭,随着系统的结束而结束。

}

八、流的操作规律:

规律就是四个明确?

1,明确源和目的。

源:InputStream   Reader 一定是被读取的。

目的:OutputStream  Writer 一定是被写入的。 

2,处理的数据是否是纯文本的数据?

是:使用字符流。Reader Writer

否:使用字节流。 InputStream OutputStream

如果是源并且是纯文本,Reader

如果是目的并且是纯文本,Writer

到这里,两个明确确定完,就可以确定出要使用哪个体系。

接下来,就应该明确具体这个体系要使用哪个具体的对象。

3,明确数据所在的设备:

源设备:

键盘(System.in)

硬盘(FileXXX)

内存(数组)

网络(Socket)

目的设备:

显示器(控制台System.out)

硬盘(FileXXX)

内存(数组)

网络(Socket)

具体使用哪个对象就可以明确了。

4,明确是否需要额外功能?

1,是否需要高效?缓冲区Buffered

2,是否需要转换?转换流

后面会学到更多。

九、需求练习

需求1:复制一个文本文件。

直接明确具体对象并创建。

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

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

然后是频繁的读写操作。

需要额外功能吗?

需要,高效。 使用缓冲区。

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

需求3:读取键盘录入,存储到一个文件中。

明确是否是纯文本?一般键盘录入的都是文字,所以是纯文本的。 

InputStream in = System.in;

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

需要将键盘录入的字节转成字符。

使用转换流。而且是 将字节-->字符的转换流对象。InputStreamReader

InputStreamReader isr = new InputStreamReader(System.in);

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

需要,高效。

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));

需求4:读取一个文本文件,显示到显示器上。

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

OutputStream out = System.out;

需要将已有的字符数据转成字节。字符-->字节的桥梁 OutputStreamWriter

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

OutputStreamWriter osw = new OutputStreamWriter(System.out);

需要高效。

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWrier(System.out));

需求5:读取一个文本文件,将文件中文本按照指定的编码表UTF-8写入到另一个文件中。

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

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

这样做不行,满足不了需求,为什么呢?

因为这个两个对象在操作文本数据,都是用了默认的编码表。在我的本机中默认码表是GBK.而需求中希望写入到文件的数据是按照utf-8的码表。其实这两个对象就是字节流+默认编码表。 

源对象不变。

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

需要目的为指定编码表。

这时就要用到转换流。因为转换流中可以指定具体的编码表。 需要往里传递一个字节流,而且要操作文件,FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");

需要高效

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8"));

原创粉丝点击