java io重点整理部分

java io流

     1)java io流相关概念

输出流:

 

 

输入流:


 

因此输入和输出都是从程序的角度来说的。

字节流:一次读入或读出是8位二进制

字符流:一次读入或读出是16位二进制

字节流和字符流的原理是相同的,只不过处理的单位不同而已。后缀是Stream是字节流,而后缀是Reader,Writer是字符流。 

节点流:直接与数据源相连,读入或读出。


直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

处理流:与节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。


     2)java io流的分类

按流向分:
输入流: 程序可以从中读取数据的流。
输出流: 程序能向其中写入数据的流。
按数据传输单位分:
字节流: 以字节为单位传输数据的流
字符流: 以字符为单位传输数据的流
按功能分:
节点流: 用于直接操作目标设备的流
过滤流: 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。

     3)java io类结构图

     流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。Java把这些不同来源和目标的数据都统一抽象为数据流,在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流,java io 类结构如下:


[1]输入字节流InputStream:InputStream 是所有的输入字节流的父类,它是一个抽象类;ByteArrayInputStream、StringBufferInputStream、 FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据;PipedInputStream 是从与其它线程共用的管道中读取数据;ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

 

[2]输出字节流OutputStream:OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据,ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。

[3]字节流的输入与输出的对应


图中蓝色的为主要的对应部分,红色的部分就是不对应部分。紫色的虚线部分代表这些流一般要搭配使用。从上面的图中可以看出Java IO 中的字节流是极其对称的。

不对称的类介绍如下:

1>LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部 分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行 号,看起来也是可以的。好像更不入流了。
2>PushbackInputStream 的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
3>StringBufferInputStream 已经被Deprecated,本身就不应该出现在InputStream 部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
4>SequenceInputStream 可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
5>PrintStream 也可以认为是一个辅助工 具。主要可以向其他输出流,或者FileInputStream 写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO 包!System.out 和System.out 就是PrintStream 的实例!

[4]字符输入流Reader:Reader 是所有的输入字符流的父类,它是一个抽象类;CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据;PipedReader 是从与其它线程共用的管道中读取数据;BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象;FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号;InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。

[5]字符输出流Writer:Writer 是所有的输出字符流的父类,它是一个抽象类;CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据,BufferedWriter 是一个装饰器为Writer 提供缓冲功能;PrintWriter 和PrintStream 极其类似,功能和使用也非常相似;
OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。

[6]字符流的输入与输出的对应


[8]字符流与字节流转换

转换流的特点:
其是字符流和字节流之间的桥梁
可对读取到的字节数据经过指定编码转换成字符
可对读取到的字符数据经过指定编码转换成字节
何时使用转换流?
当字节和字符之间有转换动作时;
流操作的数据需要编码或解码时。
具体的对象体现:
InputStreamReader:字节到字符的桥梁
OutputStreamWriter:字符到字节的桥梁
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。

     4)java 管道通信

     Java提供管道功能,实现管道通信的类有两组:PipedInputStream和PipedOutputStream或者是PipedReader和PipedWriter。管道通信主要用于不同线程间的通信
一个PipedInputStream实例对象必须和一个 PipedOutputStream实例对象进行连接而产生一个通信管道。PipedOutputStream向管道中写入数 据,PipedIntputStream读取PipedOutputStream向管道中写入的数据。一个线程的PipedInputStream对象能 够从另外一个线程的PipedOutputStream对象中读取数据。

 

  1.  //Sender类   
  2. package pipeCommu;  
  3. import java.io.PipedInputStream;  
  4. import java.io.PipedOutputStream;  
  5. public class Sender extendsThread{  
  6.    private PipedOutputStream out=new PipedOutputStream();//发送者创建PipedOutputStream向外写数据;   
  7.    public PipedOutputStream getOut(){  
  8.        return out;  
  9.    }  
  10.    public void run(){  
  11.        String strInfo="hello,receiver";  
  12.        try{  
  13.            out.write(strInfo.getBytes());//写入数据  
  14.            out.close();  
  15.        }catch(Exception e){  
  16.            e.printStackTrace();  
  17.        }  
  18.    }  
  19. }    
  20.  //Reader类,负责接收数据   
  21. package pipeCommu;  
  22. import java.io.PipedInputStream;   
  23. public class Reader extends Thread{  
  24.    private PipedInputStream in=new PipedInputStream();//发送者创建PipedOutputStream向外写数据  
  25.    public PipedInputStream getIn(){  
  26.        returnin;  
  27.    }  
  28.    public void run(){  
  29.        byte[] buf=new byte[1024];//声明字节数组  
  30.        try{  
  31.            int len=in.read(buf);//读取数据,并返回实际读到的字节数  
  32.            System.out.println("receive from sender:"+newString(buf,0,len));  
  33.            in.close();  
  34.        }catch(Exception e){  
  35.            e.printStackTrace();  
  36.        }  
  37.    }  
  38. }  
  39. package pipeCommu;   
  40. import java.io.*;  
  41. public class PipedStream {  
  42.     public static void main(String[] args) throws Exception{  
  43.        Sender send=new Sender();  
  44.        Reader read=new Reader();  
  45.        PipedOutputStream out=send.getOut();  
  46.        PipedInputStream in=read.getIn();  
  47.        out.connect(in);//或者也可以用in.connect(out);  
  48.        send.start();  
  49.        read.start();  
  50.    }    
  51. }    
  52. package pipeCommu;   
  53. import java.io.*;  
  54. public class PipedCommu {  
  55.     public static void main(String[] args) {  
  56.         // TODOAuto-generatedmethodstub  
  57.         try{  
  58.              PipedReader reader=new PipedReader();  
  59.              PipedWriter writer=new PipedWriter(reader);  
  60.              Thread a=new Send(writer);  
  61.              Thread b=new Read(reader);  
  62.              a.start();  
  63.              Thread.sleep(1000);  
  64.              b.start();  
  65.         }catch(IOException e){  
  66.              e.printStackTrace();  
  67.                
  68.         }catch(InterruptedException e1){  
  69.              e1.printStackTrace();             
  70.         }  
  71.     }  
  72. }  
  73.     class Send extends Thread{  
  74.         PipedWriter writer;  
  75.         public Send(PipedWriter writer){  
  76.              this.writer=writer;  
  77.         }  
  78.         public void run(){  
  79.              try{  
  80.                   writer.write("this is a.hello world".toCharArray());  
  81.                   writer.close();  
  82.              }catch(IOException e){  
  83.                   e.printStackTrace();  
  84.              }              
  85.         }  
  86.     }  
  87.     class Read extends Thread{  
  88.         PipedReader reader;  
  89.         public Read(PipedReader reader){  
  90.              this.reader=reader;  
  91.         }  
  92.         public void run(){  
  93.              System.out.println("this is B");  
  94.              try{  
  95.                   char[] buf=new char[1000];  
  96.                   reader.read(buf,0,100);  
  97.                   System.out.println(new String(buf));  
  98.              }catch(Exception e){  
  99.                   e.printStackTrace();  
  100.              }  
  101.         }  
  102.     }   

     5)java 对象序列化

     对于一个存在Java虚拟机中的对象来说,其内部的状态只是保存在内存中。JVM退出之后,内存资源也就被释放,Java对象的内部状态也就丢失了。而 在很多情况下,对象内部状态是需要被持久化的,将运行中的对象状态保存下来(最直接的方式就是保存到文件系统中),在需要的时候可以还原,即使是在 Java虚拟机退出的情况下。 
     对象序列化机制是Java内建的一种对象持久化方式,可以很容易实现在JVM中的活动对象与字节数组(流)之间进行转换,使用得Java对象可以被存 储,可以被网络传输,在网络的一端将对象序列化成字节流,经过网络传输到网络的另一端,可以从字节流重新还原为Java虚拟机中的运行状态中的对象。 

     对于任何需要被序列化的对象,都必须要实现接口Serializable,它只是一个标识接口,本身没有任何成员,只是用来标识说明当前的实现类的对象可以被序列化。

     如果在类中的一些属性,希望在对象序列化过程中不被序列化,使用关键字transient标注修饰就可以。当对象被序列化时,标注为transient的成员属性将会自动跳过。

     注:

      [1].当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法,静态的成员变量和transient标注的成员变量。 
   [2].如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存还原,而且会是递归的方式。
   [3].如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。可以将这个引用标记transient,那么对象仍然可以序列化。

       java对象序列化示例代码:

实体类:

 

  1. class Student implements Serializable{          
  2.     private String name;    
  3.     private transient int age;    
  4.     private Course course;          
  5.     public void setCourse(Course course){    
  6.         this.course = course;    
  7.     }        
  8.     public Course getCourse(){    
  9.         return course;    
  10.     }          
  11.     public Student(String name, int age){    
  12.         this.name = name;    
  13.         this.age = age;    
  14.     }     
  15.     public String  toString(){    
  16.         return "Student Object name:"+this.name+" age:"+this.age;    
  17.     }    
  18. }    
  19.     
  20. class Course implements Serializable{          
  21.     private static String courseName;    
  22.     private int credit;          
  23.     public Course(String courseName, int credit){    
  24.         this.courseName  = courseName;    
  25.         this.credit = credit;    
  26.     }          
  27.     public String toString(){              
  28.         return "Course Object courseName:"+courseName    
  29.                +" credit:"+credit;    
  30.     }    
  31. }    

将对象写入文件序列化

 

  1. public class TestWriteObject{      
  2.     public static void main(String[] args) {      
  3.         String filePath = "C://obj.txt";    
  4.         ObjectOutputStream objOutput = null;    
  5.         Course c1 = new Course("C language", 3);    
  6.         Course c2 = new Course("OS", 4);             
  7.         Student s1 = new Student("king", 25);    
  8.         s1.setCourse(c1);              
  9.         Student s2 = new Student("jason", 23);    
  10.         s2.setCourse(c2);     
  11.         try {    
  12.             objOutput = new ObjectOutputStream(new FileOutputStream(filePath));    
  13.             objOutput.writeObject(s1);    
  14.             objOutput.writeObject(s2);    
  15.             objOutput.writeInt(123);    
  16.         } catch (FileNotFoundException e) {    
  17.             e.printStackTrace();    
  18.         } catch (IOException e) {    
  19.             e.printStackTrace();    
  20.         }finally{    
  21.             try {    
  22.                 objOutput.close();    
  23.             } catch (IOException e) {    
  24.                 e.printStackTrace();    
  25.             }    
  26.         }              
  27.         System.out.println("Info:对象被写入"+filePath);    
  28.     }   

从文件中读取对象,反序列化

 

  1. public class TestReadObject  {    
  2.         
  3.     public static void main(String[] args) {    
  4.             
  5.         String filePath = "C://obj.txt";    
  6.         ObjectInputStream objInput = null;    
  7.         Student s1 = null,s2 = null;    
  8.         int intVal = 0;    
  9.         
  10.         try {    
  11.             objInput = new ObjectInputStream(new FileInputStream(filePath));    
  12.             s1 = (Student)objInput.readObject();    
  13.             s2 = (Student)objInput.readObject();    
  14.             intVal = objInput.readInt();    
  15.                 
  16.         } catch (FileNotFoundException e) {    
  17.             e.printStackTrace();    
  18.         } catch (IOException e) {    
  19.             e.printStackTrace();    
  20.         }catch(ClassNotFoundException cnfe){    
  21.             cnfe.printStackTrace();    
  22.         }finally{    
  23.                 
  24.             try {    
  25.                 objInput.close();    
  26.             } catch (IOException e) {    
  27.                 e.printStackTrace();    
  28.             }    
  29.         }              
  30.         System.out.println("Info:文件"+filePath+"中读取对象");    
  31.         System.out.println(s1);    
  32.         System.out.println(s1.getCourse());    
  33.         System.out.println(s2);    
  34.         System.out.println(s2.getCourse());    
  35.         System.out.println(intVal);    
  36.     }    
  37. }    

     7、java  nio

    1)java nio简介

     nio 是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如有:为所有的原始类型提供 (Buffer) 缓存支持;字符集编码解码解决方案;Channel :一个新的原始 I/O 抽象;支持锁和内存映射文件的文件访问接口;提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O 。

    2)java nio非阻塞原理

     一个常见的网络 IO 通讯流程如下 :


     从该网络通讯过程来理解一下何为阻塞 :在以上过程中若连接还没到来,那么 accept 会阻塞 , 程序运行到这里不得不挂起, CPU 转而执行其他线程。在以上过程中若数据还没准备好, read 会一样也会阻塞。阻塞式网络 IO 的特点:多线程处理多个连接。每个线程拥有自己的栈空间并且占用一些 CPU 时间。每个线程遇到外部未准备好的时候,都会阻塞掉。阻塞的结果就是会带来大量的进程上下文切换。且大部分进程上下文切换可能是无意义的。比如假设一个线程监听一个端口,一天只会有几次请求进来,但是该 cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。

     何为非阻塞?
     下面有个隐喻:
      一辆从 A 开往 B 的公共汽车上,路上有很多点可能会有人下车。司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好?
      1. 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车。 ( 类似阻塞式 )
      2. 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车。 ( 类似非阻塞 )
     很显然,每个人要到达某个目的地可以认为是一个线程,司机可以认为是 CPU 。在阻塞式里面,每个线程需要不断的轮询,上下文切换,以达到找到目的地的结果。而在非阻塞方式里,每个乘客 ( 线程 ) 都在睡觉 ( 休眠 ) ,只在真正外部环境准备好了才唤醒,这样的唤醒肯定不会阻塞。
     非阻塞的原理
     把整个过程切换成小的任务,通过任务间协作完成。由一个专门的线程来处理所有的 IO 事件,并负责分发。事件驱动机制:事件到的时候触发,而不是同步的去监视事件。线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的进程切换

     以下是异步 IO 的结构:


    Reactor 就是上面隐喻的售票员角色。每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。
    异步 IO 核心 API:

    Selector:异步 IO 的核心类,它能检测一个或多个通道 (channel) 上的事件,并将事件分发出去。使用一个 select 线程就能监听多个通道上的事件,并基于事件驱动触发相应的响应。而不需要为每个 channel 去分配一个线程。
    SelectionKey:包含了事件的状态信息和时间对应的通道的绑定。

    3)Buffer结构、主要方法

     Buffer内部结构如图:


    一个 buffer 主要由 position,limit,capacity 三个变量来控制读写的过程。此三个变量的含义见如下表格:    

 

参数

写模式    

读模式

position

当前写入的单位数据数量。

当前读取的单位数据位置。

limit

代表最多能写多少单位数据和容量是一样的。

代表最多能读多少单位数据,和之前写入的单位数据量一致。

capacity

buffer 容量

buffer 容量

    Buffer 常见方法:
    flip(): 写模式转换成读模式
    rewind() :将 position 重置为 0 ,一般用于重复读。
    clear() :清空 buffer ,准备再次被写入 (position 变成 0 , limit 变成 capacity) 。
    compact(): 将未读取的数据拷贝到 buffer 的头部位。
    mark() 、 reset():mark 可以标记一个位置, reset 可以重置到该位置。
    Buffer 常见类型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、ShortBuffer 。
    channel 常见类型 :FileChannel 、 DatagramChannel(UDP) 、 SocketChannel(TCP) 、 ServerSocketChannel(TCP)

     4)Buffer、Chanel

     Channel 和 buffer 是 NIO 是两个最基本的数据类型抽象。Buffer:是一块连续的内存块,是 NIO 数据读或写的中转地;Channel:数据的源头或者数据的目的地,用于向 buffer 提供数据或者读取 buffer 数据,buffer 对象的唯一接口,支持异步 I/O 。chanel与Buffer关系如下:


    Buffer、Chanel使用例子:

  •     
  • import java.io.FileInputStream;    
  • import java.io.FileOutputStream;    
  • import java.nio.ByteBuffer;    
  • import java.nio.channels.FileChannel;    
  •     
  • public class CopyFile {    
  •     public static void main(String[] args) throws Exception {    
  •         String infile = "C:\\copy.sql";    
  •         String outfile = "C:\\copy.txt";    
  •         // 获取源文件和目标文件的输入输出流    
  •         FileInputStream fin = new FileInputStream(infile);    
  •         FileOutputStream fout = new FileOutputStream(outfile);    
  •         // 获取输入输出通道    
  •         FileChannel fcin = fin.getChannel();    
  •         FileChannel fcout = fout.getChannel();    
  •         // 创建缓冲区    
  •         ByteBuffer buffer = ByteBuffer.allocate(1024);    
  •         while (true) {    
  •             // clear方法重设缓冲区,使它可以接受读入的数据    
  •             buffer.clear();    
  •             // 从输入通道中将数据读到缓冲区    
  •             int r = fcin.read(buffer);    
  •             // read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1    
  •             if (r == -1) {    
  •                 break;    
  •             }    
  •             // flip方法让缓冲区可以将新读入的数据写入另一个通道    
  •             buffer.flip();    
  •             // 从输出通道中将数据写入缓冲区    
  •             fcout.write(buffer);    
  •         }    
  •     }    
  • }    

     5)nio.charset

     字符编码解码 : 字节码本身只是一些数字,放到正确的上下文中被正确被解析。向 ByteBuffer 中存放数据时需要考虑字符集的编码方式,读取展示 ByteBuffer 数据时涉及对字符集解码。Java.nio.charset 提供了编码解码一套解决方案。
     以我们最常见的 http 请求为例,在请求的时候必须对请求进行正确的编码。在得到响应时必须对响应进行正确的解码。
     以下代码向 baidu 发一次请求,并获取结果进行显示。例子演示到了 charset 的使用。

  1. package nio.readpage;    
  2.     
  3. import java.nio.ByteBuffer;    
  4. import java.nio.channels.SocketChannel;    
  5. import java.nio.charset.Charset;    
  6. import java.net.InetSocketAddress;    
  7. import java.io.IOException;    
  8. public class BaiduReader {    
  9.     private Charset charset = Charset.forName("GBK");// 创建GBK字符集    
  10.     private SocketChannel channel;    
  11.     public void readHTMLContent() {    
  12.         try {    
  13.             InetSocketAddress socketAddress = new InetSocketAddress(    
  14. "www.baidu.com", 80);    
  15. //step1:打开连接    
  16.             channel = SocketChannel.open(socketAddress);    
  17.         //step2:发送请求,使用GBK编码    
  18.             channel.write(charset.encode("GET " + "/ HTTP/1.1" + "\r\n\r\n"));    
  19.             //step3:读取数据    
  20.             ByteBuffer buffer = ByteBuffer.allocate(1024);// 创建1024字节的缓冲    
  21.             while (channel.read(buffer) != -1) {    
  22.                 buffer.flip();// flip方法在读缓冲区字节操作之前调用。    
  23.                 System.out.println(charset.decode(buffer));    
  24.                 // 使用Charset.decode方法将字节转换为字符串    
  25.                 buffer.clear();// 清空缓冲    
  26.             }    
  27.         } catch (IOException e) {    
  28.             System.err.println(e.toString());    
  29.         } finally {    
  30.             if (channel != null) {    
  31.                 try {    
  32.                     channel.close();    
  33.                 } catch (IOException e) {    
  34.                 }    
  35.             }    
  36.         }    
  37.     }    
  38.     public static void main(String[] args) {    
  39.         new BaiduReader().readHTMLContent();    
  40.     }    
  41. }   

注:本文java io流部分内容参考博客:《java io流分析整理》http://blog.csdn.net/llhhyy1989/article/details/7388059、《java Io流学习总结》http://blog.csdn.net/heliteng/article/details/12812715《java 对象序列化》http://yuyiming.iteye.com/blog/1277089

 


posted @ 2015-10-26 20:00  涂文鼎  阅读(195)  评论(0编辑  收藏  举报