30道計網(wǎng)常考面試題含答案總結!血賺!
往期熱門文章: 1、Spring官方為什么建議構造器注入? 2、還在用 Random生成隨機數(shù)?試試 ThreadLocalRandom,超好用! 3、這些年 Java8 的 Optional 你用對了嗎? 4、當 Docker 遇上 IDEA ,生產(chǎn)力徹底炸裂了 5、如何把Spring Boot的Jar包做成exe?超詳細教程來了!
1、為什么TCP連接的時候是3次?2次不可以嗎?
考慮丟包問題
因為需要考慮連接時丟包的問題,如果只握手2次,第二次握手時如果服務端發(fā)給客戶端的確認報文段丟失,此時服務端已經(jīng)準備好了收發(fā)數(shù)(可以理解服務端已經(jīng)連接成功)據(jù),而客戶端一直沒收到服務端的確認報文,所以客戶端就不知道服務端是否已經(jīng)準備好了(可以理解為客戶端未連接成功),這種情況下客戶端不會給服務端發(fā)數(shù)據(jù),也會忽略服務端發(fā)過來的數(shù)據(jù)。
如果是三次握手,即便發(fā)生丟包也不會有問題,比如如果第三次握手客戶端發(fā)的確認ack報文丟失,服務端在一段時間內(nèi)沒有收到確認ack報文的話就會重新進行第二次握手,也就是服務端會重發(fā)SYN報文段,客戶端收到重發(fā)的報文段后會再次給服務端發(fā)送確認ack報文。
保證序列號雙方確認
為了實現(xiàn)可靠數(shù)據(jù)傳輸, TCP 協(xié)議的通信雙方, 都必須維護一個序列號, 以標識發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對方收到的。三次握手的過程即是通信雙方相互告知序列號起始值, 并確認對方已經(jīng)收到了序列號起始值的必經(jīng)步驟 如果只是兩次握手, 至多只有連接發(fā)起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認
2、為什么TCP連接的時候是3次,關閉的時候卻是4次?
3、為什么客戶端發(fā)出第四次揮手的確認報文后要等2MSL的時間才能釋放TCP連接?
4、如果已經(jīng)建立了連接,但是客戶端突然出現(xiàn)故障了怎么辦?
5、time wait過多會出現(xiàn)什么問題?
6、CLOSE_WAIT 過多會出現(xiàn)什么問題?
7、什么是HTTP、HTTPS?
HTTP 是一個在計算機世界里專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數(shù)據(jù)的約定和規(guī)范
端口 :HTTP的URL由“http://”起始且默認使用端口80,而HTTPS的URL由“https://”起始且默認使用端口443。 安全性和資源消耗:HTTP協(xié)議運行在TCP之上,所有傳輸?shù)膬?nèi)容都是明文,客戶端和服務器端都無法驗證對方的身份。
HTTPS中的S表示SSL或者TLS,就是在原HTTP的基礎上加上一層用于數(shù)據(jù)加密、解密、身份認證的安全層。HTTPS是運行在SSL/TLS之上的HTTP協(xié)議,SSL/TLS 運行在TCP之上。所有傳輸?shù)膬?nèi)容都經(jīng)過加密,加密采用對稱加密,但對稱加密的密鑰用服務器方的證書進行了非對稱加密。所以說,HTTP 安全性沒有 HTTPS高,但是 HTTPS 比HTTP耗費更多服務器資源。
對稱加密:密鑰只有一個,加密解密為同一個密碼,且加解密速度快,典型的對稱加密算法有DES、AES等; 非對稱加密:加密和解密使用不同的密鑰,這兩個密鑰形成有且僅有唯一的配對,叫公鑰和私鑰。數(shù)據(jù)用公鑰加密后必須用私鑰解密,數(shù)據(jù)用私鑰加密后必須用公鑰解密,相對對稱加密速度較慢,典型的非對稱加密算法有RSA、DSA等。
8、HTTP 與 HTTPS 的區(qū)別?
https協(xié)議需要到ca申請證書,一般免費證書很少,需要交費。 http是超文本傳輸協(xié)議,信息是明文傳輸,https 則是具有安全性的ssl加密傳輸協(xié)議。 http和https使用的是完全不同的連接方式用的端口也不一樣,前者是80,后者是443。 http的連接很簡單,是無狀態(tài)的 。 HTTPS協(xié)議是由SSL+HTTP協(xié)議構建的可進行加密傳輸、身份認證的網(wǎng)絡協(xié)議, 要比http協(xié)議安全。
9、GET和POST區(qū)別?
GET:從服務器上獲取數(shù)據(jù),也就是所謂的查,僅僅是獲取服務器資源,不進行修改。 POST:向服務器提交數(shù)據(jù),這就涉及到了數(shù)據(jù)的更新,也就是更改服務器的數(shù)據(jù)。
區(qū)別
get 方法一般用于請求,比如你在瀏覽器地址欄輸入 www.cxuanblog.com其實就是發(fā)送了一個 get 請求,它的主要特征是請求服務器返回資源,而 post 方法一般用于```表單`的提交,相當于是把信息提交給服務器,等待服務器作出響應,get 相當于一個是 pull/拉的操作,而 post 相當于是一個 push/推的操作。get 方法是不安全的,因為你在發(fā)送請求的過程中,你的請求參數(shù)會拼在 URL 后面,從而導致容易被攻擊者竊取,對你的信息造成破壞和偽造;而 post 方法是把參數(shù)放在請求體 body 中的,這對用戶來說不可見。 get 請求的 URL 有長度限制,這個限制是瀏覽器或者服務器給添加的,http協(xié)議并沒有對url長度進行限制,目的是為了保證服務器和瀏覽器能夠正常運行,防止有人惡意發(fā)送請求。而 post 請求會把參數(shù)和值放在消息體中,對數(shù)據(jù)長度沒有要求。 get 請求會被瀏覽器主動 cache,而 post 不會,除非手動設置。 get 請求在發(fā)送過程中會產(chǎn)生一個 TCP 數(shù)據(jù)包;post 在發(fā)送過程中會產(chǎn)生兩個 TCP 數(shù)據(jù)包。對于 get 方式的請求,瀏覽器會把 http header 和 data 一并發(fā)送出去,服務器響應 200(返回數(shù)據(jù));而對于 post,瀏覽器先發(fā)送 header,服務器響應 100 continue,瀏覽器再發(fā)送 data,服務器響應 200 ok(返回數(shù)據(jù))。
10、瀏覽器關閉后,session還可以用嗎?
11、如果客戶端禁止 cookie 能實現(xiàn) session 還能用嗎?
最常用的就是利用 URL 重寫把 Session ID 直接附加在URL路徑的后面。 用文件、數(shù)據(jù)庫等形式保存Session ID,在跨頁過程中手動調(diào)用
12、介紹下了解的通信協(xié)議?
13、什么是ARP/RARP協(xié)議?
14、地址欄輸入 URL 發(fā)生了什么
首先,你需要在瀏覽器中的 URL 地址上,輸入你想訪問的地址,比如www.baidu.com
然后,瀏覽器會根據(jù)你輸入的 URL 地址,去查找域名是否被本地 DNS 緩存,不同瀏覽器對 DNS 的設置不同,如果瀏覽器緩存了你想訪問的 URL 地址,那就直接返回 ip。如果沒有緩存你的 URL 地址,瀏覽器就會發(fā)起系統(tǒng)調(diào)用來查詢本機
hosts文件是否有配置 ip 地址,如果找到,直接返回。如果找不到,就向網(wǎng)絡中發(fā)起一個 DNS 查詢。在由根域名服務器 -> 頂級域名服務器 -> 權威 DNS 服務器后,由權威服務器告訴本地服務器目標 IP 地址,再有本地 DNS 服務器告訴用戶需要訪問的 IP 地址。“
DNS是域名系統(tǒng)(DomainNameSystem)的縮寫,該系統(tǒng)用于命名組織到域?qū)哟谓Y構中的計算機和網(wǎng)絡服務,可以簡單地理解為將URL轉(zhuǎn)換為IP地址。
第三步,瀏覽器需要和目標服務器建立 TCP 連接,需要經(jīng)過三次握手的過程,TCP/IP 分為四層,在發(fā)送數(shù)據(jù)時,每層都要對數(shù)據(jù)進行封裝:
將數(shù)據(jù)段打包,并加入源及目標的IP地址,并且負責尋找傳輸路線。 判斷目標地址是否與當前地址處于同一網(wǎng)絡中,是的話直接根據(jù) Mac 地址發(fā)送,否則使用路由表查找下一跳地址,以及使用 ARP 協(xié)議查詢它的 Mac 地址。 傳輸層會發(fā)起一條到達服務器的 TCP 連接,為了方便傳輸,會對數(shù)據(jù)進行分割(以報文段為單位),并標記編號,方便服務器接受時能夠準確地還原報文信息。 請求報頭(Request Header):請求方法、目標地址、遵循的協(xié)議等等 請求主體(其他參數(shù)) 1. 應用層:發(fā)送 HTTP 請求 在前面的步驟我們已經(jīng)得到服務器的 IP 地址,瀏覽器會開始構造一個 HTTP 報文,其中包括: 2. 傳輸層:TCP 傳輸報文 3. 網(wǎng)絡層:IP協(xié)議查詢Mac地址 4. 鏈路層:以太網(wǎng)協(xié)議 在建立連接后,瀏覽器會向目標服務器發(fā)起
HTTP-GET請求,包括其中的 URL,HTTP 1.1 后默認使用長連接,只需要一次握手即可多次傳輸數(shù)據(jù)。如果目標服務器只是一個簡單的頁面,就會直接返回。但是對于某些大型網(wǎng)站的站點,往往不會直接返回主機名所在的頁面,而會直接重定向。返回的狀態(tài)碼就不是 200 ,而是 301,302 以 3 開頭的重定向碼,瀏覽器在獲取了重定向響應后,在響應報文中 Location 項找到重定向地址,瀏覽器重新第一步訪問即可。
然后瀏覽器重新發(fā)送請求,攜帶新的 URL,返回狀態(tài)碼 200 OK,表示服務器可以響應請求,返回報文。
渲染頁面
15、中間人有可能篡改該證書嗎?
16、中間人有可能把證書掉包嗎?
假設有另一個網(wǎng)站B也拿到了CA機構認證的證書,它想搞垮網(wǎng)站A,想劫持網(wǎng)站A的信息。于是它成為中間人攔截到了A傳給瀏覽器的證書,然后替換成自己的證書,傳給瀏覽器,之后瀏覽器就會錯誤地拿到B的證書里的公鑰了,會導致上文提到的漏洞。 其實這并不會發(fā)生,因為證書里包含了網(wǎng)站A的信息,包括域名,瀏覽器把證書里的域名與自己請求的域名比對一下就知道有沒有被掉包了。
17、為什么制作數(shù)字簽名時需要hash一次?
18、http協(xié)議實現(xiàn)了什么?
客戶與服務器建立連接tcp 客戶向服務器提出請求 服務器接受請求,并根據(jù)請求返回相應的文件作為應答 客戶與服務器關閉連接。
19、什么是無狀態(tài)協(xié)議,HTTP 是無狀態(tài)協(xié)議嗎,怎么解決?
無狀態(tài)協(xié)議(Stateless Protocol) 就是指瀏覽器對于事務的處理沒有記憶能力。舉個例子來說就是比如客戶請求獲得網(wǎng)頁之后關閉瀏覽器,然后再次啟動瀏覽器,登錄該網(wǎng)站,但是服務器并不知道客戶關閉了一次瀏覽器。
HTTP 就是一種無狀態(tài)的協(xié)議,他對用戶的操作沒有記憶能力??赡艽蠖鄶?shù)用戶不相信,他可能覺得每次輸入用戶名和密碼登陸一個網(wǎng)站后,下次登陸就不再重新輸入用戶名和密碼了。這其實不是 HTTP 做的事情,起作用的是一個叫做 小甜餅(Cookie)的機制。它能夠讓瀏覽器具有記憶能力。
那么我們保存用戶狀態(tài)呢?Session 機制的存在就是為了解決這個問題,Session 的主要作用就是通過服務端記錄用戶的狀態(tài)session登錄的認證方案是看,用戶從客戶端傳遞用戶名和密碼登錄信息,服務端認證后將信息儲存在session中,將session_id放入cookie中,以后訪問其他頁面,服務器都會帶著cookie,服務端會自動從cookie中獲取session_id,在從session中獲取認證信息。
在服務端保存 Session 的方法很多,最常用的就是內(nèi)存和數(shù)據(jù)庫(比如是使用內(nèi)存數(shù)據(jù)庫redis保存)。既然 Session 存放在服務器端,那么我們?nèi)绾螌崿F(xiàn) Session 跟蹤呢?大部分情況下,我們都是通過在 Cookie 中附加一個 Session ID 來方式來跟蹤。
20、為什么使用非對稱加密和對稱加密?
21、TCP粘包是什么?
22、什么時候需要考慮粘包問題?
如果利用tcp每次發(fā)送數(shù)據(jù),就與對方建立連接,然后雙方發(fā)送完一段數(shù)據(jù)后,就關閉連接,這樣就不會出現(xiàn)粘包問題(因為只有一種包結構,類似于http協(xié)議)。
關閉連接主要是要雙方都發(fā)送close連接(參考tcp關閉協(xié)議)。如:A需要發(fā)送一段字符串給B,那么A與B建立連接,然后發(fā)送雙方都默認好的協(xié)議字符如"hello give me sth abour yourself",然后B收到報文后,就將緩沖區(qū)數(shù)據(jù)接收,然后關閉連接,這樣粘包問題不用考慮到,因為大家都知道是發(fā)送一段字符。
如果發(fā)送數(shù)據(jù)無結構,如文件傳輸,這樣發(fā)送方只管發(fā)送,接收方只管接收存儲就ok,也不用考慮粘包
如果雙方建立連接,需要在連接后一段時間內(nèi)發(fā)送不同結構數(shù)據(jù),如連接后,有好幾種結構,一般可能會在頭加一個數(shù)據(jù)長度之類的包,以確保接收
23、粘包出現(xiàn)原因?
發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包
接收方不及時接收緩沖區(qū)的包,造成多個包接收
發(fā)送方引起的粘包是由TCP協(xié)議本身造成的,TCP為提高傳輸效率,發(fā)送方往往要收集到足夠多的數(shù)據(jù)后才發(fā)送一包數(shù)據(jù)。若連續(xù)幾次發(fā)送的數(shù)據(jù)都很少,通常TCP會根據(jù)優(yōu)化算法把這些數(shù)據(jù)合成一包后一次發(fā)送出去,這樣接收方就收到了粘包數(shù)據(jù)。
接收方引起的粘包是由于接收方用戶進程不及時接收數(shù)據(jù),從而導致粘包現(xiàn)象。這是因為接收方先把收到的數(shù)據(jù)放在系統(tǒng)接收緩沖區(qū),用戶進程從該緩沖區(qū)取數(shù)據(jù),若下一包數(shù)據(jù)到達時前一包數(shù)據(jù)尚未被用戶進程取走,則下一包數(shù)據(jù)放到系統(tǒng)接收緩沖區(qū)時就接到前一包數(shù)據(jù)之后,而用戶進程根據(jù)預先設定的緩沖區(qū)大小從系統(tǒng)接收緩沖區(qū)取數(shù)據(jù),這樣就一次取到了多包數(shù)據(jù)。
粘包情況有兩種,一種是粘在一起的包都是完整的數(shù)據(jù)包,另一種情況是粘在一起的包有不完整的包。
不是所有的粘包現(xiàn)象都需要處理,若傳輸?shù)臄?shù)據(jù)為不帶結構的連續(xù)流數(shù)據(jù)(如文件傳輸),則不必把粘連的包分開(簡稱分包)。但在實際工程應用中,傳輸?shù)臄?shù)據(jù)一般為帶結構的數(shù)據(jù),這時就需要做分包處理。
在處理定長結構數(shù)據(jù)的粘包問題時,分包算法比較簡單;在處理不定長結構數(shù)據(jù)的粘包問題時,分包算法就比較復雜。特別是粘在一起的包有不完整的包的粘包情況,由于一包數(shù)據(jù)內(nèi)容被分在了兩個連續(xù)的接收包中,處理起來難度較大。實際工程應用中應盡量避免出現(xiàn)粘包現(xiàn)象。
24、如何避免粘包問題?
對于發(fā)送方引起的粘包現(xiàn)象,用戶可通過編程設置來避免,TCP提供了強制數(shù)據(jù)立即傳送的操作指令push,TCP軟件收到該操作指令后,就立即將本段數(shù)據(jù)發(fā)送出去,而不必等待發(fā)送緩沖區(qū)滿;
對于接收方引起的粘包,則可通過優(yōu)化程序設計、精簡接收進程工作量、提高接收進程優(yōu)先級等措施,使其及時接收數(shù)據(jù),從而盡量避免出現(xiàn)粘包現(xiàn)象;
由接收方控制,將一包數(shù)據(jù)按結構字段,人為控制分多次接收,然后合并,通過這種手段來避免粘包。
以上提到的三種措施,都有其不足之處。
第一種編程設置方法雖然可以避免發(fā)送方引起的粘包,但它關閉了優(yōu)化算法,降低了網(wǎng)絡發(fā)送效率,影響應用程序的性能,一般不建議使用。
第二種方法只能減少出現(xiàn)粘包的可能性,但并不能完全避免粘包,當發(fā)送頻率較高時,或由于網(wǎng)絡突發(fā)可能使某個時間段數(shù)據(jù)包到達接收方較快,接收方還是有可能來不及接收,從而導致粘包。
第三種方法雖然避免了粘包,但應用程序的效率較低,對實時應用的場合不適合。
25、講一講拆包?
1、動態(tài)緩沖區(qū)暫存方式。
為每一個連接動態(tài)分配一個緩沖區(qū),同時把此緩沖區(qū)和SOCKET關聯(lián),常用的是通過結構體關聯(lián).
當接收到數(shù)據(jù)時首先把此段數(shù)據(jù)存放在緩沖區(qū)中.
判斷緩存區(qū)中的數(shù)據(jù)長度是否夠一個包頭的長度,如不夠,則不進行拆包操作.
根據(jù)包頭數(shù)據(jù)解析出里面代表包體長度的變量.
判斷緩存區(qū)中除包頭外的數(shù)據(jù)長度是否夠一個包體的長度,如不夠,則不進行拆包操作.
取出整個數(shù)據(jù)包.這里的"取"的意思是不光從緩沖區(qū)中拷貝出數(shù)據(jù)包,而且要把此數(shù)據(jù)包從緩存區(qū)中刪除掉.刪除的辦法就是把此包后面的數(shù)據(jù)移動到緩沖區(qū)的起始地址.
為每個連接動態(tài)分配一個緩沖區(qū)增大了內(nèi)存的使用.
有三個地方需要拷貝數(shù)據(jù),一個地方是把數(shù)據(jù)存放在緩沖區(qū),一個地方是把完整的數(shù)據(jù)包從緩沖區(qū)取出來,一個地方是把數(shù)據(jù)包從緩沖區(qū)中刪除.第二種拆包的方法會解決和完善這些缺點.
2、利用底層的緩沖區(qū)來進行拆包
由于TCP也維護了一個緩沖區(qū),所以我們完全可以利用TCP的緩沖區(qū)來緩存我們的數(shù)據(jù),這樣一來就不需要為每一個連接分配一個緩沖區(qū)了。另一方面我們知道recv或者wsarecv都有一個參數(shù),用來表示我們要接收多長長度的數(shù)據(jù)。利用這兩個條件我們就可以對第一種方法進行優(yōu)化。
對于阻塞SOCKET來說,我們可以利用一個循環(huán)來接收包頭長度的數(shù)據(jù),然后解析出代表包體長度的那個變量,再用一個循環(huán)來接收包體長度的數(shù)據(jù)。
26、講一下HTTP 請求頁面的過程?
有了 HTTP 服務器的 IP 地址之后,主機就能夠生成 TCP 套接字,該套接字將用于向 Web 服務器發(fā)送 HTTP GET 報文。 在生成 TCP 套接字之前,必須先與 HTTP 服務器進行三次握手來建立連接。生成一個具有目的端口 80 的 TCP SYN 報文段,并向 HTTP 服務器發(fā)送該報文段。 HTTP 服務器收到該報文段之后,生成 TCP SYN ACK 報文段,發(fā)回給主機。 連接建立之后,瀏覽器生成 HTTP GET 報文,并交付給 HTTP 服務器。 HTTP 服務器從 TCP 套接字讀取 HTTP GET 報文,生成一個 HTTP 響應報文,將 Web 頁面內(nèi)容放入報文主體中,發(fā)回給主機。 瀏覽器收到 HTTP 響應報文后,抽取出 Web 頁面內(nèi)容,之后進行渲染,顯示 Web 頁面。
27、有哪些方面的因素會導致網(wǎng)站訪問慢?
1. 服務器出口帶寬不夠用
本身服務器購買的出口帶寬比較。一旦并發(fā)量大的話,就會造成分給每個用戶的出口帶寬就小,訪問速度自然就會慢。
跨運營商網(wǎng)絡導致帶寬縮減。例如,公司網(wǎng)站放在電信的網(wǎng)絡上,那么客戶這邊對接是長城寬帶或聯(lián)通,這也可能導致帶寬的縮減。
2. 服務器負載過大,導致響應不過來
分析系統(tǒng)負載,使用 w 命令或者 uptime 命令查看系統(tǒng)負載。如果負載很高,則使用 top 命令查看 CPU ,MEM 等占用情況,要么是 CPU 繁忙,要么是內(nèi)存不夠。 如果這二者都正常,再去使用 sar 命令分析網(wǎng)卡流量,分析是不是遭到了攻擊。一旦分析出問題的原因,采取對應的措施解決,如決定要不要殺死一些進程,或者禁止一些訪問等。
3. 數(shù)據(jù)庫瓶頸
如果慢查詢比較多。那么就要開發(fā)人員或 DBA 協(xié)助進行 SQL 語句的優(yōu)化。 如果數(shù)據(jù)庫響應慢,考慮可以加一個數(shù)據(jù)庫緩存,如 Redis 等。然后,也可以搭建 MySQL 主從,一臺 MySQL 服務器負責寫,其他幾臺從數(shù)據(jù)庫負責讀。
4. 網(wǎng)站開發(fā)代碼沒有優(yōu)化好
28、針對網(wǎng)站訪問慢,怎么去排查?
首先要確定是用戶端還是服務端的問題。當接到用戶反饋訪問慢,那邊自己立即訪問網(wǎng)站看看,如果自己這邊訪問快,基本斷定是用戶端問題,就需要耐心跟客戶解釋,協(xié)助客戶解決問題。
如果訪問也慢,那么可以利用瀏覽器的調(diào)試功能,看看加載那一項數(shù)據(jù)消耗時間過多,是圖片加載慢,還是某些數(shù)據(jù)加載慢。
針對服務器負載情況。 查看服務器硬件(網(wǎng)絡、CPU、內(nèi)存)的消耗情況。如果是購買的云主機,比如阿里云,可以登錄阿里云平臺提供各方面的監(jiān)控,比如 CPU、內(nèi)存、帶寬的使用情況。
如果發(fā)現(xiàn)硬件資源消耗都不高,那么就需要通過查日志,比如看看 MySQL慢查詢的日志,看看是不是某條 SQL 語句查詢慢,導致網(wǎng)站訪問慢。
29、怎么去解決訪問慢問題?
如果是出口帶寬問題,那么久申請加大出口帶寬。 如果慢查詢比較多,那么就要開發(fā)人員或 DBA 協(xié)助進行 SQL 語句的優(yōu)化。 如果數(shù)據(jù)庫響應慢,考慮可以加一個數(shù)據(jù)庫緩存,如 Redis 等等。然后也可以搭建MySQL 主從,一臺 MySQL 服務器負責寫,其他幾臺從數(shù)據(jù)庫負責讀。 申請購買 CDN 服務,加載用戶的訪問。 如果訪問還比較慢,那就需要從整體架構上進行優(yōu)化咯。做到專角色專用,多臺服務器提供同一個服務。
最近熱文閱讀:
1、Spring官方為什么建議構造器注入? 2、還在用 Random生成隨機數(shù)?試試 ThreadLocalRandom,超好用! 3、這些年 Java8 的 Optional 你用對了嗎? 4、當 Docker 遇上 IDEA ,生產(chǎn)力徹底炸裂了 5、如何把Spring Boot的Jar包做成exe?超詳細教程來了! 6、徹底搞懂 Nginx 的五大應用場景 7、推薦60個相見恨晚的神器工具 8、為什么有些大公司技術弱爆了? 9、這 40 道 Redis 面試題讓你不再慌(附答案) 10、優(yōu)秀的代碼都是如何分層的? 關注公眾號,你想要的Java都在這里
