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
packagelime.pri.limeNio._20_3_1.channeldemo;importjava.io.File;importjava.io.FileOutputStream;importjava.nio.ByteBuffer;importjava.nio.channels.FileChannel;public classFileChannelDemo01 {public static void main(String[] args) throwsException {
String info[]= {"lime","oracle","Lime","Oracle"};
File file= new File("F:/channels/out.txt");
FileOutputStream output= null;
output= newFileOutputStream(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
packagelime.pri.limeNio._20_3_1.channeldemo;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.FileOutputStream;importjava.nio.ByteBuffer;importjava.nio.channels.FileChannel;public classFileChannelDemo02 {public static void main(String[] args) throwsException {
File file1= new File("F:/channels/note.txt");
File file2= new File("F:/channels/outnote.txt");
FileInputStream input= null;
FileOutputStream output= null;
input= newFileInputStream(file1);
output= newFileOutputStream(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
packagelime.pri.limeNio._20_3_2.channeldemo;importjava.io.File;importjava.io.FileInputStream;importjava.nio.MappedByteBuffer;importjava.nio.channels.FileChannel;public classFileChannelDemo03 {public static void main(String[] args) throwsException {
File file= new File("F:/channels/mappedByteBuffer.txt");
FileInputStream input= null;
input= newFileInputStream(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(newString(data));
fin.close();
input.close();
}
}
20.4 文件锁:FileLock
在Java新IO中提供了文件锁的功能,这样当一个线程将文件锁定之后,其他线程是无法操作此文件的。要想进行文件的锁定操作,则要使用FileLock类完成,此类的对象需要依靠FileChannel进行实例化操作。
⊙ 共享锁:允许多个线程进行文件的读取操作。
⊙ 独占锁:只允许一个线程进行文件的读/写操作。
例:将文件锁定
Class : FileLockDemo
packagelime.pri.limeNio._20_4.channeldemo;importjava.io.File;importjava.io.FileOutputStream;importjava.nio.channels.FileChannel;importjava.nio.channels.FileLock;public classFileLockDemo {public static void main(String[] args) throwsException {
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
packagelime.pri.limeNio._20_5.channeldemo;importjava.nio.charset.Charset;importjava.util.Iterator;importjava.util.Map;importjava.util.SortedMap;public classGetAllCharsetDemo {public static voidmain(String[] args) {
SortedMap all = null;
all=Charset.availableCharsets();
Iterator> iter = null;
iter=all.entrySet().iterator();while(iter.hasNext()){
Map.Entry 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 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
packagelime.pri.limeNio._20_6.channeldemo.selector;importjava.net.InetSocketAddress;importjava.net.ServerSocket;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Date;importjava.util.Iterator;importjava.util.Set;public classDateServer {public static void main(String[] args) throwsException {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 selectedkeys = selector.selectedKeys(); //取出全部生成的key
Iterator 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
啦啦啦