IO和NIO
来源:互联网 发布:网络覆盖 编辑:程序博客网 时间:2024/06/05 02:49
1、流的抽象基类:字节流:InputStream,OutputStream.字符流:Reader,Writer。
另外,RandomAccessFile类功能强大,实现对文件的随机位置读写,它没有继承以上四种抽象类。
2,常用的IO实现类
1)fileIO:读写文件,stream每次读一个字节8位,reader读一个char16位。有中文时用reader;节点流,内存---硬盘。
2)bufferedIO:内部有一个缓存区,可以等着读出的数据达到一定数量时集中度出,比如每次读一行BufferedRead的readLine方法,把一行当成字符串读出,该方法比较常用。该方法读文件时,到文件结尾会返回null,此时退出不阻塞。(line = br.readLine()) != null。 但封装socket的话,永远不会返回null,所以一直阻塞
3)转换流:InputStreamReader 是字节流通向字符流的桥梁;OutputStreamWriter 是字符流通向字节流的桥梁;
4)DataIO:DataInputStream和DataOutputStream可以输入输出各种基础数据类型,比如long,double。
5)ByteArrayIO:ByteArrayInputStream和OutputStream是在内存中都一个字节数组,通过刘往字节数组中读写,内存----内存。UDP的packet需要包装一个字节数组,通常在该流上面套DataIO进行UDP通信。
3、序列化:一个应用是将对象的状态保持在存储媒体中,以便可以在以后重新创建精确的副本,如画图软件存盘功能;另一个应用是通过值将对象从一个应用程序域发送到另一个应用程序域中,如socket网络传输。
序列化和反序列化:序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例。
4、实现序列化的几种方式:常见的做法有两种:一是把对象包装成JSON或者XML传输,二是采用java对象的序列化和反序列化。
5、
1)概念:Serializable为标记型接口,没有方法,实现Serializable接口的类,其对象可以转化为字节序列保存到硬盘上或者实现网络传输。ObjectInputStream和ObjectOnputStream中的WriteObject和ReadObject方法,这两个方法成对存在,只有写入了多少个对象,才可以读出多少个对象。
2)UID:通过serialVersionUID来验证类版本一致性的。如果没有明确指定serialVersionUID,某个类实现Serializable接口的时候会根据字段和特定的算法生成一个serialVersionUID,当类发生任何变化时系统会自动升级这个ID,所以反序列化的时候就会失败。抛出异常。所以强烈建议所有可序列化类都显式声明 serialVersionUID 值,并且声明为private,不让子类继承。
private static final long serialVersionUID = -5726374138698742258L;
UID的更新存在两种情况:当改变类的方法或者static和transient类型的属性时,不需要更新类的UID;当增加或者减少属性时,不需要更新,只不过新增添的属性在反序列化是生成为0或null;当改变原有属性的数据类型时,需要升级UID。
3)引用类型的属性:当一个类中含有一个引用变量的属性,则这个属性所在的类也必须可序列化。
4)序列化编号:所有保存在磁盘中的对象都有一个序列化编号,而不同于类编号UID。当序列化一个对象时,先检查该对象是否已经被序列化过,如果没有则进行序列化,如果已经序列化,则第二次只保存第一次时的编号。由于该算法,对于一个属性可变对象,程序只有在第一次使用writeObject方法时才会将对象转化为二进制,即使之后把该对象属性改变进行write,输出时会发现属性并不变。
JSON(JavaS
8,NIO
传统的IO底层实现都是通过对字节的处理,而且数据远没有数据时会阻塞该线程,这样效率不高。NIO将要读取的文件映射到内存中,这样读取文件就像读取内存的数据一样了,面向数据块。Channel和Buffer是核心对象,所需要读的数据是从通道读入缓冲区,等缓冲区满了读到内存;或从缓冲区写入到通道中的。
1)Channel:双向通道,可将指定文件部分或者全部映射到Buffer,内存只能通过Buffer获取和写入数据,不能直接访问Channel。Channel不应该直接创建,而应通过传统的流节点的getChannel获取,不同流节点产生的不同。
通道的实现类:
FileChannel 从文件中读写数据。
DatagramChannel 能通过UDP读写网络中的数据。
SocketChannel 能通过TCP读写网络中的数据。
ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
2)Buffer:
capacity
作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
position
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
使用Buffer的步骤:不管是读文件还是写文件,buffer都是先写后读
- 调用
clear()
- 写入数据到Buffer
- 调用
flip()
方法(写模式到读模式,当向buffer写入数据时,position会记录下写了多少数据,limit放到position,position放到开始处。在读模式下,可以读取之前写入到buffer的所有数据。) - 从Buffer中读取数据
- 调用
clear()
方法或者compact()
方法(写模式到读模式。两种方式:调用clear()或compact()方法。clear()方法position移到开始处,limit移到capacity处。compact()方法只会清除已经读过的数据。首先将未读的数据都被移到缓冲区的起始处,position移到数据末尾,这样新写入的数据将放到缓冲区未读数据的后面。)
01
RandomAccessFile aFile =
new
RandomAccessFile(
"data/nio-data.txt"
,
"rw"
);
02
FileChannel inChannel = aFile.getChannel();
03
04
//create buffer with capacity of 48 bytes
05
ByteBuffer buf = ByteBuffer.allocate(
48
);
08
while
(inChannel.read(buf)!= -
1
) { //
读文件内容写入buffer,返回值为
读取的字节数,如果该通道已到达文件的末尾,则返回 -109
10
buf.flip();
//make buffer ready for read
11
12
while
(buf.hasRemaining()){
13
System.out.print((
char
) buf.get());
//把刚才写入buffer的内容读出
14
}
15
16
buf.clear();
//make buffer ready for writing
17
bytesRead = inChannel.read(buf);
18
}
19
aFile.close();
1
RandomAccessFile aFile =
new
RandomAccessFile(
"data/nio-data.txt"
,
"rw"
);
2
FileChannel outChannel = aFile.getChannel();
01
String newData = "New String to write to file..." + System.currentTimeMillis();
02
03
ByteBuffer buf = ByteBuffer.allocate(48);
04
buf.clear();05
buf.put(newData.getBytes());
06
07
buf.flip();
08
09
while(buf.hasRemaining()) {
10
channel.write(buf);
11
}
实例:打开一个Selector,注册一个通道注册到这个Selector上(通道的初始化过程略去),然后通过Selector监听Channel的当前状态(接受,连接,读,写)是否就绪。这样提高了NIO使用效率。
01
Selector selector = Selector.open();
02
channel.configureBlocking(
false
);//将通道设为非阻塞
03
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
//注册通道,并写上你感兴趣的状态
,当通道达到这个状态,通道就绪。04
while
(
true
) {
05
int
readyChannels = selector.select();//返回已就绪通道的数目
06
if
(readyChannels ==
0
)
continue
;
/*当readyChannels不为0时,就可通过以下操作来访问那些已经达到感兴趣状态的通道了*/07
Set selectedKeys = selector.selectedKeys();
08
Iterator keyIterator = selectedKeys.iterator();
09
while
(keyIterator.hasNext()) {
10
SelectionKey selectionKey = keyIterator.next();
11
if
(key.isAcceptable()) {
12
// a connection was accepted by a ServerSocketChannel.
13
}
else
if
(key.isConnectable()) {
14
// a connection was established with a remote server.
15
}
else
if
(key.isReadable()) {
16
// a channel is ready for reading
17
}
else
if
(key.isWritable()) {
18
// a channel is ready for writing
19
}
20
keyIterator.remove();
21
}
22
}
下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。
IO NIO面向流 面向缓冲阻塞IO 非阻塞IO无 选择器
1
BufferedReader reader =
new
BufferedReader(
new
InputStreamReader(input));
2
3
String nameLine = reader.readLine();
4
String ageLine = reader.readLine();
readline()会阻塞直到整行读完。这样必须使进程开辟另外的线程,线程转换耗时,所以使得进程效率降低。
NIO的非阻塞模型,方法调用完成后立刻返回,不管是否接收到数据。
假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。可以使用bufferFull()方法使得必须读满了buffer在对数据进行处理。
- java io和nio
- java IO和NIO
- IO和NIO
- IO和NIO
- Java IO和NIO
- NIO和IO
- Java NIO和IO
- 关于io和nio
- NIO和IO(一)
- IO 和 NIO
- Java NIO和IO
- java NIO和IO
- IO和NIO
- java nio和io
- nio.2 nio 和传统io
- NIO理解和io区别
- IO和NIO的区别
- Java NIO 和IO 对比
- 数据库&MYSQL&JDBC
- 华为上机题目
- 单元测试与Junit
- vim个人配置记录
- Hadoop基础学习
- IO和NIO
- R类与资源文件说明
- log4j与日志系统
- JavaScript基础学习
- 计算机网络总结
- Linux and Shell
- 大型网站构架
- C/C++
- 论文核心思想总结