NIO系列——通道(Channel)
1 import java.io.FileInputStream;
2 import java.io.FileOutputStream;
3 import java.io.IOException;
4 import java.io.RandomAccessFile;
5 import java.nio.ByteBuffer;
6 import java.nio.CharBuffer;
7 import java.nio.MappedByteBuffer;
8 import java.nio.channels.FileChannel;
9 import java.nio.channels.FileChannel.MapMode;
10 import java.nio.charset.CharacterCodingException;
11 import java.nio.charset.Charset;
12 import java.nio.charset.CharsetDecoder;
13 import java.nio.charset.CharsetEncoder;
14 import java.nio.file.Paths;
15 import java.nio.file.StandardOpenOption;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19
20 import org.junit.Test;
21
22 /*
23 * 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
24 *
25 * 二、通道的主要实现类
26 * java.nio.channels.Channel 接口:
27 * |--FileChannel
28 * |--SocketChannel
29 * |--ServerSocketChannel
30 * |--DatagramChannel
31 *
32 * 三、获取通道
33 * 1. Java 针对支持通道的类提供了 getChannel() 方法
34 * 本地 IO:
35 * FileInputStream/FileOutputStream
36 * RandomAccessFile
37 *
38 * 网络IO:
39 * Socket
40 * ServerSocket
41 * DatagramSocket
42 *
43 * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
44 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
45 *
46 * 四、通道之间的数据传输
47 * transferFrom()
48 * transferTo()
49 *
50 * 五、分散(Scatter)与聚集(Gather)
51 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
52 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
53 *
54 * 六、字符集:Charset
55 * 编码:字符串 -> 字节数组
56 * 解码:字节数组 -> 字符串
57 *
58 */
59 public class TestChannel {
60
61 //字符集
62 @Test
63 public void test6() throws IOException{
64 Charset cs1 = Charset.forName("GBK");
65
66 //获取编码器
67 CharsetEncoder ce = cs1.newEncoder();
68
69 //获取解码器
70 CharsetDecoder cd = cs1.newDecoder();
71
72 CharBuffer cBuf = CharBuffer.allocate(1024);
73 cBuf.put("好好学习天天向上!");
74 cBuf.flip();
75
76 //编码
77 ByteBuffer bBuf = ce.encode(cBuf);
78
79 for (int i = 0; i < 12; i++) {
80 System.out.println(bBuf.get());
81 }
82
83 //解码
84 bBuf.flip();
85 CharBuffer cBuf2 = cd.decode(bBuf);
86 System.out.println(cBuf2.toString());
87
88 System.out.println("------------------------------------------------------");
89
90 Charset cs2 = Charset.forName("GBK");
91 bBuf.flip();
92 CharBuffer cBuf3 = cs2.decode(bBuf);
93 System.out.println(cBuf3.toString());
94 }
95
96 @Test
97 public void test5(){
98 Map<String, Charset> map = Charset.availableCharsets();
99
100 Set<Entry<String, Charset>> set = map.entrySet();
101
102 for (Entry<String, Charset> entry : set) {
103 System.out.println(entry.getKey() + "=" + entry.getValue());
104 }
105 }
106
107 //分散和聚集
108 @Test
109 public void test4() throws IOException{
110 RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
111
112 //1. 获取通道
113 FileChannel channel1 = raf1.getChannel();
114
115 //2. 分配指定大小的缓冲区
116 ByteBuffer buf1 = ByteBuffer.allocate(100);
117 ByteBuffer buf2 = ByteBuffer.allocate(1024);
118
119 //3. 分散读取
120 ByteBuffer[] bufs = {buf1, buf2};
121 channel1.read(bufs);
122
123 for (ByteBuffer byteBuffer : bufs) {
124 byteBuffer.flip();
125 }
126
127 System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
128 System.out.println("-----------------");
129 System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
130
131 //4. 聚集写入
132 RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
133 FileChannel channel2 = raf2.getChannel();
134
135 channel2.write(bufs);
136 }
137
138 //通道之间的数据传输(直接缓冲区)
139 @Test
140 public void test3() throws IOException{
141 FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
142 FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
143
144 // inChannel.transferTo(0, inChannel.size(), outChannel);
145 outChannel.transferFrom(inChannel, 0, inChannel.size());
146
147 inChannel.close();
148 outChannel.close();
149 }
150
151 //使用直接缓冲区完成文件的复制(内存映射文件)
152 @Test
153 public void test2() throws IOException{//2127-1902-1777
154 long start = System.currentTimeMillis();
155
156 FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
157 FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
158
159 //内存映射文件
160 MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
161 MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
162
163 //直接对缓冲区进行数据的读写操作
164 byte[] dst = new byte[inMappedBuf.limit()];
165 inMappedBuf.get(dst);
166 outMappedBuf.put(dst);
167
168 inChannel.close();
169 outChannel.close();
170
171 long end = System.currentTimeMillis();
172 System.out.println("耗费时间为:" + (end - start));
173 }
174
175 //利用通道完成文件的复制(非直接缓冲区)
176 @Test
177 public void test1(){//10874-10953
178 long start = System.currentTimeMillis();
179
180 FileInputStream fis = null;
181 FileOutputStream fos = null;
182 //①获取通道
183 FileChannel inChannel = null;
184 FileChannel outChannel = null;
185 try {
186 fis = new FileInputStream("d:/1.mkv");
187 fos = new FileOutputStream("d:/2.mkv");
188
189 inChannel = fis.getChannel();
190 outChannel = fos.getChannel();
191
192 //②分配指定大小的缓冲区
193 ByteBuffer buf = ByteBuffer.allocate(1024);
194
195 //③将通道中的数据存入缓冲区中
196 while(inChannel.read(buf) != -1){
197 buf.flip(); //切换读取数据的模式
198 //④将缓冲区中的数据写入通道中
199 outChannel.write(buf);
200 buf.clear(); //清空缓冲区
201 }
202 } catch (IOException e) {
203 e.printStackTrace();
204 } finally {
205 if(outChannel != null){
206 try {
207 outChannel.close();
208 } catch (IOException e) {
209 e.printStackTrace();
210 }
211 }
212
213 if(inChannel != null){
214 try {
215 inChannel.close();
216 } catch (IOException e) {
217 e.printStackTrace();
218 }
219 }
220
221 if(fos != null){
222 try {
223 fos.close();
224 } catch (IOException e) {
225 e.printStackTrace();
226 }
227 }
228
229 if(fis != null){
230 try {
231 fis.close();
232 } catch (IOException e) {
233 e.printStackTrace();
234 }
235 }
236 }
237
238 long end = System.currentTimeMillis();
239 System.out.println("耗费时间为:" + (end - start));
240
241 }
242
243 }
纸上学来终觉浅,觉知此事需躬行