3.通道 Channel

一、通道(Channel):由java.nio.channels包定义的 。Channel 表示 IO 源与目标打开的连接。

Channel 类似于传统的 ‘流’。只不过 Channel 本身不能直接访问数据,Channel只能与Buffer进行交互

二、 /*通道的主要实现类*/

Java 为 Channel 接口提供的 最主要实现类如下:

  FileChannel:用于读取、写入、映射和操作文件的通道

  SocketChannel:通过TCP 读写网络中的数据

  ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个 SocketChannel

三、 /*如何获取通道*/

获取通道

  * 1.Java针对 支持通道的类提供了 getChannel()方法

    * 本地IO:

      * FileInputStream/FileOutputStream

      * RandomAccessFile


    * 网络IO:

      * Socket

      * ServerSocket

      * DatagramSocket


  2.在 JDK1.7 中 的 NIO.2 针对各个通道提供了静态方法 open()

  3.在 JDK1.7 中 的 NIO.2 的 File 工具类的newByteChannel()

利用通道进行数据传输


四、 /*通道之间的数据传输*/

  transferForm() 将数据从源通道 传输到其他 Channel中

  transferTo() 其他 Channel 从 原通道中 获取数据

  

五、 /*分散(Scatter) 与 聚集(Gather)*/

  分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区

  聚集写入(Gathering Writes):将多个缓冲区中的 数据 聚集到通道中

  
六、 /*字符集:Charset*/

  编码:字符串 -> 字节数组

  解码: 字节数组 -> 字符串
  

  1 /*
  2  * 一、通道(Channel):用于源节点 与目标节点的连接 ,在java nio 中 负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输
  3  * 
  4  * 二、通道的主要实现类
  5  *     java.nio.channels.Channel 接口
  6  *         |--FileChannel
  7  *         |--SocketChannel
  8  *         |--ServerSocketChannel
  9  *         |--DatagramChannel
 10  * 
 11  * 三、获取通道
 12  * 1.Java针对 支持通道的类提供了 getChannel()方法
 13  *      本地IO:
 14  *      FileInputStream/FileOutputStream
 15  *      RandomAccessFile
 16  * 
 17  *   网络IO:
 18  *   Socket
 19  *   ServerSocket
 20  *   DatagramSocket
 21  *  
 22  * 2.在 JDK1.7 中 的 NIO.2 针对各个通道提供了静态方法 open()
 23  * 
 24  * 3.在 JDK1.7 中 的 NIO.2 的 File 工具类的newByteChannel()
 25  * 
 26  * 四、通道之间的数据传输
 27  * transferForm()
 28  * transferTo()
 29  * 
 30  * 五、分散(Scatter) 与 聚集(Gather)
 31  * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区
 32  * 聚集写入(Gathering Writes):将多个缓冲区中的 数据 聚集到通道中
 33  * 
 34  * 六、字符集:Charset
 35  * 编码:字符串 -> 字节数组
 36  * 解码: 字节数组 -> 字符串
 37  * 
 38  * */
 39 public class TestChannel {
 40     
 41     //使用指定字符集 进行编码 和 解码
 42     @Test
 43     public void test6() throws IOException {
 44         //1.选择字符集
 45         Charset charset1 = Charset.forName("GBK");
 46         
 47         //2.获取编码器
 48         CharsetEncoder encoder = charset1.newEncoder();
 49         
 50         //3.获取解码器
 51         CharsetDecoder decoder = charset1.newDecoder();
 52         
 53         //4.创建字符串缓冲区,放入需要编码的字符串
 54         CharBuffer charBuffer = CharBuffer.allocate(1024);
 55         charBuffer.put("迅雷影音");
 56         
 57         //5.对字符串进行编码  (编码:字符串 -> 字节数组)
 58         charBuffer.flip();    //操作字符串缓冲区,解码字符串之前 需要 flip 一下
 59         ByteBuffer byteBuffer = encoder.encode(charBuffer);
 60 
 61         // byteBuffer 此时 是 初始状态,即position 是0
 62         //调用 这个 for ,每 get 一次,position + 1 
 63         //这样才能 在 flip 之后, 解码 需要操作的数据
 64         for(int i = 0;i<8;i++) {
 65             System.out.println(byteBuffer.position());
 66             System.out.println(byteBuffer.get());
 67         }
 68         
 69         //6.对字节数组进行解码 (解码: 字节数组 -> 字符串)
 70         byteBuffer.flip();    //操作字节缓冲区,解码字节数组之前 需要 flip 一下
 71         CharBuffer charBuffer2 = decoder.decode(byteBuffer);
 72         
 73         //打印解码后的数据
 74         System.out.println(charBuffer2.toString());
 75     }
 76     
 77     //5.显示所有的字符集
 78     @Test
 79     public void test5() {
 80         Map<String,Charset> map = Charset.availableCharsets();
 81         Set<Entry<String, Charset>> set = map.entrySet();
 82         
 83         for(Entry<String, Charset> entry:set) {
 84             System.out.println(entry.getKey() + " = " + entry.getValue());
 85         }
 86     }
 87 
 88     //4.分散和 聚集 (多个缓冲区)
 89     @Test
 90     public void test4() throws IOException {
 91         // "rw" 是指 具有read 和 write 的 权限
 92         RandomAccessFile raf = new RandomAccessFile("1.txt", "rw");
 93         //1.获取通道
 94         FileChannel channel1 = raf.getChannel();
 95         
 96         //2.分配指定大小的缓冲区 (多个)
 97         ByteBuffer buf1 = ByteBuffer.allocate(100);
 98         ByteBuffer buf2 = ByteBuffer.allocate(1024);
 99         ByteBuffer[] bufs = {buf1,buf2};
100         
101         
102         //4.聚集写入
103         RandomAccessFile raf2 = new RandomAccessFile("2.txt","rw");
104         FileChannel channel2 = raf2.getChannel();
105         
106         
107         //3.分散读取    
108         while(channel1.read(bufs)!= -1) {
109             for(ByteBuffer buf:bufs) {
110                 buf.flip();
111             }
112             //4.聚集写入
113             channel2.write(bufs);
114             
115             System.out.println("-----------------缓冲区1---------------");
116             System.out.println(new String(bufs[0].array(),0,bufs[0].limit()) );
117             System.out.println("-----------------缓冲区1---------------");
118             
119             System.out.println("-----------------缓冲区2---------------");
120             System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
121             System.out.println("-----------------缓冲区2---------------");
122             
123             for(ByteBuffer buf:bufs) {
124                 buf.clear();
125             }
126         
127 
128         }            
129         
130     }
131     
132     //3.通道之间的数据传输
133     @Test
134     public void test3() throws Exception {
135         FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
136         FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
137         
138         //直接使用transferTo 或者 transferFrom 完成通道之间的数据传输
139         inChannel.transferTo(0, inChannel.size(), outChannel);
140         outChannel.transferFrom(inChannel, 0, inChannel.size());
141         
142         inChannel.close();
143         outChannel.close();
144     }
145     
146     //2.使用直接缓冲区完成文件的复制(内存映射文件)(这种方式效率更高)
147     //会出现的问题 :文件传输已经完成,但是程序仍然没有结束,因为 java 虚拟机无法及时 对 内存映射文件进行  进行释放,必须要等指向映射文件的那个变量被回收
148     @Test
149     public void test2() {
150         
151         long start = System.currentTimeMillis();
152         
153         FileChannel inChannel = null;
154         FileChannel outChannel = null;
155         MappedByteBuffer inMapperBuf = null;
156         MappedByteBuffer outMapperBuf = null;
157         
158         try {
159             //使用 FileChannel 的 open方法 (可以不用创建流就可以得到通道)
160             inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
161             outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
162         
163             //内存映射文件 (也是一个 缓冲区 ,继承自 ByteBuffer,直接缓冲区也只能使用 ByteBuffer)
164             inMapperBuf = inChannel.map(MapMode.READ_ONLY,0 , inChannel.size());
165             outMapperBuf = outChannel.map(MapMode.READ_WRITE,0 , inChannel.size());
166             
167         } catch (IOException e) {
168             e.printStackTrace();
169         }  
170 
171     
172         //直接对缓冲区进行数据读写操作
173         byte[] bytes = new byte[inMapperBuf.limit()];
174         inMapperBuf.get(bytes);
175         outMapperBuf.put(bytes);
176         
177         try {
178             if(inChannel != null) {
179                 inChannel.close();
180             }
181             if(outChannel != null) {
182                 outChannel.close();
183             }
184         } catch (IOException e) {
185             e.printStackTrace();
186         }
187         
188         long end = System.currentTimeMillis();
189         System.out.println("花费:" +  (end-start));
190         
191     }
192     
193     //1.利用通道完成文件的复制
194     @Test
195     public void test1() {
196         long start = System.currentTimeMillis();
197         
198         FileInputStream fis = null;
199         FileOutputStream fos = null;
200         try {
201             fis = new FileInputStream("1.jpg");
202             fos = new FileOutputStream("2.jpg");
203         } catch (FileNotFoundException e) {
204             e.printStackTrace();
205         }
206         
207         //1.获取流对应的 通道
208         FileChannel inChannel = fis.getChannel();
209         FileChannel outChannel = fos.getChannel();
210         
211         //2.分配指定大小的缓冲区
212         ByteBuffer buffer = ByteBuffer.allocate(1024);
213         
214         //3.将通道中的数据存入缓存区
215         try {
216             //read 方法,从Channel 中读取数据到 ByteBuffer (即往 缓冲区中 put 数据),所以不用 flip
217             while(inChannel.read(buffer) != -1) {
218                 buffer.flip();  //切换到读取数据模式            
219                 
220                 //4.将缓存区中的数据写入通道中 (需要get 出 缓冲区中的 数据 ,所以需要 flip)
221                 outChannel.write(buffer);
222                 buffer.clear();  //清空缓冲区,使其继续循环
223             }
224         } catch (IOException e) {
225             e.printStackTrace();
226         } finally {
227             if(inChannel != null) {
228                 try {
229                     inChannel.close();
230                 } catch (IOException e) {                
231                     e.printStackTrace();
232                 }
233             }
234             if(outChannel != null) {
235                 try {
236                     outChannel.close();
237                 } catch (IOException e) {                    
238                     e.printStackTrace();
239                 }
240             }
241             if(fis != null) {
242                 try {
243                     fis.close();
244                 } catch (IOException e) {                    
245                     e.printStackTrace();
246                 }
247             }
248             if(fos != null) {
249                 try {
250                     fos.close();
251                 } catch (IOException e) {                    
252                     e.printStackTrace();
253                 }
254             }    
255         }
256         
257         long end = System.currentTimeMillis();
258         System.out.println("花费:" +  (end-start));        
259     }
260     
261 }


  

原文地址:https://www.cnblogs.com/xuzekun/p/7435504.html