目录
特殊RandomAccessFile
- DataInput接口中定义了基本数据类型的读操作,例如readInt/readDouble等
- DataOutput接口定义了基本数据类型的写操作,例如writeInt/writeDouble等
RandomAccessFile构造器
- r以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出 IOException
- rw以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件
- rws以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用 RandomAccessFile.close()关闭流的时候儿才真正的写到文件
- rwd与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据
RandomAccessFile写方法
RandomAccessFile raf=new RandomAccessFile("d:\\abc.data","rw");
raf.writeInt(1);
raf.writeChars("yanjun");
raf.writeDouble(1234.0/567);
raf.close();
RandomAccessFile读方法
RandomAccessFile raf=new RandomAccessFile("d:/abc.data","r");
int id=raf.readInt();
//String name=raf.readLine();
//byte[] bb=new byte[4];
//raf.readFully(bb);
//String name=new String(bb);
int len=raf.readInt();
StringBuilder sb=new StringBuilder();
for(int i=0;i<len;i++)
sb.append(raf.readChar());
String name=sb.toString();
double salary=raf.readDouble();
读取文件时如果到文件结尾是抛出异常EOFException,所以这里采用的是异常用于判断文件结束
记录指针的特殊方法
- long getFilePointer():返回文件记录指针的当前位置
- void seek(long pos):将文件记录指针定位到pos位置
- skipByte(int step); 相对当前位置跳过step个字节
常见方法
void close() 关闭操作
int read(byte[] b)将内容读取到一个byte数组之中
byte readByte()读取一个字节
int readInt()从文件中读取整型数据… readDouble()等8种简单类型
String readLine()读取一行数据
void writeBytes(String s)将一个字符串写入到文件之中,按字节的方式处理。writeChars
void writeInt(int v)将一个int型数据写入文件,长度为4位。…writeDouble等8种类型
NIO
NIO基础
- Chanel是对传统输入输出系统的模拟,通过map方法可以将一块数据映射到内存中
- Buffer本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读取的数据必须先放到Buffer中
NIO和多路复用的区别
IO模型
- 同步阻塞IO(Blocking IO):即传统的IO模型
- 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库
- 多路复用IO(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型
- 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。
java NIO就是采用多路复用IO模式
在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当 socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用
Buffer的使用
- 最常见的子类ByteBuffer可以在底层字节数组上进行get/set操作
- 其它基本数据类型都有对应的Buffer类:CharBuffer、 ShortBuffer、 IntBuffer、 LongBuffer、 FloatBuffer、 DoubleBuffer
静态方法static XxxBuffer allocate(int capacity)创建一个容量为capacity的XxxBuffer对象
- 容量capacity表示该Buffer的最大数据容量,创建后则不能改变
- 界限limit,位于limit后的数据既不可被读,也不可被写
- 位置position用于指明下一个可以被读出的或者写入缓冲区的位置索引
- 标记mark位置
写文件操作
String fileName = "nioFile";
try (FileOutputStream fos = new FileOutputStream(new File(fileName))) {
FileChannel channel = fos.getChannel();
ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode("你好你好..");
int length = 0;
while ((length = channel.write(byteBuffer)) != 0) {
System.out.println("写入长度:" + length);
}
} catch (IOException e) {
e.printStackTrace();
}
读文件操作
String fileName = "C:\IODemo\nioFile";
try (
FileInputStream fis = new FileInputStream(new File(fileName));
FileChannel channel = fis.getChannel()) {
int capacity = 100;
ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
int length = -1;
while ((length = channel.read(byteBuffer)) != -1) {
byteBuffer.clear();
byte[] array = byteBuffer.array();
System.out.write(array, 0, length);
System.out.println();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Buffer类的常用方法
- capacity():int返回Buffer的容量大小
- hasRemaining():boolean判断是否还有元素可以进行处理
- remaining():int返回当前位置和界限之间的元素个数
- position():int返回当前操作的位置
- mark():Buffer设置Buffer的标记位置,只能在0和position之间做标记
- reset():Buffer将位置position转到mark所在的位置
- rewind():Buffer将位置position设置到0,取消设置的mark
- put(obj)用于向Buffer中放入数据
- get()用于从Buffer中取出数据
// 创建对应的buffer对象,其中最大容积值为10
CharBuffer buffer = CharBuffer . allocate ( 10 );System . out . println ( buffer . capacity ()); // 获取 buffer 的容积值capacity=10,position=0,limit=10System . out . println ( buffer . position ());System . out . println ( buffer . limit ());buffer . put ( ' 中 ' );System . out . println ( buffer . capacity ()); // 10System . out . println ( buffer . position ()); // 1System . out . println ( buffer . limit ()); // 10String ss = " 中国人民解放军 " ;for ( int i = 0 ; i < ss . length (); i ++ )buffer . put ( ss . charAt ( i ));buffer . flip (); // 将 limit 设置到 position, 并且把 position 设置为 0 。相当于是将 buffer 中没有数据的存储位置封印起来,从而避免读取时读到不合法的数据System . out . println ( buffer . capacity ()); // 10System . out . println ( buffer . position ()); // 0System . out . println ( buffer . limit ()); //7char cc = buffer . get (); // 获取 position 对应的字符System . out . println ( cc ); // 中System . out . println ( buffer . capacity ()); // 10System . out . println ( buffer . position ()); // 1System . out . println ( buffer . limit ()); //7buffer . clear (); // 清空 buffer 中的数据,并重置 position 和 limitSystem . out . println ( buffer . capacity ()); // 10System . out . println ( buffer . position ()); // 0System . out . println ( buffer . limit ()); //10ss = " 中国人民解放军 " ;for ( int i = 0 ; i < ss . length (); i ++ )buffer . put ( ss . charAt ( i ));cc = buffer . get ( 2 ); // 按照下标位置获取对应的数据 , 并不会操作 positionSystem . out . println ( cc );System . out . println ( buffer . position ()); //7
Channel类的常用方法
- 所有Channel不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回对应的Channel
- 常用的是FileInputStream、FileOutputStream的getChannel()返回的FileChannel
File file = new File("T1.java");
FileChannel inChannel = new FileInputStream(file).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(256);
while (inChannel.read(buffer) != -1) {//多次读取数据的方式从文件中获取内容
buffer.flip(); //将没有数据的区域封印起来
Charset charset = Charset.forName("GBK");//使用GBK的字符集创建解码器
CharsetDecoder decoder = charset.newDecoder();
CharBuffer cb = decoder.decode(buffer);//使用解码器将ByteBuffer转换为CharBuffer
System.out.println(cb);
buffer.clear();//将position设置为0,为下一次读取数据做准备
}
异常:
map()方法将Channel对应的部分或全部数据映射成ByteBuffer
File f = new File("T1.java");
FileChannel in = new FileInputStream(f).getChannel();
FileChannel out = new FileOutputStream("a.txt").getChannel();
MappedByteBuffer buffer = in.map(FileChannel.MapMode.READ_ONLY, 0,
f.length());//参数1为执行映射时的模式,有只读、读写模式;参数2和3用于设置哪些数据执行映 射。可以将FileChannel中全部数据映射为ByteBuffer
out.write(buffer);
buffer.clear();
Charset charset = Charset.forName("GBK");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer cb = decoder.decode(buffer);
System.out.println(cb);
写入数据
File file = new File("data/a1.txt");
int len=(int)file.length();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel channel = raf.getChannel();// 返回的channel是只读还是读写,取决于
RandomAccessFile文件对象的打开模式
ByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, len);//buffer支持可读,将
文件中的所有内容映射到buffer中
channel.position(len); //移动指针到内容末尾
channel.write(buffer); //重新写出buffer中的内容,实际上就是将文件内容拷贝
selector
- FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承 SelectableChannel
- 多用于网络应用编程中
基本用法
Selector selector = Selector.open();
2. 注册Channel到Selector
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Channel必须是非阻塞的。
while(selector.select()>0){
Iterator < SelectionKey > it = selector . selectedKeys (). iterator ();...}
Chatset字符集
所有文件在底层都是二进制文件,字符文件是系统将底层的二进制序列转换为字符,这里会涉及编码Encoder和解码Decoder
- 将明文的字符序列转换为计算机理解的二进制序列称为编码
- 将二进制序列转换为明文字符串称为解码
Charset类
availableCharsets():SortedMap<String,Charset> 获取当前JDK所支持的所有字符集
*字符串别名
- GBK简体中文
- ISO-8859-1拉丁文
- UTF-8是8位UCS转换格式
Charset c=Charset.forName("GBK");
- newDecoder():CharsetDecoder获取该编码字符集对应的解码
- decode(ByteBuffer):CharBuffer方法可以将字节序列ByteBuffer转换为CharBuffer字符序列
- newEncoder():CharsetEncoder获取该编码字符集对应的编码
-
encode(CharBuffer):ByteBuffer 方法可以将字符序列CharBuffer转换为ByteBuffer字节序列
Charset c1 = Charset.forName("GBK");
CharsetEncoder encoder = c1.newEncoder();
CharsetDecoder decoder = c1.newDecoder();
CharBuffer cb = CharBuffer.allocate(8);
cb.put('孙');
cb.put('悟');
cb.put('空');
cb.flip();
ByteBuffer bb=encoder.encode(cb);//将CharBuffer转换为ByteBuffer
for(int i=0;i<6;i++) { System.out.println(bb.get(i)+" ");
}
System.out.println("====================");
System.out.println(decoder.decode(bb));//将byteBuffer转换为charBuffer