传统IO总结-14年写的

Posted coolgame

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了传统IO总结-14年写的相关的知识,希望对你有一定的参考价值。


上面是IO家族的全家福,这里先讲一下InputStreamOutputStream


字节流InputStreamOutputStream
一个我们日常开发常见的场景,复制文件,应该如何实现?一般的,我们会使用InputStream的read方法从源文件读取数据,然后用OutputStream的write方法向目标文件写数据。
读写部分的代码是这样的:

 

 

try{

      //构造一个输入流对象(读数据)source.txt文本的内容为: 呵呵呵呵呵

      InputStream is = new FileInputStream("F:\\source.txt");

      //构造一个输出流对象(写数据)

      OutputStream os = new FileOutputStream("F:\\target.txt");

      int len;//表示读入的数据(十进制的形式表示)              

      while((len = is.read())!=-1){

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

         os.write(len);

      }

      os.close();

      is.close();

}catch(Exception e){

      e.printStackTrace();

}

 

byte是基本类型的一种,八位,取值范围是‘1000 0000‘到‘0111 1111‘(这是补码),转换成十进制是‘-128‘到‘127‘。
int也是基本类型的一种,三十二位,取值范围-2147483648到2147483647。代码里,读取一字节(byte)的数据,然后返回的是一个十进制的数字(int),因为int有32位,所以接收只有8位的byte简直绰绰有余,把这个十进制数字作为参数传给write,在write方法里自然会还原成对应的byte数据,写到目标文件里。这样,复制就完成了。其实,InputStream有三个重载的read方法。它们是:
1.read():一次读取一个字节;返回一个int,读到文件末尾返回-1;   
2.read(byte [] b):一次读取一个byte数组长度的数据;返回一个int,读到文件末尾返回-1,这个int代表的是实际读取的字节个数,如果数组长度是1024,在数据足够的情况下,返回值都是1024,代表read方法读取了1024个字节。读取到的数据都存放在数组b中,可以通过OutputStream的write(b)方法写到目标文件中。
3.read(byte[] b,int off,int len):将输入流中最多 len 个数据字节读入字节数组。尝试读取多达 len 字节,但可能读取较少数量。以整数形式返回实际读取的字节数。将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。

这里说一下native关键字,native方法是指本地方法,当在方法中调用一些不是由java语言写的代码或者在方法中用java语言,直接操纵计算机硬件时要声明为native方法,java中,通过JNI(Java Native Interface,java本地接口)里实现。FileInputStream的read方法就是native的,据说是交给c库实现了。
OutputStream有三个重载的write方法。参数形式和InputStream是一样的,不说了。

FileInputStream和FileOutputStream   

因为操作文件,所以要使用FileInputStream和FileOutputStream,它们重写了父类的方法。这里说一点,new FileOutputStream("文件路径/文件名"),如果文件路径存在,文件不存在,则会根据文件名创建文件。如果文件路径不存在,则会报错。(可以修改参数,决定FileOutputStream是否替换已存在文件)。
最后说一句,如果你要复制文件,一个字节一个字节的copy是很慢的。

BufferedInputStream和BufferedOutputStream

这两货是缓冲流,存在就是为了提高读写速率的,各有一个8K(默认的)的缓冲区。可以这样使用它们:

try{

        InputStream is = new FileInputStream("F:\\source.txt");

        OutputStream os = new FileOutputStream("F:\\target.txt");

        InputStream bis = new BufferedInputStream(is);

        OutputStream bos = new BufferedOutputStream(os);

        int len;//表示读入的数据(十进制的形式表示)

        while((len = bis.read())!=-1){

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

              bos.write(len);

        }

        bos.close();

        bis.close();

}catch(Exception e){

        e.printStackTrace();

}

可以这样理解,BufferedInputStream一次读取8K的数据到内存中(如果有这么多数据),然后向BufferedOutputStream中写8K数据,当达到8K,BufferedOutputStream会一次将这8k数据从内存写到目标文件里。因为大大减少了将数据从硬盘copy到内存的次数(一个字节一个字节读写,文件里有多少个字节就要copy多少次,学过操作系统的同学知道,这是很浪费时间的),所以节省了大把的时间。需要注意的是,可以通过BufferedOutputStream的flush方法送出不足8k的数据。当然,关闭流也可以达到相同效果。另外,关闭上层的流,下层的流也会同时关闭,所以代码里不用再关闭FileInputStream和FileOutputStream了。 

DataInputStream和DataOutputStream

如果要求你向文件中写入boolean类型,你要怎么做?首先,你要知道true和false的二进制编码是什么,然后通过字节流可以将2进制编码写入文件。当读取的时候,你要判断不同的二进制编码是什么意思,才可以把文件中的二进制编码还原成true和false。很麻烦,用数据流可以解决这个问题。
代码如下:

try {

               File file = new File("F:\\source.txt");

               DataOutputStream out = new DataOutputStream(new FileOutputStream(file));

               out.writeBoolean(true);

               out.writeByte((byte) 121);

               out.writeChar((char) ‘爱‘);

               out.writeShort((short) 100);

               out.writeInt(300);

               out.writeLong(100000000000000L);

               out.writeUTF("你好");

               out.close();

           } catch (FileNotFoundException e) {

               e.printStackTrace();

           } catch (SecurityException e) {

               e.printStackTrace();

           } catch (IOException e) {

               e.printStackTrace();

           }

 

 

使用代码读取文件中的内容:

         try {

               File file = new File("F:\\source.txt");

               DataInputStream in = new DataInputStream(new FileInputStream(file));

               System.out.println("readBoolean():"+ in.readBoolean());

               System.out.println("readByte():"+in.readByte());

               System.out.println("readChar():"+in.readChar());

               System.out.println("readShort():"+in.readShort());

               System.out.println("readInt():"+in.readInt());

               System.out.println("readLong():"+in.readLong());

               System.out.println("readUTF():"+ in.readUTF());

               in.close();

           } catch (FileNotFoundException e) {

               e.printStackTrace();

           } catch (SecurityException e) {

               e.printStackTrace();

           } catch (IOException e) {

               e.printStackTrace();

           }

 
 输出结果如下:

据我观察,write和read是一一对应关系。你在文件里写入的是一长串二进制数据,其中有Int、byte、boolean,当你读取的时候,也要按照写入的顺序去读取,否则是读不出准确的数据的。 
PS:请看source.txt文件里存储的内容 :

 


ObjectInputStream和ObjectOutputStream

要求你向文件中写入java中的对象,怎么办。用这两个家伙吧,它们存在的意义和DataInputStream与DataOutputStream是一样的。
写数据:

       try {

               ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("F:/source.txt"));

               out.writeBoolean(true);

               out.writeByte((byte)65);

               out.writeChar(‘a‘);

               out.writeInt(20131015);

               out.writeFloat(3.14F);

               out.writeDouble(1.414D);

               // 写入HashMap对象

               HashMap map = new HashMap();

               map.put("one", "red");

               map.put("two", "green");

               map.put("three", "blue");

               out.writeObject(map);

               // 写入自定义的Box对象,Box实现了Serializable接口

               Box box = new Box("desk", 80, 48);

               out.writeObject(box);

               out.close();

           } catch (Exception ex) {

               ex.printStackTrace();

           }   

 读取代码:

           try {

               ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:/source.txt"));

               System.out.printf("boolean:%b\n" , in.readBoolean());

               System.out.printf("byte:%d\n" , (in.readByte()&0xff));

               System.out.printf("char:%c\n" , in.readChar());

               System.out.printf("int:%d\n" , in.readInt());

               System.out.printf("float:%f\n" , in.readFloat());

               System.out.printf("double:%f\n" , in.readDouble());

               // 读取HashMap对象

               HashMap map = (HashMap) in.readObject();

               Iterator iter = map.entrySet().iterator();

               while (iter.hasNext()) {

                   Map.Entry entry = (Map.Entry)iter.next();

                   System.out.printf("%-6s -- %s\n" , entry.getKey(), entry.getValue());

               }

               // 读取Box对象,Box实现了Serializable接口

               Box box = (Box) in.readObject();

               System.out.println("box: " + box);

               in.close();

           } catch (Exception e) {

               e.printStackTrace();

           }

我只想说,完全可以用理解DataInputStream与DataOutputStream的方式去理解这两个流。


System.out.println()

先看看System的源码:
public final class System {

       public final static PrintStream out = null;

...

}

我们知道了,syso实际上调用的是PrintStream的println方法。


字符流Reader和Writer

首先讲一下字节流和字符流的区别:

读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
我们主要使用Writer的write方法和Reader的read方法。下面介绍它们的具体子类。

InputStreamReader和OutputStreamWriter

既然是处理字符的,首先一个问题就是如何编码。 

       private static void testWrite() {

              try {

                  File file = new File("f:/source.txt");

                  OutputStreamWriter out = new OutputStreamWriter(

                          new FileOutputStream(file), "utf-8");

                  out.write("哈哈哈啊哈哈哈呀呀");

                  out.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

          }

          private static void testRead() {

              try {

                  File file = new File("f:/source.txt");

                  InputStreamReader in = new InputStreamReader(new FileInputStream(file), "utf-8");

                  char c1 = (char) in.read();

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

                  //跳过2个字符

                  in.skip(2);

                  char c2 = (char) in.read();

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

                  // 测试read(char[] cbuf, int off, int len)

                  char[] buf = new char[2];

                  in.read(buf, 0, buf.length);

                  System.out.println("buf=" + (new String(buf)));

                  //循环方式读取

                  int b;

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

                      System.out.println("@"+(char)b);

                  }

                  in.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

          }

 

source.txt里的内容:


因为我这个source.txt就是用的utf-8编码,所以文件里面的内容不是乱码~~~ 

BufferedReader、BufferedWriter和PrintWriter

这三个流是处理字符串的,有了它们终于可以成行的读写了,欢呼~ 

         private static void testWrite() {

              try {

                  File file = new File("f:/source.txt");

                  OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), "utf-8");

                  PrintWriter pw = new PrintWriter(out);

                  //BufferedWriter bw = new BufferedWriter(out);

                  pw.println("我说我写的手都麻了你信么?");

                  pw.append("啊!");

                  pw.write("反正我信了!");

                  //bw.newLine();

                  //bw.write("O(∩_∩)O哈哈~");

                  pw.close();

                  //bw.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

          }

          private static void testRead() {

              try {

                  File file = new File("f:/source.txt");

                  InputStreamReader in = new InputStreamReader(new FileInputStream(file), "utf-8");

                  BufferedReader br = new BufferedReader(in);

                  String str = "";

                  while((str=br.readLine())!=null){

                      System.out.println(str);

                  }

                  br.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

          }

输出:


只能算是简单的总结吧,写累了,不写了。 

 

@font-face { font-family: "宋体"; }@font-face { font-family: "宋体"; }@font-face { font-family: "@宋体"; }@font-face { font-family: "Cambria"; }@font-face { font-family: "Monaco"; }p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 0.0001pt; text-align: justify; font-size: 12pt; font-family: Cambria; }a:link, span.MsoHyperlink { color: blue; text-decoration: underline; }a:visited, span.MsoHyperlinkFollowed { color: purple; text-decoration: underline; }.MsoChpDefault { font-family: Cambria; }div.WordSection1 { }








































以上是关于传统IO总结-14年写的的主要内容,如果未能解决你的问题,请参考以下文章

2019年总结

java nio知识点总结

单片机用定时器分配任务的程序结构总结

Java 网络IO编程总结

java IO总结(BIONIOAIO)

2021年总结:前路有光,初心莫忘