主頁 > 後端開發 > Java NIO

Java NIO

2020-10-19 10:15:53 後端開發

1. 概述

Java NIO (New IO) 由以下三個核心組件組成:

  • Channels (通道)
  • Buffers (緩沖區)
  • Selectors (選擇器)

通常,在NIO中,IO從一個Channel開始,資料可以從Channel中讀到Buffer,也可以從Buffer中寫道Channel,而Selector允許單個執行緒處理多個Channel,

2. Channel

Channels和Streams很像,但還是有一些不同的:

  • 在一個Channels上既可以讀又可以寫,而Streams只能讀或寫;
  • Channels可以異步讀寫;
  • Channels總是從Buffer中讀,或寫到Buffer中;

如上所述,資料從通道讀取到緩沖區中,從緩沖區寫入通道中,如下圖所示:

Channel最重要的四種實作:

  • FileChannel : 從檔案中讀資料
  • DatagramChannel : 可以通過UDP在網路上讀寫資料
  • SocketChannel : 可以通過TCP在網路上讀寫資料
  • ServerSocketChannel : 監聽TCP連接

3. Buffer

在Java NIO中Buffer用來和Channel互動,資料從channel讀到buffer中,從buffer寫到channel中,

Buffer本質上是一個記憶體塊,可以在其中寫入資料,然后在以后再次讀取,該記憶體塊包裝在NIO Buffer物件中,該物件提供了一組方法,可以更輕松地使用該記憶體塊,

使用Buffer讀寫資料,典型地分為四步:

  1. 寫資料到Buffer
  2. 呼叫buffer.flip()
  3. 從Buffer中讀資料
  4. 呼叫 buffer.clear() 或者 buffer.compact()

當你向一個buffer中寫資料時,buffer會跟蹤你已經寫了多少資料了,一旦你需要讀取資料,你需要呼叫flip()方法將buffer從寫入模式切換為讀取模式,在讀取模式下,buffer使你可以讀取寫入緩沖區的所有資料,

一旦你已經讀取了所有資料,你需要清除buffer,以使得它可以再次被寫入資料,有兩個方法可以達到這個效果:clear()或者compact(),clear()方法會清理整個buffer,compact()方法只清理你已經讀過的資料,任何未讀的資料都將移至緩沖區的開頭,并且將來寫入buffer的資料在現在未讀的資料之后,

 1 RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
 2 FileChannel inChannel = aFile.getChannel();
 3 
 4 // create buffer with capacity of 48 bytes
 5 ByteBuffer buf = ByteBuffer.allocate(48);
 6 
 7 int bytesRead = inChannel.read(buf); //read into buffer.
 8 while (bytesRead != -1) {
 9 
10   buf.flip();  //make buffer ready for read
11 
12   while(buf.hasRemaining()){
13       System.out.print((char) buf.get()); // read 1 byte at a time
14   }
15 
16   buf.clear(); //make buffer ready for writing
17   bytesRead = inChannel.read(buf);
18 }
19 aFile.close();

Buffer有三個屬性:

  • capacity
  • position
  • limit 

position和limit的含義取決于Buffer是處于讀模式還是寫模式,無論哪種模式下capacity的含義總是不變的,

Capacity 

作為一個記憶體塊,Buffer有一個固定大小,也被稱作“capacity”,一旦Buffer滿了,就需要清空它(讀取資料或清除資料),然后才能將更多資料寫入其中,

Position 

當你將資料寫入Buffer時,你需要在一個明確的位置寫入,初始位置是0,當有資料被寫入后,position會向前移動以指向下一個可寫入的位置,position的最大值是capacity-1,

當你從Buffer中讀取資料時,也需要從一個給定的位置處開始讀取,當你將Buffer從寫模式切換為讀模式時,position會被重置為0,

Limit

在寫模式下,limit表示你可以寫多少資料到Buffer,在寫模式下,limit的值等于capacity,

在讀模式下,limit表示你可以從Buffer中讀多少資料,因此,當從寫模式切換為讀模式時,limit被設定為在寫模式是的position,換言之,寫了多少就能讀多少,

3.1. 分配一個緩沖區

為了獲得一個Buffer,首先必須先給它分配空間,每種型別的Buffer都有一個allocate()方法來做這件事, 

1 // 分配位元組緩沖區,容量為48位元組
2 ByteBuffer buf = ByteBuffer.allocate(48);
3 // 分配字符緩沖區,容量為1024個字符
4 CharBuffer buf = CharBuffer.allocate(1024);

3.2. 寫資料到Buffer

有兩種方式向Buffer中寫資料:

  • 從Channel中向Buffer寫資料
  • 從Buffer本身向自己寫資料,通過put()方法 
1 //read into buffer
2 int bytesRead = inChannel.read(buf); 
3 
4 buf.put(127);

flip()

flip()方法將Buffer從寫模式切換為讀模式,呼叫flip()將設定position為0,limit不變還是在剛才的位置, 

3.3. 從Buffer中讀資料

有兩種方式從Buffer中讀資料:

  • 從Buffer中讀資料到Channel
  • 從Buffer自身讀取,通過get()方法 
1 //read from buffer into channel
2 int bytesWritten = inChannel.write(buf);
3 
4 byte aByte = buf.get();

rewind()

Buffer.rewind()設定position為0,以至于你可以從頭再讀一遍Buffer中的所有資料,

clear()

clear()方法將position置為0,并且limit與capacity相等,換句話說,Buffer被清除了,其實,Buffer上的資料并沒有被真正清除,只是告訴你你可以將資料寫到哪里,

compact()

compact()方法將所有未讀的資料復制到Buffer的開頭,然后它將position設定在最后一個未讀元素的右側,limit仍然等于capacity,現在,Buffer可以寫了,只不過你不能覆寫之前那些未讀的資料, 

mark() 和 reset() 

通過呼叫Buffer.mark()你可以標記一個給定的位置,你可以在隨后呼叫Buffer.reset()回傳到剛才標記的位置那里,

 

4. Selector

Selector是一個組件,它可以檢查一個或多個Channel實體,并決定哪些Channel已經準備好讀或寫,通過這種方式,一個執行緒可以管理多個通道,從而實作管理多個網路連接(PS:Selelctor可以確定哪些Channel可讀或可寫,這樣只需要一個執行緒就能管理多個網路連接)

4.1. 為什么要用Selector

使用單個執行緒來處理多個通道的優點是,處理通道所需的執行緒更少,事實上,你可以使用一個執行緒來處理所有的通道,對于作業系統來說,執行緒之間的切換非常昂貴,而且每個執行緒也會占用作業系統中的一些資源(記憶體),因此,使用的執行緒越少越好,(PS:但是請記住,現代作業系統和CPU在多任務處理方面變得越來越好,因此,隨著時間的推移,多執行緒的開銷會越來越小,)

4.2. 創建Selector

1 //  創建一個Selector
2 Selector selector = Selector.open();
3 
4 //  注冊Channel到Selector
5 channel.configureBlocking(false);
6 SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

Channel必須是非阻塞模式才能和Selector一起使用,這就意味著FileChannel不能和Selector一起用,因為FileChannel不能切換成非阻塞模式,

register()方法的第二個引數表示你希望通過Selector在Channel中監聽的事件,有四種不同的事件可以被監聽:

  1. Connect
  2. Accept
  3. Read
  4. Write

這四種事件用SelectionKey的四個常量來表示:

  1. SelectionKey.OP_CONNECT 
  2. SelectionKey.OP_ACCEPT
  3. SelectionKey.OP_READ
  4. SelectionKey.OP_WRITE

如果你對多個事件都感興趣,可以這樣寫:

1 int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

4.3. 通過Selector選擇Channel

在呼叫任意一個select()方法以后,會回傳你感興趣的并且相應事件已經準備好的channel給你,簡單地來說就是,如果你對已經為讀取做好準備的channel感興趣,那么你將從select()方法中接收到這樣的channel,

  • select() : 阻塞,直到至少有一個你注冊的事件準備好的channel
  • select(long timeout) : 跟select()很像,多了一個超時時間
  • selectNow() : 不阻塞,無論有沒有已經準備好的channel都立刻回傳

select()方法的回傳值是一個int值,表示有多少個準備好的channel,也就是說,在上一次呼叫select()以后有多少個channel變成已準備好,

完整的示例:

 1 Selector selector = Selector.open();
 2 
 3 channel.configureBlocking(false);
 4 
 5 SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
 6 
 7 while (true) {
 8 
 9     int readyChannels = selector.selectNow();
10 
11     if (readyChannels == 0) continue;
12 
13 
14     Set<SelectionKey> selectedKeys = selector.selectedKeys();
15 
16     Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
17 
18     while (keyIterator.hasNext()) {
19 
20         SelectionKey key = keyIterator.next();
21 
22         if (key.isAcceptable()) {
23             // a connection was accepted by a ServerSocketChannel.
24 
25         } else if (key.isConnectable()) {
26             // a connection was established with a remote server.
27 
28         } else if (key.isReadable()) {
29             // a channel is ready for reading
30 
31         } else if (key.isWritable()) {
32             // a channel is ready for writing
33         }
34 
35         keyIterator.remove();
36     }
37 }

5. SocketChannel

Java NIO SocketChannel是連接到TCP網路Socket的通道,

創建SocketChannel有兩種方式:

  • 打開一個SocketChannel并連接到服務器上
  • 當一個輸入連接到達ServerSocketChannel時,也會創建一個SocketChannel 
1 //  Opening a SocketChannel
2 SocketChannel socketChannel = SocketChannel.open();
3 socketChannel.connect(new InetSocketAddress("localhost", 9000));
4 
5 //  Closing a SocketChannel
6 socketChannel.close(); 

5.1. 從SocketChannel中讀取

1 ByteBuffer buf = ByteBuffer.allocate(48);
2 int bytesRead = socketChannel.read(buf);

SocketChannel.read()方法將資料從SocketChannel讀到Buffer中,其回傳值表示有多少位元組被寫道Buffer中,如果回傳-1,則表示到達流的末尾,

5.2. 寫資料到SocketChannel

 1 String newData = "hahaha";
 2 
 3 ByteBuffer buf = ByteBuffer.allocate(48);
 4 buf.clear();
 5 buf.put(newData.getBytes());
 6 
 7 buf.flip();
 8 
 9 while(buf.hasRemaining()) {
10     channel.write(buf);
11 }

注意,SocketChannel.write()是放在while回圈體中的,由于無法保證write()方法將多少位元組寫入SocketChannel,因此,要重復呼叫write()方法,直到緩沖區沒有位元組可寫為止,

5.3. 非阻塞模式

當一個SocketChannel被設定為非阻塞模式時,你就可以異步地呼叫connect(), read(), write()方法了,

connect() 

如果SocketChannel是非阻塞模式,那么當你呼叫connect()方法時,該方法可能在建立連接之前回傳,為了確定連接是否已經成功建立,可以呼叫finishConnect()方法,

1 socketChannel.configureBlocking(false);
2 socketChannel.connect(new InetSocketAddress("localhost", 9000));
3 
4 while(! socketChannel.finishConnect() ){
5     //wait, or do something else...    
6 }

write() 和 read()

在非阻塞模式下,write()方法可能會在未寫入任何內容的情況下回傳,因此需要在回圈中呼叫write(),同樣的,在非阻塞模式下,read()方法可能在沒有讀取任何資料的情況下就回傳了,因此,需要注意回傳的int,它告訴我們讀取了多少位元組, 

6. ServerSocketChannel

Java NIO ServerSocketChannel是一個可以監聽輸入TCP連接的通道,就像標準Java網路中的ServerSocket一樣, 

1 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
2 
3 serverSocketChannel.socket().bind(new InetSocketAddress(9999));
4 
5 while(true){
6     SocketChannel socketChannel = serverSocketChannel.accept();
7     //do something with socketChannel...
8 }

6.1. Listening for Incoming Connections

通過呼叫ServerSocketChannel.accept()方法可以監聽輸入的連接,當accept()方法有回傳的時候,它回傳一個帶有輸入連接的SocketChannel,因此,accept()會阻塞直到有輸入連接到來為止,通常的做法是這樣的:

1 while(true){
2     SocketChannel socketChannel = serverSocketChannel.accept();
3     //do something with socketChannel...
4 }

6.2. 非阻塞模式

ServerSocketChannel可以設定為非阻塞模式,在非阻塞模式下,呼叫accept()方法會立即回傳,因此如果沒有輸入連接到達,它回傳的可能是null,因此,必須檢查回傳的SocketChannel是否為null,下面是一個例子:

 1 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 2 
 3 serverSocketChannel.socket().bind(new InetSocketAddress(9999));
 4 serverSocketChannel.configureBlocking(false);
 5 
 6 while(true){
 7     SocketChannel socketChannel = serverSocketChannel.accept();
 8 
 9     if(socketChannel != null){
10         //do something with socketChannel...
11     }
12 }

7. Java NIO vs. IO

 

7.1. Stream Oriented vs. Buffer Oriented

Java NIO和IO之間的第一個大區別是IO是面向流的,而NIO是面向緩沖區的, 那是什么意思呢?

面向流的Java IO意味著你一次從流中讀取一個或多個位元組,如何處理讀取的位元組由你自己決定,它們不會被快取到任何地方,此外,你不能在流中的資料中來回移動,如果需要來回移動從流中讀取的資料,需要首先將其快取到緩沖區中,

Java NIO的面向緩沖區的方法略有不同,資料被讀入緩沖區,以后再從緩沖區中進行處理,你可以根據需要在緩沖區中來回移動,這使得在處理程序中更具靈活性,但是,你還需要檢查緩沖區是否包含你需要的所有資料,以便對其進行完全處理,并且,你需要確保在將更多資料讀入緩沖區時,不會覆寫緩沖區中尚未處理的資料,

7.2. Blocking vs. Non-blocking IO

Java IO的各種流被阻塞,這意味著,當執行緒呼叫read()或write()時,該執行緒將被阻塞,直到有一些資料需要讀取,或者資料被完全寫入, 在此期間,執行緒無法執行其他任何操作,

Java NIO的非阻塞模式允許執行緒請求從通道讀取資料,并且只獲取當前可用的資料,如果當前沒有可用的資料,則什么也得不到,在資料可以讀取之前,執行緒不會一直處于阻塞狀態,而是可以繼續執行其他操作,

非阻塞寫入也是如此,執行緒可以請求將某些資料寫入通道,但不等待將其完全寫入,然后執行緒可以繼續運行,同時執行其他操作,

當執行緒在IO呼叫中沒有被阻塞時,它們的空閑時間通常在其他通道上執行IO,也就是說,單個執行緒現在可以管理輸入和輸出的多個通道,

7.3. Selectors

Java NIO的Selector允許單個執行緒監視多個輸入通道,可以使用Selector注冊多個通道,然后使用一個執行緒“select”具有可用于處理輸入的通道,或者選擇準備好進行寫入的通道,這種選擇器機制使單個執行緒可以輕松管理多個通道,

7.4. 不同的資料讀取方式

Java IO: Reading data from a blocking stream 

Java NIO: Reading data from a channel until all needed data is in buffer

8. 示例

ChatServer.java

 1 package com.cjs;
 2 
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.SelectionKey;
 7 import java.nio.channels.Selector;
 8 import java.nio.channels.ServerSocketChannel;
 9 import java.nio.channels.SocketChannel;
10 import java.nio.charset.Charset;
11 import java.util.Iterator;
12 import java.util.Set;
13 
14 public class ChatServer {
15 
16     private ServerSocketChannel serverSocketChannel;
17     private Selector selector;
18 
19     private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
20     private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
21 
22     private static final Charset CHARSET = Charset.forName("UTF-8");
23 
24     public ChatServer(int port) {
25         try {
26             serverSocketChannel = ServerSocketChannel.open();
27             serverSocketChannel.socket().bind(new InetSocketAddress(port));
28             serverSocketChannel.configureBlocking(false);
29 
30             selector = Selector.open();
31             serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
32 
33         } catch (IOException e) {
34             e.printStackTrace();
35         }
36     }
37 
38     /**
39      * 監聽客戶端連接
40      */
41     public void listen() throws IOException {
42         System.out.println("服務器啟動成功");
43         while(true) {
44 
45             int readyChannels = selector.selectNow();
46 
47             if(readyChannels == 0) {
48                 continue;
49             }
50 
51             Set<SelectionKey> selectedKeys = selector.selectedKeys();
52 
53             Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
54 
55             while(keyIterator.hasNext()) {
56 
57                 SelectionKey key = keyIterator.next();
58 
59                 if(key.isAcceptable()) {
60                     // a connection was accepted by a ServerSocketChannel.
61                     SocketChannel socketChannel = serverSocketChannel.accept();
62                     socketChannel.configureBlocking(false);
63                     socketChannel.register(selector, SelectionKey.OP_READ);
64                     socketChannel.write(CHARSET.encode("來自服務器的問候: 你好!"));
65                 } else if (key.isConnectable()) {
66                     // a connection was established with a remote server.
67                 } else if (key.isReadable()) {
68                     // a channel is ready for reading
69                     SocketChannel socketChannel = (SocketChannel) key.channel();
70                     String msg = "";
71                     while (socketChannel.read(readBuffer) > 0) {
72                         readBuffer.flip();
73                         msg += CHARSET.decode(readBuffer).toString();
74                     }
75                     System.out.println(msg);
76                     readBuffer.clear();
77 
78                     //  給客戶端回復訊息
79                     writeBuffer.put("服務器對你說: 收到".getBytes());
80                     writeBuffer.flip();
81                     socketChannel.write(writeBuffer);
82                     writeBuffer.clear();
83 
84                 } else if (key.isWritable()) {
85                     // a channel is ready for writing
86                 }
87 
88                 keyIterator.remove();
89             }
90         }
91     }
92 
93     public static void main(String[] args) throws IOException {
94         ChatServer chatServer = new ChatServer(9000);
95         chatServer.listen();
96     }
97 }

ChatClient.java

  1 package com.cjs;
  2 
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.SocketChannel;
  9 import java.nio.charset.Charset;
 10 import java.util.Iterator;
 11 import java.util.Scanner;
 12 
 13 public class ChatClient {
 14 
 15     private SocketChannel socketChannel;
 16     private Selector selector;
 17 
 18     private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
 19 
 20     private static final Charset CHARSET = Charset.forName("UTF-8");
 21 
 22     public ChatClient(String host, int port) {
 23         try {
 24             socketChannel = SocketChannel.open();
 25             socketChannel.configureBlocking(false);
 26             socketChannel.connect(new InetSocketAddress(host, port));
 27 
 28             while (!socketChannel.finishConnect()) {
 29                 System.out.println("正在等待連接");
 30             }
 31 
 32             System.out.println("連接成功");
 33 
 34             selector = Selector.open();
 35             socketChannel.register(selector, SelectionKey.OP_READ);
 36 
 37             new Thread(new Handler(selector)).start();
 38 
 39         } catch (IOException e) {
 40             e.printStackTrace();
 41         }
 42     }
 43 
 44     /**
 45      * 給服務器發訊息
 46      */
 47     public void start() throws IOException {
 48         Scanner scanner = new Scanner(System.in);
 49         while (scanner.hasNext()) {
 50             String line = scanner.nextLine();
 51             if (null != line && !"".equals(line.trim())) {
 52                 socketChannel.write(CHARSET.encode(line));
 53             }
 54         }
 55     }
 56 
 57     /**
 58      * 接收來自服務器的訊息
 59      */
 60     class Handler implements Runnable {
 61 
 62         private Selector selector;
 63 
 64         public Handler(Selector selector) {
 65             this.selector = selector;
 66         }
 67 
 68         @Override
 69         public void run() {
 70             try {
 71                 while (true) {
 72 
 73                     int readyChannels = selector.selectNow();
 74 
 75                     if (readyChannels == 0) continue;
 76 
 77                     Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
 78 
 79                     while (keyIterator.hasNext()) {
 80 
 81                         SelectionKey key = keyIterator.next();
 82 
 83                         if (key.isReadable()) {
 84                             SocketChannel socketChannel = (SocketChannel) key.channel();
 85                             String msg = "";
 86                             while (socketChannel.read(readBuffer) > 0) {
 87                                 //  從寫模式切換為讀模式
 88                                 readBuffer.flip();
 89                                 msg += CHARSET.decode(readBuffer);
 90                             }
 91                             System.out.println(msg);
 92                             readBuffer.clear();
 93                         }
 94 
 95                         keyIterator.remove();
 96                     }
 97                 }
 98             } catch (IOException e) {
 99                 e.printStackTrace();
100             }
101         }
102     }
103 
104     public static void main(String[] args) throws IOException {
105         ChatClient chatClient = new ChatClient("127.0.0.1", 9000);
106         chatClient.start();
107     }
108 } 

控制臺

9. 參考

http://tutorials.jenkov.com/java-nio/index.html

http://tutorials.jenkov.com/java-nio/socketchannel.html 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/179761.html

標籤:其他

上一篇:Redis Lua腳本完全入門

下一篇:作為Java架構師必須掌握的分布式架構技術,阿里百萬架構師精心整理的ZK+Dubbo筆記來助你一臂之力!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more