Java -- 新IO -- 目录
20.1 Java 新IO简介
20.2 缓冲区与Buffer
例:演示缓冲区的操作流程
Class : IntBufferDemo01
20.2.2 深入缓冲区操作
20.2.3 创建子缓冲区
20.2.4 创建只读缓冲区
20.2.5 创建直接缓冲区
20.3 通道
20.3.1 FileChannel
例:使用输出通道输出内容
Class : FileChannelDemo01
package lime.pri.limeNio._20_3_1.channeldemo; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileChannelDemo01 { public static void main(String[] args) throws Exception { String info[] = {"lime","oracle","Lime","Oracle"}; File file = new File("F:/channels/out.txt"); FileOutputStream output = null; output = new FileOutputStream(file); FileChannel fout = null; fout = output.getChannel(); ByteBuffer buf = ByteBuffer.allocate(1024); for(int i = 0;i < info.length;i++){ buf.put(info[i].getBytes()); buf.put("\n".getBytes()); } buf.flip(); fout.write(buf); fout.close(); output.close(); } }
例:使用通道进行读写操作
Class :FileChannelDemo02
package lime.pri.limeNio._20_3_1.channeldemo; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileChannelDemo02 { public static void main(String[] args) throws Exception { File file1 = new File("F:/channels/note.txt"); File file2 = new File("F:/channels/outnote.txt"); FileInputStream input = null; FileOutputStream output = null; input = new FileInputStream(file1); output = new FileOutputStream(file2); FileChannel fin = null; FileChannel fout = null; fin = input.getChannel(); fout = output.getChannel(); ByteBuffer buf = ByteBuffer.allocate(1024); int temp = 0; while((temp = fin.read(buf)) != -1){ buf.flip(); fout.write(buf); buf.clear(); } fin.close(); fout.close(); input.close(); output.close(); } }
20.3.2 内存映射
内存映射可以把文件映射到内存中,这样文件内的数据就可以用内存读/写指令来访问,而不是用InputStream或OutputStream这样的I/O操作类,采用此种方式读取文件的速度是最快的。
提示:Java中访问文件内容的4中方法。
⊙ RandomAccessFile,随机读取数据,此种访问速度较慢。
⊙ FileInputStream,文件输入流,使用此种方式数度较慢。
⊙ 缓冲读取(例BufferedReader),使用此种方式访问速度较快。
⊙ 内存映射(MappedByteBuffer),使用此种方式读取速度最快。
例:内存映射
Class : FileChannelDemo03
package lime.pri.limeNio._20_3_2.channeldemo; import java.io.File; import java.io.FileInputStream; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class FileChannelDemo03 { public static void main(String[] args) throws Exception { File file = new File("F:/channels/mappedByteBuffer.txt"); FileInputStream input = null; input = new FileInputStream(file); FileChannel fin = null; fin = input.getChannel(); MappedByteBuffer mbb = null; mbb = fin.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); byte data[] = new byte[(int)file.length()]; int foot = 0; while(mbb.hasRemaining()){ data[foot++] = mbb.get(); } System.out.println(new String(data)); fin.close(); input.close(); } }
20.4 文件锁:FileLock
在Java新IO中提供了文件锁的功能,这样当一个线程将文件锁定之后,其他线程是无法操作此文件的。要想进行文件的锁定操作,则要使用FileLock类完成,此类的对象需要依靠FileChannel进行实例化操作。
⊙ 共享锁:允许多个线程进行文件的读取操作。
⊙ 独占锁:只允许一个线程进行文件的读/写操作。
例:将文件锁定
Class : FileLockDemo
package lime.pri.limeNio._20_4.channeldemo; import java.io.File; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class FileLockDemo { public static void main(String[] args) throws Exception { File file = new File("F:/channels/fileLock.txt"); FileOutputStream output = null; output = new FileOutputStream(file,true); FileChannel fout = null; fout = output.getChannel(); FileLock lock = fout.tryLock(); if(lock != null){ System.out.println(file.getName() + " 文件锁定300秒"); Thread.sleep(300000); lock.release(); System.out.println(file.getName() + " 文件解除锁定"); } fout.close(); output.close(); } }
20.5 字符集:Charset
例:取得Charset类的全部编码
Class : GetAllCharsetDemo
package lime.pri.limeNio._20_5.channeldemo; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Map; import java.util.SortedMap; public class GetAllCharsetDemo { public static void main(String[] args) { SortedMap<String, Charset> all = null; all = Charset.availableCharsets(); Iterator<Map.Entry<String,Charset>> iter = null; iter = all.entrySet().iterator(); while(iter.hasNext()){ Map.Entry<String, Charset> me = iter.next(); System.out.println(me.getKey() + " --> " + me.getValue()); } } }
例:编码-解码操作
Class : CharsetEnDeDemo
20.6 Selector
在新IO中Selector是一个极其重要的概念,在原来使用IO和Socket构造网络服务时,所有的网络服务经使用阻塞方式进行客户端的连接,而如果使用了新IO则可以构造一个非阻塞的的网络服务。
Selector类的常用方法:
⊙ public static Selector open() throws IOException : 打开一个选择器。
⊙ public abstract int select() throws IOException : 选择一组键,其相应的通道已为 I/O 操作准备就绪。
⊙ public abstract Set<SelectionKey> selectedKeys() : 返回此选择器的已选择键集。
在进行非阻塞网络开发时需要使用SelectableChannel类向Select类注册,而且在新IO中实现网络程序需要依靠ServerSocketChannel类与SocketChannel类,这两个类都是SelectableChannel的子类,SelectableChannel提供了注册Selector的方法和阻塞模式。
ServerSocketChannel类的常用方法:
⊙ public final SelectableChannel configureBlocking(boolean block) throws IOException : 调整此通道的阻塞模式。true:阻塞模式;false:非阻塞模式
⊙ public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException : 向给定的选择器注册此通道,返回一个选择键。
⊙ public static ServerSocketChannel open() throws IOException : 打开服务器的套接字通道。
⊙ public abstract ServerSocket socket() : 返回与此通道关联的服务器套接字。
在使用register()方法时需要指定一个选择器(Selector对象)以及Select域,Selector对象可以通过Selector中的open()方法取得,而Selector域则在SelectionKey类中定义。
4中Selector域:
⊙ public static final int OP_ACCEPT = 1 << 4: 相当于ServerSocket中的accpet()操作。
⊙ public static final int OP_CONNECT = 1 << 3 :连接操作。
⊙ public static final int OP_READ = 1 << 0 : 读操作。
⊙ public static final int OP_WRITE = 1 << 2 : 写操作。
如果要使用服务器想客户端发送信息,则需要通过SelectionKey类中提供的方法判断服务器的操作状态。而要想取得客户端的连接也需要使用SelectionKey类。
SelectionKey常用的方法:
⊙ public abstract SelectableChannel channel() : 返回为之创建此键的通道。
⊙ public final boolean isAcceptable() : 测试此键的通道是否已准备好接受新的套接字连接。
⊙ public final boolean isConnectable() : 测试此键的通道是否已完成其套接字连接操作。
⊙ public final boolean isReadable() : 测试此键的通道是否已准备好进行读取。
⊙ public final boolean isWritable() : 测试此键的通道是否已准备好进行写入。
例:取得时间的服务器
Class : DateServer
package lime.pri.limeNio._20_6.channeldemo.selector; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Date; import java.util.Iterator; import java.util.Set; public class DateServer { public static void main(String[] args) throws Exception { int ports[] = {8000,8001,8002,8003,8005,8006}; //定义一组连接端口号 Selector selector = Selector.open(); //打开一个选择器 for(int i = 0;i < ports.length;i++){ //构造服务器的启动信息 ServerSocketChannel initSer = null; //声明ServerSocketChannel initSer = ServerSocketChannel.open(); //打开服务器套接字通道 initSer.configureBlocking(false); //服务器配置为非阻塞 ServerSocket initSock = initSer.socket(); //检索此通道关联的服务器套接字 InetSocketAddress address = null; //表示监听地址 address = new InetSocketAddress(ports[i]); //实例化绑定地址 initSock.bind(address); //绑定地址 //注册选择器,相当于使用accept()方法接收 initSer.register(selector, SelectionKey.OP_ACCEPT); System.out.println("服务器运行,在 " + ports[i] + " 端口监听。"); } int keysAdd = 0; //接收一组SelectionKey while((keysAdd = selector.select()) > 0){ //选择一组键,相应的通道已为IO准备就绪 Set<SelectionKey> selectedkeys = selector.selectedKeys(); //取出全部生成的key Iterator<SelectionKey> iter = selectedkeys.iterator(); //实例化Iterator while(iter.hasNext()){ //迭代全部的key SelectionKey key = (SelectionKey)iter.next(); //取出每一个SelectionKey if(key.isAcceptable()){ //判断客户端是否已经连接上 ServerSocketChannel server = (ServerSocketChannel)key.channel(); //取得Channel SocketChannel client = server.accept(); // 接收新连接 client.configureBlocking(false); //设置成非阻塞状态 ByteBuffer outBuf = ByteBuffer.allocateDirect(1024); //开辟缓冲区 outBuf.put(("当前时间为:" + new Date()).getBytes()); //缓冲区设置内容 outBuf.flip(); //重置缓冲区 client.write(outBuf); //输出信息 client.close(); //关闭输出流 } } selectedkeys.clear(); //清除全部key } } }
Console : telnet
啦啦啦