柚子快報邀請碼778899分享:JavaIO、NIO和序列化
JavaIO與NIO
阻塞IO模型非阻塞IO模型多路復(fù)用IO模型事件輪詢機制的偽源碼
信號驅(qū)動IO模型異步IO模型JAVA IO包JAVA NIONIO的緩沖區(qū)NIO的非阻塞完全寫入ChannelBufferSelector實現(xiàn)多連接的客戶端代碼示例
JAVA序列化(創(chuàng)建客服用的Java對象)保持(持久化)對象及其狀態(tài)到內(nèi)存或者磁盤序列化對象以字節(jié)數(shù)組保存(靜態(tài)成員不保存)序列化用戶遠程和對象傳輸序列化IDTransient關(guān)鍵字阻止該變量被序列化到文件中
阻塞IO模型
最傳統(tǒng)的一種IO 模型,即在讀寫數(shù)據(jù)過程中會發(fā)生阻塞現(xiàn)象。當(dāng)用戶線程發(fā)出IO請求之后,內(nèi)核會去查看數(shù)據(jù)是否就緒,,如果沒有就緒就會等待數(shù)據(jù)就緒,而用戶線程就會處于阻塞狀態(tài),用戶線程交出CPU。當(dāng)數(shù)據(jù)就緒之后,內(nèi)核會將數(shù)據(jù)拷貝到用戶線程,并返回結(jié)果給用戶線程,用戶線程才會接觸block狀態(tài)。 典型的阻塞IO模型的例子為:
data=socket.read();
如果數(shù)據(jù)沒有就緒,就會一直阻塞在read方法
非阻塞IO模型
又叫BIO,當(dāng)用戶線程發(fā)起一個read操作后,并不需要等待,而是馬上就得到了一個結(jié)果。如果結(jié)果是一個error,他就知道數(shù)據(jù)還沒有準(zhǔn)備好,于是它在此發(fā)送read操作。一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了,并且再次收到了用戶線程的請求,那么它馬上就將數(shù)據(jù)拷貝到了用戶線程,然后返回。所以事實上,在非阻塞IO模型中,用戶線程需要不斷地詢問內(nèi)核數(shù)據(jù)是否就緒,也就是說非阻塞IO不會交出CPU,而會一直占用CPU。 典型的非阻塞IO模型一般如下:
while(true){
data=socket.read();
if(data!=error){
//在這里處理數(shù)據(jù)
break;
}
}
但是對于非阻塞IO就有一個非常嚴(yán)重的問題,在while循環(huán)中需要不斷地去詢問內(nèi)核數(shù)據(jù)是否就緒,這樣會導(dǎo)致CPU占用率非常高,因此一般情況下很少使用while循環(huán)這種方式來讀取數(shù)據(jù)。
多路復(fù)用IO模型
多路復(fù)用IO模型是目前使用的比較多的模型。JavaNIO實際上就是多路復(fù)用IO。在多路復(fù)用IO模型中,會有一個線程不斷去輪詢多個socket狀態(tài),只有當(dāng)socket真正有讀寫事件時,才真正調(diào)用實際的IO讀寫操作。因為在多路復(fù)用IO模型中,只需要使用一個線程就可以管理多個socket,系統(tǒng)不需要建立新的進程或線程,也不必維護這些進程和線程,并且只有在真正有socket讀寫時間在進行時,才會使用IO資源,所以它大大減少了資源占用。在JavaNIO中,是通過selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那里,因此這種方式會導(dǎo)致用戶線程的阻塞。多路復(fù)用IO模式,通過一個線程就可以管理多個socket,只有當(dāng)socket真正有讀寫事件發(fā)生才會占用資源來進行實際的讀寫操作。因此多路復(fù)用IO比較適用連接數(shù)比較多的情況。 另外多路復(fù)用IO比非阻塞IO效率高的原因是因為在非阻塞IO中,不斷地詢問socket狀態(tài)時是通過用戶線程去進行的,而在多路復(fù)用IO中,輪詢每個socket是在內(nèi)核中進行的,這個效率要比用戶線程高得多。 不過要注意的是,多路復(fù)用IO模型師通過輪詢的方式來檢測是否有事件到達,并且對到達的事件注意進行響應(yīng),因此對于多路復(fù)用IO模型來說,一旦事件響應(yīng)體很大,那么就會導(dǎo)致后續(xù)的事件遲遲得不到處理,并且會影響新的事件輪詢
事件輪詢機制的偽源碼
下面是Java NIO事件輪詢機制的偽源碼,幫助理解NIO的工作原理:
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import java.util.Set;
public class NIOEventLoop {
private Selector selector;
public void run() {
while (true) {
// 等待事件
int readyChannels = selector.select();
if (readyChannels == 0) {
continue;
}
// 處理事件
Set
for (SelectionKey key : selectedKeys) {
if (key.isValid() && key.isReadable()) {
// 處理讀事件
} else if (key.isValid() && key.isWritable()) {
// 處理寫事件
}
}
selectedKeys.clear();
}
}
}
在這個示例中,我們首先創(chuàng)建一個選擇器 Selector 對象。然后,在 run() 方法中,我們不斷循環(huán)等待事件:調(diào)用 select() 方法等待事件的發(fā)生,如果沒有事件發(fā)生則繼續(xù)等待。如果有事件發(fā)生,我們就處理它們:獲取所發(fā)生事件的 SelectionKey 集合,并根據(jù) isReadable() 和 isWritable() 方法判斷事件類型。最后,我們清空已處理的 SelectionKey 集合,然后繼續(xù)等待事件的發(fā)生。
這個示例演示了Java NIO的事件輪詢機制的基本原理:使用選擇器 Selector 對象等待事件的發(fā)生,并處理所發(fā)生的事件。在實際應(yīng)用中,我們通常會使用多個線程來處理不同類型的事件,以提高性能和響應(yīng)速度。
信號驅(qū)動IO模型
在信號驅(qū)動IO模型中,當(dāng)用戶線程發(fā)起一個IO請求事件,會給對應(yīng)的socket注冊一個信號函數(shù),然后用戶線程會繼續(xù)執(zhí)行,當(dāng)內(nèi)核數(shù)據(jù)就緒時就會發(fā)送一個信號給用戶線程,用戶線程接收到信號之后,便在型號函數(shù)中調(diào)用IO讀寫操作來進行實際的IO請求操作。
在Java中,可以利用NIO框架來實現(xiàn)信號驅(qū)動IO。下面是一個簡單的基于Java NIO實現(xiàn)的例子:
import java.nio.channels.Pipe;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
public class SignalDrivenIOExample {
public static void main(String[] args) throws Exception {
// 創(chuàng)建管道
Pipe pipe = Pipe.open();
// 創(chuàng)建選擇器
Selector selector = SelectorProvider.provider().openSelector();
// 注冊管道讀事件到選擇器中
pipe.source().configureBlocking(false);
pipe.source().register(selector, SelectionKey.OP_READ);
// 啟動信號
Signal.handle(new Signal("IO"), new SignalHandler() {
@Override
public void handle(Signal signal) {
// 處理IO事件
try {
selector.selectNow();
} catch (IOException e) {
e.printStackTrace();
}
}
});
// 無限循環(huán)等待信號驅(qū)動IO事件
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
break;
}
}
}
}
此示例使用Java NIO框架創(chuàng)建了一個管道,將管道的讀事件注冊到選擇器中。然后,我們使用Signal類啟動一個名為 “IO” 的信號,并為其注冊一個信號處理程序Handler。當(dāng)接收到 “IO” 信號時,處理程序會調(diào)用選擇器的 selectNow() 方法來檢查是否有管道讀事件發(fā)生。最后,我們通過一個無限循環(huán)等待信號驅(qū)動IO事件的發(fā)生。這樣,我們就成功地使用信號驅(qū)動IO實現(xiàn)了一個簡單的輸入監(jiān)聽程序。
異步IO模型
異步IO模型是最理想的IO模型,在異步IO模型中,當(dāng)用戶線程發(fā)起read操作之后,立刻就可以開始去做其他的事。而另一方面,從內(nèi)核的角度,當(dāng)它受到一個asynchronous read之后,它會立刻返回,說明read請求已經(jīng)成功發(fā)起了,因此不會對用戶線程產(chǎn)生任何的block。然后,內(nèi)核會等待數(shù)據(jù)準(zhǔn)備完成,然后將數(shù)據(jù)拷貝到用戶線程,當(dāng)這一切都完成之后,內(nèi)核會給用戶線程發(fā)送一個信號,告訴它read操作完成了。也就是說用戶線程完全不需要實際的整個IO操作是如何進行的,只需要先發(fā)起一個請求,當(dāng)接收到內(nèi)核返回的成功信號時表示IO操作已經(jīng)完成,可以直接使用數(shù)據(jù)。 也就是說在異步IO模型中,IO操作的兩個階段都不會阻塞用戶線程,這兩個階段都是由內(nèi)核自動完成,然后發(fā)送一個信號告知用戶線程已經(jīng)操作完成。用戶線程中不需要再次調(diào)用IO函數(shù)進行具體的讀寫。這點適合信號驅(qū)動模型有所不同的,在信號驅(qū)動模型中,當(dāng)用戶線程接收到信號表示數(shù)據(jù)已經(jīng)就緒,然后需要用戶線程調(diào)用IO函數(shù)進行時機的讀寫操作;而在異步IO模型中,收到信號表示IO操作已經(jīng)完成,不需要再在用戶線程中調(diào)用IO函數(shù)進行實際的讀寫操作。 注意: 異步IO是需要操作系統(tǒng)的底層支持的,在java7中提供Asynchronous IO
Java NIO提供了異步IO模型的支持,它通過使用非阻塞IO和事件輪詢機制來實現(xiàn)異步IO。
下面是一個基于Java NIO實現(xiàn)的異步IO模型的偽代碼:
import java.nio.*;
import java.nio.channels.*;
public class AsynchronousIO {
private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
public void read(SelectableChannel channel) {
channel.read(readBuffer, this, new CompletionHandler
public void completed(Integer bytesRead, AsynchronousIO aio) {
if (bytesRead == -1) {
// 讀取結(jié)束
return;
}
// 處理讀取到的數(shù)據(jù)
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
System.out.print(new String(bytes));
readBuffer.clear();
// 繼續(xù)讀取
channel.read(readBuffer, aio, this);
}
public void failed(Throwable exc, AsynchronousIO aio) {
// 處理讀取失敗
}
});
}
public void write(SelectableChannel channel, byte[] data) {
writeBuffer.put(data);
writeBuffer.flip();
channel.write(writeBuffer, this, new CompletionHandler
public void completed(Integer bytesWritten, AsynchronousIO aio) {
// 處理寫入成功
writeBuffer.compact();
if (writeBuffer.hasRemaining()) {
// 如果還有數(shù)據(jù),繼續(xù)寫
channel.write(writeBuffer, aio, this);
}
}
public void failed(Throwable exc, AsynchronousIO aio) {
// 處理寫入失敗
}
});
}
}
在這個示例中,我們使用 SelectableChannel 對象來讀取和寫入數(shù)據(jù)。read() 方法通過調(diào)用 channel.read() 來實現(xiàn)異步讀取,當(dāng)事件完成時,CompletionHandler 中的 completed() 將會被回調(diào),我們在其中處理讀取到的數(shù)據(jù)并繼續(xù)讀取。
同樣的,write() 方法通過調(diào)用 channel.write() 來實現(xiàn)異步寫入,當(dāng)事件完成時,CompletionHandler 中的 completed() 將會被回調(diào),我們在其中處理寫入成功并繼續(xù)寫入。
在實際應(yīng)用中,我們通常會使用多個線程來處理不同類型的異步IO事件,以提高性能和響應(yīng)速度。
JAVA IO包
Java IO包包括以下類和接口:
1. InputStream 和 OutputStream:用于讀取和寫入字節(jié)流的類和接口 2. Reader 和 Writer:用于讀取和寫入字符流的類和接口。 3. File:用于操作文件和目錄的類。 4. RandomAccessFile:用于隨機訪問文件的類。 5. FileDescriptor:使用底層文件描述符進行文件訪問的類。 6. FileInputStream 和 FileOutputStream:用于讀取和寫入文件的字節(jié)流的類。 7. FileReader 和 FileWriter:用于讀取和寫入文件的字符流的類。 8. BufferedInputStream 和 BufferedOutputStream:用于處理緩沖區(qū)的字節(jié)流類。 9. BufferedReader 和 BufferedWriter:用于處理緩沖區(qū)的字符流類。 10. DataInputStream 和 DataOutputStream:用于讀取和寫入基本數(shù)據(jù)類型的字節(jié)流類。 11. InputStreamReader 和 OutputStreamWriter:將字節(jié)流轉(zhuǎn)換為字符流的類。 12. ObjectInputStream 和 ObjectOutputStream:用于讀取和寫入 Java 對象的類。 13. PipedInputStream 和 PipedOutputStream:用于在不同線程之間進行通信的字節(jié)流類。 14. PushbackInputStream:允許將字節(jié)推回輸入流的類。 15. SequenceInputStream:允許將多個輸入流合并為一個流的類。
這些類和接口提供了豐富的功能,使得 Java IO 包成為 Java 編程中不可或缺的一部分。
JAVA NIO
NIO主要有三大核心部分:Channel(通道)、Buffer(緩沖區(qū))、Selector(選擇區(qū))。 傳統(tǒng)IO基于字節(jié)流和字符流進行操作,而NIO基于Channel和Buffer(緩沖區(qū))進行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫入到通道中。Selector(選擇區(qū))用于監(jiān)聽多個通道的事件(比如:連接打開,數(shù)據(jù)到達) 因此,單個線程可以監(jiān)聽多個數(shù)據(jù)通道。 NIO和傳統(tǒng)IO之間第一個最大的區(qū)別就是,IO是面向流的,NIO是面向緩沖區(qū)的。
NIO的緩沖區(qū)
JavaIO面向流意味著每次從流中讀取一個或多個字節(jié),直到讀取所有字節(jié),他們沒有被緩存在任何地方。此外,他不能前后移動流中的數(shù)據(jù)。如果需要前后移動從流中讀取的數(shù)據(jù),需要先將它緩存到一個緩沖區(qū)。 NIO的緩沖導(dǎo)向方法不同。數(shù)據(jù)讀取到一個它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區(qū)中包含所有你需要除磷的數(shù)據(jù)。而且,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時,不需要覆蓋緩沖區(qū)中尚未處理的數(shù)據(jù)。
NIO的非阻塞
IO的各種流是阻塞的。這意味著,當(dāng)一個線程調(diào)用read()或write()時,該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。
NIO的非阻塞模式,使一個線程從某通道發(fā)送請求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時,就什么都不會獲取,而不是保持線程阻塞。所以直至數(shù)據(jù)變得可以讀取之前,該線程繼續(xù)做其他的事情。
非阻塞寫也是如此,一個線程請求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情?,F(xiàn)成通常將非阻塞IO的空閑時間用于在其他通道上執(zhí)行IO操作,索引一個單獨的線程現(xiàn)在可以管理多個輸入和輸出通道
完全寫入
**完全寫入指的是將線程請求寫入的數(shù)據(jù)全部寫入到通道中,直到通道緩沖區(qū)已經(jīng)被填滿,或者所有數(shù)據(jù)都已經(jīng)被寫入到通道中。**在非阻塞寫中,即使緩沖區(qū)已經(jīng)被填滿,或者只是寫入了一部分數(shù)據(jù),寫入操作也會持續(xù)進行,同時將剩余的待寫入數(shù)據(jù)返回給調(diào)用方,以便該調(diào)用方可以進行其他操作。這與阻塞寫相比,阻塞寫將等待所有數(shù)據(jù)完全寫入后才會返回。因此,非阻塞寫可以提高程序的效率,避免了線程阻塞和資源浪費。
Channel
**Channel,國內(nèi)大多翻譯成通道。Channel和IO中的Stream(流)是差不多一個等級的。**只不過Stream是單向的,譬如:InputStream和OutputStream,而Channel是雙向的,既可以用來進行讀操作,也可以用來進行寫操作。 NIO中的Channel的主要實現(xiàn)有:
FileChannelDatagramChannelSocketChannelServerSockertChannel
不難看出,以上分別對應(yīng)文件IO、UDP、TCP(Server和Client)
Buffer
Buffer(緩沖區(qū)),實際上就是一個容器,是一個連接數(shù)組。Channel提供從文件、網(wǎng)絡(luò)讀取數(shù)據(jù)的渠道,但是讀取或?qū)懭氲臄?shù)據(jù)都必須經(jīng)過Buffer。 客戶端發(fā)送數(shù)據(jù)時,必須先將數(shù)據(jù)存入Buffer中,然后將Buffer中的內(nèi)容寫入通道。服務(wù)端這邊接受數(shù)據(jù)必須通過Channel將數(shù)據(jù)讀入到Buffer中,然后再從Buffer中取出數(shù)據(jù)來處理。 在NIO中,Buffer是一個頂層父類,他是一個抽象類,常用的Buffer有ByteBuffer、IntBuffer、CharBuffer、LongBuffer、DoubleBuffer、FloatBuffer、ShortBuffer
Selector
Selector是NIO的核心類,Selector能夠檢測多個注冊的通道上是否有事件發(fā)生,如果有事件發(fā)生,便獲取事件然后針對每個事件進行相應(yīng)的響應(yīng)處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正的有讀寫事件發(fā)生時,才會調(diào)用函數(shù)來進行讀寫,就大大減少了系統(tǒng)開銷,并且不必為每個連接都創(chuàng)建一個線程,不用去維護多個線程,并且避免了多線程之間的上下文切換導(dǎo)致的開銷。
實現(xiàn)多連接的客戶端代碼示例
這里提供一個基于Java NIO實現(xiàn)多連接的客戶端代碼示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
public class MultiNIOClient {
// 客戶端自動生成的id
private static final String CLIENT_ID = "client" + System.currentTimeMillis();
public static void main(String[] args) throws IOException {
// 創(chuàng)建Selector選擇器
Selector selector = Selector.open();
// 創(chuàng)建多個SocketChannel連接遠程服務(wù)器
SocketChannel socketChannel1 = SocketChannel.open();
SocketChannel socketChannel2 = SocketChannel.open();
socketChannel1.connect(new InetSocketAddress("localhost", 8888));
socketChannel2.connect(new InetSocketAddress("localhost", 8888));
socketChannel1.configureBlocking(false);
socketChannel2.configureBlocking(false);
// 注冊到選擇器中
socketChannel1.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, CLIENT_ID + "-001");
socketChannel2.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, CLIENT_ID + "-002");
// 開始循環(huán)處理請求
while (true) {
// 選擇已經(jīng)準(zhǔn)備就緒的通道
int readyChannels = selector.select();
if (readyChannels > 0) {
// 遍歷已經(jīng)準(zhǔn)備就緒的通道,處理請求
Set
Iterator
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isReadable()) {
// 讀取數(shù)據(jù)
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes, "UTF-8");
System.out.println("Message received from " + selectionKey.attachment() + ": " + message);
} else if (bytesRead < 0) {
System.out.println(selectionKey.attachment() + " disconnected.");
channel.close();
}
} else if (selectionKey.isWritable()) {
// 發(fā)送數(shù)據(jù)
SocketChannel channel = (SocketChannel) selectionKey.channel();
Scanner scanner = new Scanner(System.in);
System.out.println("Enter message: ");
String message = scanner.nextLine();
if (message.trim().length() > 0) {
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
channel.write(buffer);
System.out.println("Message sent from " + selectionKey.attachment() + ": " + message);
}
}
iterator.remove();
}
}
}
}
}
在該示例中,創(chuàng)建了兩個連接并注冊到選擇器中進行監(jiān)聽,分別使用字符串id作為SelectionKey的attachment屬性。通過控制臺輸入消息并往對應(yīng)的SocketChannel發(fā)送消息,同時可以接收來自其他客戶端的消息。
簡單的客戶端到服務(wù)器端的數(shù)據(jù)傳輸示例
客戶端代碼:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NIOClient {
public static void main(String[] args) throws IOException {
// 創(chuàng)建SocketChannel通道并連接服務(wù)器
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8888));
// 發(fā)送數(shù)據(jù)
String message = "Hello, Server!";
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(buffer);
System.out.println("Message sent to the server: " + message);
// 接收數(shù)據(jù)
buffer.clear();
socketChannel.read(buffer);
buffer.flip();
String response = new String(buffer.array(), 0, buffer.limit());
System.out.println("Message received from the server: " + response);
// 關(guān)閉通道
socketChannel.close();
}
}
服務(wù)器端代碼:
import java.io.IOException;
import java.net.InetSocketAddress;
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.Iterator;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) throws IOException {
// 創(chuàng)建Selector選擇器
Selector selector = Selector.open();
// 創(chuàng)建ServerSocketChannel通道并綁定端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8888));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 開始循環(huán)處理請求
while (true) {
// 選擇已經(jīng)準(zhǔn)備就緒的通道
int readyChannels = selector.select();
// 遍歷已經(jīng)準(zhǔn)備就緒的通道,處理請求
if (readyChannels > 0) {
Set
Iterator
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
// 如果是接收請求事件
ServerSocketChannel serverChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = serverChannel.accept();
// 將通道設(shè)置為非阻塞模式,并注冊到選擇器中
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Connection Accepted: " + socketChannel.getLocalAddress() + "\n");
} else if (selectionKey.isReadable()) {
// 如果是讀取數(shù)據(jù)事件
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes, "UTF-8");
System.out.println("Message received from client: " + message);
// 將響應(yīng)數(shù)據(jù)返回給客戶端
ByteBuffer responseBuffer = ByteBuffer.wrap(("Server Response: " + message).getBytes());
socketChannel.write(responseBuffer);
} else if (bytesRead < 0) {
// 客戶端關(guān)閉了連接
socketChannel.close();
}
}
iterator.remove();
}
}
}
}
}
在客戶端向服務(wù)端發(fā)送數(shù)據(jù)后,服務(wù)端會接收到數(shù)據(jù)并返回響應(yīng),客戶端再接收到響應(yīng)數(shù)據(jù)。
JAVA序列化(創(chuàng)建客服用的Java對象)
保持(持久化)對象及其狀態(tài)到內(nèi)存或者磁盤
Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,只有當(dāng)JVM處于運行時,這些對象才可能存在,也就是說這些對象的生命周期不會比JVM的聲明周期更長。但在現(xiàn)實應(yīng)用中,就可能要求在JVM停止運行之后能夠保存 (持久化)指定的對象,并在將來重新讀取被保存的對象。Java對象序列化就能幫助我們實現(xiàn)該功能。
序列化對象以字節(jié)數(shù)組保存(靜態(tài)成員不保存)
使用java對象序列化,在保存對象時,會把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對象。必須注意的是,對象序列化保存的是對象的“狀態(tài)”,即他的成員變量。由此可知,對象序列化不會關(guān)注類中的靜態(tài)變量
序列化用戶遠程和對象傳輸
除了在持久化對象時會用到對象序列化之外,當(dāng)使用RMI(遠程方法調(diào)用),或在網(wǎng)絡(luò)中傳遞對象時,都會用到對象序列化。java序列化API為處理對象序列化提供了一個標(biāo)準(zhǔn)機制,該API簡單易用。
Serializable實現(xiàn)序列化
在java中,只要一個類實現(xiàn)了java.io.Serizliable接口,那么他就可以被序列化。
ObjectOutputStream和ObjectInputStream對對象進行序列化及反序列化writeObject和readObject自定義序列化策略 在類中增加writeObject和readObject方法自定義序列化策略
序列化ID
虛擬機是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個非常重要的一點是兩個類序列化ID是否一致(就是private static final long serialVersionUID) 序列化并不保存靜態(tài)變量 序列化字父類說明 要想將父類對象也序列化,就需要讓父類也實現(xiàn)Serializable接口
Transient關(guān)鍵字阻止該變量被序列化到文件中
在變量聲明前加上Transient關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient變量的值被設(shè)為初始值,如int型的是0,對象型的是null。服務(wù)器端給客戶端發(fā)送序列化對象數(shù)據(jù),對象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對該密碼字段在序列化時,進行加密,而客戶端如果擁有解密的密鑰,只有在客戶端進行反序列時,才可以對密碼進行讀取,這樣可以一定程度保證序列化對象的數(shù)據(jù)安全。
柚子快報邀請碼778899分享:JavaIO、NIO和序列化
推薦閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。