面试题:各大公司Java后端开发面试题总结 已看1 背1 有用 链接有必要看看
ThreadLocal(線程變量副本) ? ? ? --整理
Synchronized實(shí)現(xiàn)內(nèi)存共享,ThreadLocal為每個(gè)線程維護(hù)一個(gè)本地變量。
采用空間換時(shí)間,它用于線程間的數(shù)據(jù)隔離,為每一個(gè)使用該變量的線程提供一個(gè)副本,每個(gè)線程都可以獨(dú)立地改變自己的副本,副本之間不共享數(shù)據(jù).而不會(huì)和其他線程的副本沖突。
ThreadLocal類中維護(hù)一個(gè)Map,用于存儲(chǔ)每一個(gè)線程的變量副本,Map中元素的鍵為線程對(duì) ?象,而值為對(duì)應(yīng)線程的變量副本。
ThreadLocal在Spring中發(fā)揮著巨大的作用,在管理Request作用域中的Bean、事務(wù)管理、任務(wù)調(diào)度、AOP等模塊都出現(xiàn)了它的身影。
Spring中絕大部分Bean都可以聲明成Singleton作用域,采用ThreadLocal進(jìn)行封裝,因此有狀態(tài)的Bean就能夠以singleton的方式在多線程中正常工作了。
友情鏈接:深入研究java.lang.ThreadLocal類
Java內(nèi)存模型:
Java虛擬機(jī)規(guī)范中將Java運(yùn)行時(shí)數(shù)據(jù)分為六種。
1.程序計(jì)數(shù)器:是一個(gè)數(shù)據(jù)結(jié)構(gòu),用于保存當(dāng)前正常執(zhí)行的程序的內(nèi)存地址。Java虛擬機(jī)的多線程就是通過線程輪流切換并分配處理器時(shí)間來實(shí)現(xiàn)的,為了線程切換后能恢復(fù)到正確的位置,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,互不影響,該區(qū)域?yàn)椤熬€程私有”。
2.Java虛擬機(jī)棧:線程私有的,與線程生命周期相同,用于存儲(chǔ)局部變量表,操作棧,方法返回值。局部變量表放著基本數(shù)據(jù)類型,還有對(duì)象的引用。
3.本地方法棧:跟虛擬機(jī)棧很像,不過它是為虛擬機(jī)使用到的Native方法服務(wù)。
4.Java堆:所有線程共享的一塊內(nèi)存區(qū)域,對(duì)象實(shí)例幾乎都在這分配內(nèi)存。
5.方法區(qū):各個(gè)線程共享的區(qū)域,儲(chǔ)存虛擬機(jī)加載的類信息,常量,靜態(tài)變量,編譯后的代碼。
6.運(yùn)行時(shí)常量池:代表運(yùn)行時(shí)每個(gè)class文件中的常量表。包括幾種常量:編譯時(shí)的數(shù)字常量、方法或者域的引用。
友情鏈接:?Java中JVM虛擬機(jī)詳解
“你能不能談?wù)?#xff0c;java GC是在什么時(shí)候,對(duì)什么東西,做了什么事情?”
在什么時(shí)候:
1.新生代有一個(gè)Eden區(qū)和兩個(gè)survivor區(qū),首先將對(duì)象放入Eden區(qū),如果空間不足就向其中的一個(gè)survivor區(qū)上放,如果仍然放不下就會(huì)引發(fā)一次發(fā)生在新生代的minor GC,將存活的對(duì)象放入另一個(gè)survivor區(qū)中,然后清空Eden和之前的那個(gè)survivor區(qū)的內(nèi)存。在某次GC過程中,如果發(fā)現(xiàn)仍然又放不下的對(duì)象,就將這些對(duì)象放入老年代內(nèi)存里去。
2.大對(duì)象以及長(zhǎng)期存活的對(duì)象直接進(jìn)入老年區(qū)。
3.當(dāng)每次執(zhí)行minor GC的時(shí)候應(yīng)該對(duì)要晉升到老年代的對(duì)象進(jìn)行分析,如果這些馬上要到老年區(qū)的老年對(duì)象的大小超過了老年區(qū)的剩余大小,那么執(zhí)行一次Full GC以盡可能地獲得老年區(qū)的空間。
對(duì)什么東西:從GC Roots搜索不到,而且經(jīng)過一次標(biāo)記清理之后仍沒有復(fù)活的對(duì)象。
做什么:?
新生代:復(fù)制清理;?
老年代:標(biāo)記-清除和標(biāo)記-壓縮算法;?
永久代:存放Java中的類和加載類的類加載器本身。
GC Roots都有哪些:?
1. 虛擬機(jī)棧中的引用的對(duì)象?
2. 方法區(qū)中靜態(tài)屬性引用的對(duì)象,常量引用的對(duì)象?
3. 本地方法棧中JNI(即一般說的Native方法)引用的對(duì)象。
友情鏈接:Java GC的那些事(上)
友情鏈接:Java GC的那些事(下)
友情鏈接:CMS垃圾收集器介紹
Synchronized 與Lock都是可重入鎖,同一個(gè)線程再次進(jìn)入同步代碼的時(shí)候.可以使用自己已經(jīng)獲取到的鎖。
Synchronized是悲觀鎖機(jī)制,獨(dú)占鎖。而Locks.ReentrantLock是樂觀鎖,每次不加鎖而是假設(shè)沒有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試,直到成功為止。?
b) tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會(huì)等待參數(shù)給定的時(shí)間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時(shí),返回false;
ReentrantLock適用場(chǎng)景
友情鏈接:?Synchronized關(guān)鍵字、Lock,并解釋它們之間的區(qū)別
StringBuffer是線程安全的,每次操作字符串,String會(huì)生成一個(gè)新的對(duì)象,而StringBuffer不會(huì);StringBuilder是非線程安全的
友情鏈接:String、StringBuffer與StringBuilder之間區(qū)別
fail-fast:機(jī)制是java集合(Collection)中的一種錯(cuò)誤機(jī)制。當(dāng)多個(gè)線程對(duì)同一個(gè)集合的內(nèi)容進(jìn)行操作時(shí),就可能會(huì)產(chǎn)生fail-fast事件。?
例如:當(dāng)某一個(gè)線程A通過iterator去遍歷某集合的過程中,若該集合的內(nèi)容被其他線程所改變了;那么線程A訪問集合時(shí),就會(huì)拋出ConcurrentModificationException異常,產(chǎn)生fail-fast事件
happens-before:如果兩個(gè)操作之間具有happens-before 關(guān)系,那么前一個(gè)操作的結(jié)果就會(huì)對(duì)后面一個(gè)操作可見。?
1.程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happens- before 于該線程中的任意后續(xù)操作。?
2.監(jiān)視器鎖規(guī)則:對(duì)一個(gè)監(jiān)視器鎖的解鎖,happens- before 于隨后對(duì)這個(gè)監(jiān)視器鎖的加鎖。?
3.volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫,happens- before于任意后續(xù)對(duì)這個(gè)volatile域的讀。?
4.傳遞性:如果A happens- before B,且B happens- before C,那么A happens- before C。?
5.線程啟動(dòng)規(guī)則:Thread對(duì)象的start()方法happens- before于此線程的每一個(gè)動(dòng)作。
Volatile和Synchronized四個(gè)不同點(diǎn):?
1 粒度不同,前者針對(duì)變量 ,后者鎖對(duì)象和類?
2 syn阻塞,volatile線程不阻塞?
3 syn保證三大特性,volatile不保證原子性?
4 syn編譯器優(yōu)化,volatile不優(yōu)化?
volatile具備兩種特性:?
1. 保證此變量對(duì)所有線程的可見性,指一條線程修改了這個(gè)變量的值,新值對(duì)于其他線程來說是可見的,但并不是多線程安全的。?
2. 禁止指令重排序優(yōu)化。?
Volatile如何保證內(nèi)存可見性:?
1.當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存。?
2.當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量。
同步:就是一個(gè)任務(wù)的完成需要依賴另外一個(gè)任務(wù),只有等待被依賴的任務(wù)完成后,依賴任務(wù)才能完成。?
異步:不需要等待被依賴的任務(wù)完成,只是通知被依賴的任務(wù)要完成什么工作,只要自己任務(wù)完成了就算完成了,被依賴的任務(wù)是否完成會(huì)通知回來。(異步的特點(diǎn)就是通知)。?
打電話和發(fā)短信來比喻同步和異步操作。?
阻塞:CPU停下來等一個(gè)慢的操作完成以后,才會(huì)接著完成其他的工作。?
非阻塞:非阻塞就是在這個(gè)慢的執(zhí)行時(shí),CPU去做其他工作,等這個(gè)慢的完成后,CPU才會(huì)接著完成后續(xù)的操作。?
非阻塞會(huì)造成線程切換增加,增加CPU的使用時(shí)間能不能補(bǔ)償系統(tǒng)的切換成本需要考慮。
友情鏈接:Java并發(fā)編程之volatile關(guān)鍵字解析
CAS(Compare And Swap) 無鎖算法:?
CAS是樂觀鎖技術(shù),當(dāng)多個(gè)線程嘗試使用CAS同時(shí)更新同一個(gè)變量時(shí),只有其中一個(gè)線程能更新變量的值,而其它線程都失敗,失敗的線程并不會(huì)被掛起,而是被告知這次競(jìng)爭(zhēng)中失敗,并可以再次嘗試。CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。
友情鏈接:非阻塞同步算法與CAS(Compare and Swap)無鎖算法
線程池的作用:?
在程序啟動(dòng)的時(shí)候就創(chuàng)建若干線程來響應(yīng)處理,它們被稱為線程池,里面的線程叫工作線程?
第一:降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。?
第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到e線程創(chuàng)建就能立即執(zhí)行。?
第三:提高線程的可管理性。?
常用線程池:ExecutorService 是主要的實(shí)現(xiàn)類,其中常用的有?
Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。
友情鏈接:線程池原理
友情鏈接:線程池原理解析
類加載器工作機(jī)制:?
1.加載:將Java二進(jìn)制代碼導(dǎo)入jvm中,生成Class文件。?
2.連接:a)校驗(yàn):檢查載入Class文件數(shù)據(jù)的正確性 (檢查數(shù)據(jù)結(jié)構(gòu))b)準(zhǔn)備:給類的靜態(tài)變量分配存儲(chǔ)空間 c)解析:將符號(hào)引用轉(zhuǎn)成直接引用 (java類并不知道所引用的類的實(shí)際地址,因此只能使用符號(hào)引用來代替)
3:初始化:對(duì)類的靜態(tài)變量,靜態(tài)方法和靜態(tài)代碼塊執(zhí)行初始化工作。
雙親委派模型:類加載器收到類加載請(qǐng)求,首先將請(qǐng)求委派給父類加載器完成? 確保類的唯一性
用戶自定義加載器->應(yīng)用程序加載器-appClassLoad用于加載自定義類的加載器>擴(kuò)展類加載器-extClassLoad用于加載:%JDK%/jre/lib/ext目錄下的第三方j(luò)ar包中的類>啟動(dòng)類加載器bootstrapclassload?根類加載器:打印:null。用于加載Java類庫中的類。
友情鏈接:深入理解Java虛擬機(jī)筆記—雙親委派模型?
友情鏈接:JVM類加載的那些事
友情鏈接:JVM(1):Java 類的加載機(jī)制
一致性哈希:
Memcahed緩存:?
數(shù)據(jù)結(jié)構(gòu):key,value對(duì)?
使用方法:get,put等方法
友情鏈接:hashcode(),equal()方法深入解析
Redis數(shù)據(jù)結(jié)構(gòu): String—字符串(key-value 類型)?
Hash—字典(hashmap) Redis的哈希結(jié)構(gòu)可以使你像在數(shù)據(jù)庫中更新一個(gè)屬性一樣只修改某一項(xiàng)屬性值?
List—列表 實(shí)現(xiàn)消息隊(duì)列?
Set—集合 利用唯一性? ?Zset?
Sorted Set—有序集合 可以進(jìn)行排序?
可以實(shí)現(xiàn)數(shù)據(jù)持久化
友情鏈接:?Spring + Redis 實(shí)現(xiàn)數(shù)據(jù)的緩存
java自動(dòng)裝箱拆箱深入剖析
談?wù)凧ava反射機(jī)制
如何寫一個(gè)不可變類?
要?jiǎng)?chuàng)建不可變類,要實(shí)現(xiàn)下面幾個(gè)步驟:
索引:B+,B-,全文索引?
Mysql的索引是一個(gè)數(shù)據(jù)結(jié)構(gòu),旨在使數(shù)據(jù)庫高效的查找數(shù)據(jù)。?
常用的數(shù)據(jù)結(jié)構(gòu)是B+Tree,每個(gè)葉子節(jié)點(diǎn)不但存放了索引鍵的相關(guān)信息還增加了指向相鄰葉子節(jié)點(diǎn)的指針,這樣就形成了帶有順序訪問指針的B+Tree,做這個(gè)優(yōu)化的目的是提高不同區(qū)間訪問的性能。?
什么時(shí)候使用索引:?
1. 經(jīng)常出現(xiàn)在group by,order by和distinc關(guān)鍵字后面的字段?
2. 經(jīng)常與其他表進(jìn)行連接的表,在連接字段上應(yīng)該建立索引?
3. 經(jīng)常出現(xiàn)在Where子句中的字段?
4. 經(jīng)常出現(xiàn)用作查詢選擇的字段
友情鏈接:MySQL:InnoDB存儲(chǔ)引擎的B+樹索引算法
友情鏈接:MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理
Spring IOC (控制反轉(zhuǎn),依賴注入)
Spring支持三種依賴注入方式,分別是屬性(Setter方法)注入,構(gòu)造注入和對(duì)象注入。
在Spring中,那些組成應(yīng)用的主體及由Spring IOC容器所管理的對(duì)象被稱之為Bean。
Spring的IOC容器通過反射的機(jī)制實(shí)例化Bean并建立Bean之間的依賴關(guān)系。?
簡(jiǎn)單地講,Bean就是由Spring IOC容器初始化、裝配及被管理的對(duì)象。?
獲取Bean對(duì)象的過程,首先通過Resource加載配置文件并啟動(dòng)IOC容器,然后通過getBean方法獲取bean對(duì)象,就可以調(diào)用他的方法。?
Spring Bean的作用域:?
Singleton:Spring IOC容器中只有一個(gè)共享的Bean實(shí)例,一般都是Singleton作用域。?
Prototype:每一個(gè)請(qǐng)求,會(huì)產(chǎn)生一個(gè)新的Bean實(shí)例。?
Request:每一次http請(qǐng)求會(huì)產(chǎn)生一個(gè)新的Bean實(shí)例。
友情鏈接:?Spring框架IOC容器和AOP解析
友情鏈接:淺談Spring框架注解的用法分析
友情鏈接:關(guān)于Spring的69個(gè)面試問答——終極列表?
關(guān)于Spring的69個(gè)面試問答——終極列表
分享到: 本文由?ImportNew?-?人曉?翻譯自?javacodegeeks。歡迎加入翻譯小組。轉(zhuǎn)載請(qǐng)見文末要求。這篇文章總結(jié)了一些關(guān)于Spring框架的重要問題,這些問題都是你在面試或筆試過程中可能會(huì)被問到的。下次你再也不用擔(dān)心你的面試了,Java Code Geeks這就幫你解答。
大多數(shù)你可能被問到的問題都列舉在下面的列表中了。所有的核心模塊,從基礎(chǔ)的Spring功能(如Spring Beans)到上層的Spring MVC框架,文章中都會(huì)進(jìn)行簡(jiǎn)短的講解。看完這些面試問題,你應(yīng)該看看我們的Spring教程。
我們開始吧!
目錄
Spring概述
依賴注入
Spring Beans
Spring注解
Spring的對(duì)象訪問
Spring面向切面編程
Spring MVC框架
Spring概述
1.什么是Spring?
Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Java應(yīng)用程序中,但對(duì)Java EE平臺(tái)上的Web應(yīng)用程序有更好的擴(kuò)展性。Spring框架的目標(biāo)是使得Java EE應(yīng)用程序的開發(fā)更加簡(jiǎn)捷,通過使用POJO為基礎(chǔ)的編程模型促進(jìn)良好的編程風(fēng)格。
2.Spring有哪些優(yōu)點(diǎn)?
-
輕量級(jí):Spring在大小和透明性方面絕對(duì)屬于輕量級(jí)的,基礎(chǔ)版本的Spring框架大約只有2MB。
-
控制反轉(zhuǎn)(IOC):Spring使用控制反轉(zhuǎn)技術(shù)實(shí)現(xiàn)了松耦合。依賴被注入到對(duì)象,而不是創(chuàng)建或?qū)ふ乙蕾噷?duì)象。
-
面向切面編程(AOP):?Spring支持面向切面編程,同時(shí)把應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)的服務(wù)分離開來。
-
容器:Spring包含并管理應(yīng)用程序?qū)ο蟮呐渲眉吧芷凇?/span>
-
MVC框架:Spring的web框架是一個(gè)設(shè)計(jì)優(yōu)良的web MVC框架,很好的取代了一些web框架。
-
事務(wù)管理:Spring對(duì)下至本地業(yè)務(wù)上至全局業(yè)務(wù)(JAT)提供了統(tǒng)一的事務(wù)管理接口。
-
異常處理:Spring提供一個(gè)方便的API將特定技術(shù)的異常(由JDBC, Hibernate, 或JDO拋出)轉(zhuǎn)化為一致的、Unchecked異常。
代理的共有優(yōu)點(diǎn):業(yè)務(wù)類只需要關(guān)注業(yè)務(wù)邏輯本身,保證了業(yè)務(wù)類的重用性。?
Java靜態(tài)代理:?
代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)了相同的接口,目標(biāo)對(duì)象作為代理對(duì)象的一個(gè)屬性,具體接口實(shí)現(xiàn)中,代理對(duì)象可以在調(diào)用目標(biāo)對(duì)象相應(yīng)方法前后加上其他業(yè)務(wù)處理邏輯。?
缺點(diǎn):一個(gè)代理類只能代理一個(gè)業(yè)務(wù)類。如果業(yè)務(wù)類增加方法時(shí),相應(yīng)的代理類也要增加方法。?
Java動(dòng)態(tài)代理:?
Java動(dòng)態(tài)代理是寫一個(gè)類實(shí)現(xiàn)InvocationHandler接口,重寫Invoke方法,在Invoke方法可以進(jìn)行增強(qiáng)處理的邏輯的編寫,這個(gè)公共代理類在運(yùn)行的時(shí)候才能明確自己要代理的對(duì)象,同時(shí)可以實(shí)現(xiàn)該被代理類的方法的實(shí)現(xiàn),然后在實(shí)現(xiàn)類方法的時(shí)候可以進(jìn)行增強(qiáng)處理。?
實(shí)際上:代理對(duì)象的方法 = 增強(qiáng)處理 + 被代理對(duì)象的方法
JDK和CGLIB生成動(dòng)態(tài)代理類的區(qū)別:?
JDK動(dòng)態(tài)代理只能針對(duì)實(shí)現(xiàn)了接口的類生成代理(實(shí)例化一個(gè)類)。此時(shí)代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)了相同的接口,目標(biāo)對(duì)象作為代理對(duì)象的一個(gè)屬性,具體接口實(shí)現(xiàn)中,可以在調(diào)用目標(biāo)對(duì)象相應(yīng)方法前后加上其他業(yè)務(wù)處理邏輯?
CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類(沒有實(shí)例化一個(gè)類),覆蓋其中的方法 。?
Spring AOP應(yīng)用場(chǎng)景?
性能檢測(cè),訪問控制,日志管理,事務(wù)等。?
默認(rèn)的策略是如果目標(biāo)類實(shí)現(xiàn)接口,則使用JDK動(dòng)態(tài)代理技術(shù),如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,則默認(rèn)會(huì)采用CGLIB代理
SpringMVC運(yùn)行原理?
1. 客戶端請(qǐng)求提交到DispatcherServlet?
2. 由DispatcherServlet控制器查詢HandlerMapping,找到并分發(fā)到指定的Controller中。?
4. Controller調(diào)用業(yè)務(wù)邏輯處理后,返回ModelAndView?
5. DispatcherServlet查詢一個(gè)或多個(gè)ViewResoler視圖解析器,找到ModelAndView指定的視圖?
6. 視圖負(fù)責(zé)將結(jié)果顯示到客戶端
友情鏈接:Spring:基于注解的Spring MVC(上)
友情鏈接:?Spring:基于注解的Spring MVC(下)?
友情鏈接:SpringMVC與Struts2區(qū)別與比較總結(jié)
友情鏈接:SpringMVC與Struts2的對(duì)比?
一個(gè)Http請(qǐng)求?
DNS域名解析 –> 發(fā)起TCP的三次握手 –> 建立TCP連接后發(fā)起http請(qǐng)求 –> 服務(wù)器響應(yīng)http請(qǐng)求,瀏覽器得到html代碼 –> 瀏覽器解析html代碼,并請(qǐng)求html代碼中的資源(如javascript、css、圖片等) –> 瀏覽器對(duì)頁面進(jìn)行渲染呈現(xiàn)給用戶
設(shè)計(jì)存儲(chǔ)海量數(shù)據(jù)的存儲(chǔ)系統(tǒng):設(shè)計(jì)一個(gè)叫“中間層”的一個(gè)邏輯層,在這個(gè)層,將數(shù)據(jù)庫的海量數(shù)據(jù)抓出來,做成緩存,運(yùn)行在服務(wù)器的內(nèi)存中,同理,當(dāng)有新的數(shù)據(jù)到來,也先做成緩存,再想辦法,持久化到數(shù)據(jù)庫中,這是一個(gè)簡(jiǎn)單的思路。主要的步驟是負(fù)載均衡,將不同用戶的請(qǐng)求分發(fā)到不同的處理節(jié)點(diǎn)上,然后先存入緩存,定時(shí)向主數(shù)據(jù)庫更新數(shù)據(jù)。讀寫的過程采用類似樂觀鎖的機(jī)制,可以一直讀(在寫數(shù)據(jù)的時(shí)候也可以),但是每次讀的時(shí)候會(huì)有個(gè)版本的標(biāo)記,如果本次讀的版本低于緩存的版本,會(huì)重新讀數(shù)據(jù),這樣的情況并不多,可以忍受。
友情鏈接:?HTTP與HTTPS的區(qū)別
友情鏈接:?HTTPS 為什么更安全,先看這些?
友情鏈接:?HTTP請(qǐng)求報(bào)文和HTTP響應(yīng)報(bào)文
友情鏈接:?HTTP 請(qǐng)求方式: GET和POST的比較
什么是HTTP? 超文本傳輸協(xié)議(HyperText Transfer Protocol -- HTTP)是一個(gè)設(shè)計(jì)來使客戶端和服務(wù)器順利進(jìn)行通訊的協(xié)議。 HTTP在客戶端和服務(wù)器之間以request-response protocol(請(qǐng)求-回復(fù)協(xié)議)工作。 GET - 從指定的服務(wù)器中獲取數(shù)據(jù) POST - 提交數(shù)據(jù)給指定的服務(wù)器處理 GET方法: 使用GET方法時(shí),查詢字符串(鍵值對(duì))被附加在URL地址后面一起發(fā)送到服務(wù)器: /test/demo_form.jsp?name1=value1&name2=value2 特點(diǎn):- GET請(qǐng)求能夠被緩存
- GET請(qǐng)求會(huì)保存在瀏覽器的瀏覽記錄中
- 以GET請(qǐng)求的URL能夠保存為瀏覽器書簽
- GET請(qǐng)求有長(zhǎng)度限制
- GET請(qǐng)求主要用以獲取數(shù)據(jù)
- POST請(qǐng)求不能被緩存下來
- POST請(qǐng)求不會(huì)保存在瀏覽器瀏覽記錄中
- 以POST請(qǐng)求的URL無法保存為瀏覽器書簽
- POST請(qǐng)求沒有長(zhǎng)度限制
| ? | GET | POST |
| 點(diǎn)擊返回/刷新按鈕 | 沒有影響 | 數(shù)據(jù)會(huì)重新發(fā)送(瀏覽器將會(huì)提示用戶“數(shù)據(jù)被從新提交”) |
| 添加書簽 | 可以 | 不可以 |
| 緩存 | 可以 | 不可以 |
| 編碼類型(Encoding type) | application/x-www-form-urlencoded | application/x-www-form-urlencoded or multipart/form-data. 請(qǐng)為二進(jìn)制數(shù)據(jù)使用multipart編碼 |
| 歷史記錄 | 有 | 沒有 |
| 長(zhǎng)度限制 | 有 | 沒有 |
| 數(shù)據(jù)類型限制 | 只允許ASCII字符類型 | 沒有限制。允許二進(jìn)制數(shù)據(jù) |
| 安全性 | 查詢字符串會(huì)顯示在地址欄的URL中,不安全,請(qǐng)不要使用GET請(qǐng)求提交敏感數(shù)據(jù) | 因?yàn)閿?shù)據(jù)不會(huì)顯示在地址欄中,也不會(huì)緩存下來或保存在瀏覽記錄中,所以看POST求情比GET請(qǐng)求安全,但也不是最安全的方式。如需要傳送敏感數(shù)據(jù),請(qǐng)使用加密方式傳輸 |
| 可見性 | 查詢字符串顯示在地址欄的URL中,可見 | 查詢字符串不會(huì)顯示在地址欄中,不可見 |
| 方式 | 描述 |
| HEAD | 與GET請(qǐng)求類似,不同在與服務(wù)器只返回HTTP頭部信息,沒有頁面內(nèi)容 |
| PUT | 上傳指定URL的描述 |
| DELETE | 刪除指定資源 |
| OPTIONS | 返回服務(wù)器支持的HTTP方法 |
| CONNECT | 轉(zhuǎn)換為透明TCP/IP隧道的連接請(qǐng)求 |
Session與Cookie:Cookie可以讓服務(wù)端跟蹤每個(gè)客戶端的訪問,但是每次客戶端的訪問都必須傳回這些Cookie,如果Cookie很多,則無形的增加了客戶端與服務(wù)端的數(shù)據(jù)傳輸量,?
而Session則很好地解決了這個(gè)問題,同一個(gè)客戶端每次和服務(wù)端交互時(shí),將數(shù)據(jù)存儲(chǔ)通過Session到服務(wù)端,不需要每次都傳回所有的Cookie值,而是傳回一個(gè)ID,每個(gè)客戶端第一次訪問服務(wù)器生成的唯一的ID,客戶端只要傳回這個(gè)ID就行了,這個(gè)ID通常為NAME為JSESSIONID的一個(gè)Cookie。這樣服務(wù)端就可以通過這個(gè)ID,來將存儲(chǔ)到服務(wù)端的KV值取出了。?
Session和Cookie的超時(shí)問題,Cookie的安全問題
分布式Session框架?
1. 配置服務(wù)器,Zookeeper集群管理服務(wù)器可以統(tǒng)一管理所有服務(wù)器的配置文件?
2. 共享這些Session存儲(chǔ)在一個(gè)分布式緩存中,可以隨時(shí)寫入和讀取,而且性能要很好,如Memcache,Tair。?
3. 封裝一個(gè)類繼承自HttpSession,將Session存入到這個(gè)類中然后再存入分布式緩存中?
4. 由于Cookie不能跨域訪問,要實(shí)現(xiàn)Session同步,要同步SessionID寫到不同域名下。
適配器模式:將一個(gè)接口適配到另一個(gè)接口,Java I/O中InputStreamReader將Reader類適配到InputStream,從而實(shí)現(xiàn)了字節(jié)流到字符流的準(zhǔn)換。?
裝飾者模式:保持原來的接口,增強(qiáng)原來有的功能。?
FileInputStream 實(shí)現(xiàn)了InputStream的所有接口,BufferedInputStreams繼承自FileInputStream是具體的裝飾器實(shí)現(xiàn)者,將InputStream讀取的內(nèi)容保存在內(nèi)存中,而提高讀取的性能。
Spring事務(wù)配置方法:?
1. 切點(diǎn)信息,用于定位實(shí)施事物切面的業(yè)務(wù)類方法?
2. 控制事務(wù)行為的事務(wù)屬性,這些屬性包括事物隔離級(jí)別,事務(wù)傳播行為,超時(shí)時(shí)間,回滾規(guī)則。?
Spring通過aop/tx Schema 命名空間和@Transaction注解技術(shù)來進(jìn)行聲明式事物配置。
Mybatis?
每一個(gè)Mybatis的應(yīng)用程序都以一個(gè)SqlSessionFactory對(duì)象的實(shí)例為核心。首先用字節(jié)流通過Resource將配置文件讀入,然后通過SqlSessionFactoryBuilder().build方法創(chuàng)建SqlSessionFactory,然后再通過SqlSessionFactory.openSession()方法創(chuàng)建一個(gè)SqlSession為每一個(gè)數(shù)據(jù)庫事務(wù)服務(wù)。?
經(jīng)歷了Mybatis初始化 –>創(chuàng)建SqlSession –>運(yùn)行SQL語句,返回結(jié)果三個(gè)過程
Servlet和Filter的區(qū)別:?
整個(gè)流程是:Filter對(duì)用戶請(qǐng)求進(jìn)行預(yù)處理,接著將請(qǐng)求交給Servlet進(jìn)行處理并生成響應(yīng),最后Filter再對(duì)服務(wù)器響應(yīng)進(jìn)行后處理。
Filter有如下幾個(gè)用處:?
Filter可以進(jìn)行對(duì)特定的url請(qǐng)求和相應(yīng)做預(yù)處理和后處理。?
在HttpServletRequest到達(dá)Servlet之前,攔截客戶的HttpServletRequest。?
根據(jù)需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和數(shù)據(jù)。?
在HttpServletResponse到達(dá)客戶端之前,攔截HttpServletResponse。?
根據(jù)需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和數(shù)據(jù)。
實(shí)際上Filter和Servlet極其相似,區(qū)別只是Filter不能直接對(duì)用戶生成響應(yīng)。實(shí)際上Filter里doFilter()方法里的代碼就是從多個(gè)Servlet的service()方法里抽取的通用代碼,通過使用Filter可以實(shí)現(xiàn)更好的復(fù)用。
Filter和Servlet的生命周期:?
1.Filter在web服務(wù)器啟動(dòng)時(shí)初始化?
2.如果某個(gè)Servlet在web.xml配置了 1 ,(配置<load onstartup>)該Servlet也是在Tomcat(Servlet容器)啟動(dòng)時(shí)初始化。?
3.如果Servlet沒有配置1 ,該Servlet不會(huì)在Tomcat啟動(dòng)時(shí)初始化,而是在請(qǐng)求到來時(shí)初始化。?
4.每次請(qǐng)求, Request都會(huì)被初始化,響應(yīng)請(qǐng)求后,請(qǐng)求被銷毀。 ? 每次請(qǐng)求都會(huì)調(diào)用 servlet 單帶service()方法
5.Servlet初始化后,將不會(huì)隨著請(qǐng)求的結(jié)束而注銷。?
6.關(guān)閉Tomcat時(shí),Servlet、Filter依次被注銷。
HashMap與HashTable的區(qū)別。?
1、HashMap是非線程安全的,HashTable是線程安全的。?
2、HashMap的鍵和值都允許有null值存在,而HashTable則不行。?
3、因?yàn)榫€程安全的問題,HashMap效率比HashTable的要高。
HashMap的實(shí)現(xiàn)機(jī)制:?
1. 維護(hù)一個(gè)每個(gè)元素是一個(gè)鏈表的數(shù)組,而且鏈表中的每個(gè)節(jié)點(diǎn)是一個(gè)Entry[]鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu)。?
2. 實(shí)現(xiàn)了數(shù)組+鏈表的特性,查找快,插入刪除也快。?
3. 對(duì)于每個(gè)key,他對(duì)應(yīng)的數(shù)組索引下標(biāo)是 int i = hash(key.hashcode)&(len-1);?
4. 每個(gè)新加入的節(jié)點(diǎn)放在鏈表首,然后該新加入的節(jié)點(diǎn)指向原鏈表首 ? ? ? ? ? ? ? ? ? ? 擴(kuò)容2倍擴(kuò)容?在插入元素后判斷元素是否已經(jīng)到達(dá)容量的,如果到達(dá)了就進(jìn)行擴(kuò)容,但是很有可能擴(kuò)容之后沒有新元素插入,這時(shí)HashMap就進(jìn)行了一次無效的擴(kuò)容。
?containsKey 和?containsvalue?
containskey ?可以用于統(tǒng)計(jì)文本中 某個(gè)字符串的個(gè)數(shù).
面試題: hashMap是怎樣實(shí)現(xiàn)key-value這樣鍵值對(duì)的保存?
?
HashMap中有一個(gè)內(nèi)部類Entry,
?
?
?
| 1 2 3 4 5 6 7 | static?class?Entry<k,v> implements?Map.Entry<k,v> { ????????final?K key; ????????V value; ????????Entry<k,v> next; ????????int?hash; ????????//..... }</k,v></k,v></k,v> |
?
主要有4個(gè)屬性,key ,hash,value,指向下一個(gè)節(jié)點(diǎn)的引用next ,看到這個(gè)實(shí)體類就明白了,在HashMap中存放的key-value實(shí)質(zhì)是通過實(shí)體類Entry來保存的
?
?
?
面試題: hashMap的實(shí)現(xiàn)原理?
?
HashMap使用到的數(shù)據(jù)類型主要就是數(shù)組和鏈表,首先看原理圖
?
?
查 看?
?
?
在hashMap的原理圖中,左邊是通過數(shù)組來存放鏈表的第一個(gè)節(jié)點(diǎn),看懂這個(gè)圖這個(gè)問題就ok
?
面試題: hashMap的put過程?
?
面我們提到過Entry類里面有一個(gè)next屬性,作用是指向下一個(gè)Entry。比如說: 第一個(gè)鍵值對(duì)A進(jìn)來,通過計(jì)算其key的hash得到的index=0,記做:Entry[0] = A。一會(huì)后又進(jìn)來一個(gè)鍵值對(duì)B,通過計(jì)算其index也等于0,現(xiàn)在怎么辦?HashMap會(huì)這樣做:B.next = A,Entry[0] = B,如果又進(jìn)來C,index也等于0,那么C.next = B,Entry[0] = C;這樣我們發(fā)現(xiàn)index=0的地方其實(shí)存取了A,B,C三個(gè)鍵值對(duì),他們通過next這個(gè)屬性鏈接在一起。也就是說數(shù)組中存儲(chǔ)的是最后插入的元素。
?
?
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public?V put(K key, V value) { ????if?(key == null) ????????return?putForNullKey(value); ????int?hash = hash(key); ????int?i = indexFor(hash, table.length); ????for?(Entry<k,v> e = table[i]; e != null; e = e.next) {//循環(huán)判斷插入的key是否已經(jīng)存在,若存在就更新key對(duì)應(yīng)的value ????????Object k; ????????if?(e.hash == hash && ((k = e.key) == key || key.equals(k))) { ????????????V oldValue = e.value; ????????????e.value = value; ????????????e.recordAccess(this); ????????????return?oldValue; ????????} ????} ????modCount++; ????addEntry(hash, key, value, i);//key不存在,那么插入新的key-value ????return?null; }</k,v> |
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void?addEntry(int?hash, K key, V value, int?bucketIndex) { ????if?((size >= threshold) && (null?!= table[bucketIndex])) { ????????resize(2?* table.length); ????????hash = (null?!= key) ? hash(key) : 0; ????????bucketIndex = indexFor(hash, table.length); ????} ????createEntry(hash, key, value, bucketIndex); } void?createEntry(int?hash, K key, V value, int?bucketIndex) {//這個(gè)方法就驗(yàn)證了上面說的<strong>數(shù)組中存儲(chǔ)的是最后插入的元素</strong> ????Entry<k,v> e = table[bucketIndex]; ????table[bucketIndex] = new?Entry<>(hash, key, value, e); ????size++; }</k,v> |
?
面試題: hashMap的get過程?
這個(gè)過程比較簡(jiǎn)單,直接看代碼:
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public?V get(Object key) { ????????if?(key == null) ????????????return?getForNullKey(); ????????int?hash = hash(key.hashCode()); ????????//先定位到數(shù)組元素,再遍歷該元素處的鏈表 ????????for?(Entry<k,v> e = table[indexFor(hash, table.length)]; ?????????????e != null; ?????????????e = e.next) { ????????????Object k; ????????????if?(e.hash == hash && ((k = e.key) == key || key.equals(k))) ????????????????return?e.value; ????????} ????????return?null; }</k,v> |
?HashMap其實(shí)就是一個(gè)Entry數(shù)組,Entry對(duì)象中包含了鍵和值,其中next也是一個(gè)Entry對(duì)象,它就是用來處理hash沖突的,形成一個(gè)鏈表。
HashMap的底層主要是基于數(shù)組和鏈表來實(shí)現(xiàn)的,它之所以有相當(dāng)快的查詢速度主要是因?yàn)樗峭ㄟ^計(jì)算散列碼來決定存儲(chǔ)的位置。HashMap中主要是通過key的hashCode來計(jì)算hash值的,只要hashCode相]同,計(jì)算出來的hash值就一樣。如果存儲(chǔ)的對(duì)象對(duì)多了,就有可能不同的對(duì)象所算出來的hash值是相同的,這就出現(xiàn)了所謂的hash沖突。學(xué)過數(shù)據(jù)結(jié)構(gòu)的同學(xué)都知道,解決hash沖突的方法有很多,HashMap底層是通過鏈表來解決hash沖突的。
?
?圖中,紫色部分即代表哈希表,也稱為哈希數(shù)組,數(shù)組的每個(gè)元素都是一個(gè)單鏈表的頭節(jié)點(diǎn),鏈表是用來解決沖突的,如果不同的key映射到了數(shù)組的同一位置處,就將其放入單鏈表中。
保證hashmap線程安全
方法一:
優(yōu)點(diǎn):代碼實(shí)現(xiàn)十分簡(jiǎn)單,一看就懂.
缺點(diǎn):從鎖的角度來看,方法一直接使用了鎖住方法,基本上是鎖住了盡可能大的代碼塊.性能會(huì)比較差.
?
方法二:
優(yōu)點(diǎn):需要互斥的代碼段比較少,性能會(huì)比較好. ConcurrentHashMap把整個(gè)Map切分成了多個(gè)塊,發(fā)生鎖碰撞的幾率大大降低,性能會(huì)比較好.
缺點(diǎn):代碼實(shí)現(xiàn)稍稍復(fù)雜些.
HashMap和TreeMap區(qū)別?
友情鏈接:?Java中HashMap和TreeMap的區(qū)別深入理解
HashMap沖突
友情鏈接:?HashMap沖突的解決方法以及原理分析
友情鏈接:?HashMap的工作原理
友情鏈接:?HashMap和Hashtable的區(qū)別
友情鏈接:?2種辦法讓HashMap線程安全
HashMap,ConcurrentHashMap與LinkedHashMap的區(qū)別
ConcurrentHashMap應(yīng)用場(chǎng)景
1:ConcurrentHashMap的應(yīng)用場(chǎng)景是高并發(fā),但是并不能保證線程安全,而同步的HashMap的是鎖住整個(gè)容器,而加鎖之后ConcurrentHashMap不需要鎖住整個(gè)容器,只需要鎖住對(duì)應(yīng)的Segment就好了,所以可以保證高并發(fā)同步訪問,提升了效率。
2:可以多線程寫。?
ConcurrentHashMap把HashMap分成若干個(gè)Segmenet?
1.get時(shí),不加鎖,先定位到segment然后在找到頭結(jié)點(diǎn)進(jìn)行讀取操作。而value是volatile變量,所以可以保證在競(jìng)爭(zhēng)條件時(shí)保證讀取最新的值,如果讀到的value是null,則可能正在修改,那么就調(diào)用ReadValueUnderLock函數(shù),加鎖保證讀到的數(shù)據(jù)是正確的。?
2.Put時(shí)會(huì)加鎖,一律添加到hash鏈的頭部。?
3.Remove時(shí)也會(huì)加鎖,由于next是final類型不可改變,所以必須把刪除的節(jié)點(diǎn)之前的節(jié)點(diǎn)都復(fù)制一遍。?
4.ConcurrentHashMap允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù)。它使用了多個(gè)鎖來控制對(duì)Hash表的不同Segment進(jìn)行的修改。
ConcurrentHashMap能夠保證每一次調(diào)用都是原子操作,但是并不保證多次調(diào)用之間也是原子操作。
友情鏈接:Java集合—ConcurrentHashMap原理分析
Vector和ArrayList的區(qū)別
友情鏈接:Java中Vector和ArrayList的區(qū)別
ExecutorService service = Executors….?
ExecutorService service = new ThreadPoolExecutor()?
ExecutorService service = new ScheduledThreadPoolExecutor();
ThreadPoolExecutor源碼分析
線程池本身的狀態(tài):
等待任務(wù)隊(duì)列和工作集: 正在被執(zhí)行的hashset集合 等待被執(zhí)行的是 阻塞隊(duì)列
線程池的主要狀態(tài)鎖:
線程池的存活時(shí)間和大小:
1.2 ThreadPoolExecutor 的內(nèi)部工作原理?
有了以上定義好的數(shù)據(jù),下面來看看內(nèi)部是如何實(shí)現(xiàn)的 。 Doug Lea 的整個(gè)思路總結(jié)起來就是 5 句話:?
1. 如果當(dāng)前池大小 poolSize 小于 corePoolSize ,則創(chuàng)建新線程執(zhí)行任務(wù)。?
2. 如果當(dāng)前池大小 poolSize 大于 corePoolSize ,且等待隊(duì)列未滿,則進(jìn)入等待隊(duì)列?
3. 如果當(dāng)前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待隊(duì)列已滿,則創(chuàng)建新線程執(zhí)行任務(wù)。?
4. 如果當(dāng)前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待隊(duì)列已滿,則調(diào)用拒絕策略來處理該任務(wù)。?
5. 線程池里的每個(gè)線程執(zhí)行完任務(wù)后不會(huì)立刻退出,而是會(huì)去檢查下等待隊(duì)列里是否還有線程任務(wù)需要執(zhí)行,如果在 keepAliveTime 里等不到新的任務(wù)了,那么線程就會(huì)退出。
Executor包結(jié)構(gòu)
CopyOnWriteArrayList : 寫時(shí)加鎖,當(dāng)添加一個(gè)元素的時(shí)候,將原來的容器進(jìn)行copy,復(fù)制出一個(gè)新的容器,然后在新的容器里面寫,寫完之后再將原容器的引用指向新的容器,而讀的時(shí)候是讀舊容器的數(shù)據(jù),所以可以進(jìn)行并發(fā)的讀,但這是一種弱一致性的策略。?
使用場(chǎng)景:CopyOnWriteArrayList適合使用在讀操作遠(yuǎn)遠(yuǎn)大于寫操作的場(chǎng)景里,比如緩存。
Linux常用命令:cd,cp,mv,rm,ps(進(jìn)程),tar,cat(查看內(nèi)容),chmod,vim,find,ls
死鎖的必要條件?
1. 互斥 至少有一個(gè)資源處于非共享狀態(tài)?
2. 占有并等待?
3. 非搶占?
4. 循環(huán)等待?
解決死鎖,第一個(gè)是死鎖預(yù)防,就是不讓上面的四個(gè)條件同時(shí)成立。二是,合理分配資源。?
三是使用銀行家算法,如果該進(jìn)程請(qǐng)求的資源操作系統(tǒng)剩余量可以滿足,那么就分配。
進(jìn)程間的通信方式
進(jìn)程與線程的區(qū)別和聯(lián)系
進(jìn)程概念
進(jìn)程是表示資源分配的基本單位,又是調(diào)度運(yùn)行的基本單位。例如,用戶運(yùn)行自己的程序,系統(tǒng)就創(chuàng)建一個(gè)進(jìn)程,并為它分配資源,包括各種表格、內(nèi)存空間、磁盤空間、(讀寫)I/O設(shè)備等.
線程概念
線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位,亦即CPU執(zhí)行處理機(jī)調(diào)度的基本單位。如果把進(jìn)程理解為在邏輯上操作系統(tǒng)所完成的任務(wù),那么線程表示完成該任務(wù)的許多可能的子任務(wù)之一。例如,假設(shè)用戶啟動(dòng)了一個(gè)窗口中的數(shù)據(jù)庫應(yīng)用程序,操作系統(tǒng)就將對(duì)數(shù)據(jù)庫的調(diào)用表示為一個(gè)進(jìn)程。假設(shè)用戶要從數(shù)據(jù)庫中產(chǎn)生一份工資單報(bào)表,并傳到一個(gè)文件中,這是一個(gè)子任務(wù);在產(chǎn)生工資單報(bào)表的過程中,用戶又可以輸人數(shù)據(jù)庫查詢請(qǐng)求,這又是一個(gè)子任務(wù)。這樣,操作系統(tǒng)則把每一個(gè)請(qǐng)求――工資單報(bào)表和新輸人的數(shù)據(jù)查詢表示為數(shù)據(jù)庫進(jìn)程中的獨(dú)立的線程。線程可以在處理器上獨(dú)立調(diào)度執(zhí)行,這樣,在多處理器環(huán)境下就允許幾個(gè)線程各自在單獨(dú)處理器上進(jìn)行
?
引入線程的好處
(1)易于調(diào)度。
(2)提高并發(fā)性。通過線程可方便有效地實(shí)現(xiàn)并發(fā)性。進(jìn)程可創(chuàng)建多個(gè)線程來執(zhí)行同一程序的不同部分。
(3)開銷少。創(chuàng)建線程比創(chuàng)建進(jìn)程要快,所需開銷很少。。
(4)利于充分發(fā)揮多處理器的功能。通過創(chuàng)建多線程進(jìn)程(即一個(gè)進(jìn)程可具有兩個(gè)或更多個(gè)線程),每個(gè)線程在一個(gè)處理器上運(yùn)行,從而實(shí)現(xiàn)應(yīng)用程序的并發(fā)性,使每個(gè)處理器都得到充分運(yùn)行。(例如多核CPU)進(jìn)程和線程的關(guān)系
(1)一個(gè)線程只能屬于一個(gè)進(jìn)程,而一個(gè)進(jìn)程可以有多個(gè)線程,但至少有一個(gè)線程。線程是操作系統(tǒng)可識(shí)別的最小執(zhí)行和調(diào)度單位。
(2)資源分配給進(jìn)程,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源。 同一進(jìn)程中的多個(gè)線程共享代碼段(代碼和常量),數(shù)據(jù)段(全局變量和靜態(tài)變量),擴(kuò)展段(堆存儲(chǔ))。但是每個(gè)線程擁有自己的棧段,棧段又叫運(yùn)行時(shí)段,用來存放所有局部變量和臨時(shí)變量。
(3)處理機(jī)分給線程,即真正在處理機(jī)上運(yùn)行的是線程。
(4)線程在執(zhí)行過程中,需要協(xié)作同步。不同進(jìn)程的線程間要利用消息通信的辦法實(shí)現(xiàn)同步。
?
線程與進(jìn)程的比較
線程具有許多傳統(tǒng)進(jìn)程所具有的特征,故又稱為輕型進(jìn)程(Light—Weight Process)或進(jìn)程元;而把傳統(tǒng)的進(jìn)程稱為重型進(jìn)程(Heavy—Weight Process),它相當(dāng)于只有一個(gè)線程的任務(wù)。在引入了線程的操作系統(tǒng)中,通常一個(gè)進(jìn)程都有若干個(gè)線程,至少需要一個(gè)線程。下面,我們從調(diào)度、并發(fā)性、 系統(tǒng)開銷、擁有資源等方面,來比較線程與進(jìn)程。
1.調(diào)度
在傳統(tǒng)的操作系統(tǒng)中,擁有資源的基本單位和獨(dú)立調(diào)度、分派的基本單位都是進(jìn)程。而在引入線程的操作系統(tǒng)中,則把線程作為調(diào)度和分派的基本單位。而把進(jìn)程作 為資源擁有的基本單位,使傳統(tǒng)進(jìn)程的兩個(gè)屬性分開,線程便能輕裝運(yùn)行,從而可顯著地提高系統(tǒng)的并發(fā)程度。在同一進(jìn)程中,線程的切換不會(huì)引起進(jìn)程的切換,在 由一個(gè)進(jìn)程中的線程切換到另一個(gè)進(jìn)程中的線程時(shí),將會(huì)引起進(jìn)程的切換。
2.并發(fā)性
在引入線程的操作系統(tǒng)中,不僅進(jìn)程之間可以并發(fā)執(zhí)行,而且在一個(gè)進(jìn)程中的多個(gè)線程之間,亦可并發(fā)執(zhí)行,因而使操作系統(tǒng)具有更好的并發(fā)性,從而能更有效地使 用系統(tǒng)資源和提高系統(tǒng)吞吐量。例如,在一個(gè)未引入線程的單CPU操作系統(tǒng)中,若僅設(shè)置一個(gè)文件服務(wù)進(jìn)程,當(dāng)它由于某種原因而被阻塞時(shí),便沒有其它的文件服 務(wù)進(jìn)程來提供服務(wù)。在引入了線程的操作系統(tǒng)中,可以在一個(gè)文件服務(wù)進(jìn)程中,設(shè)置多個(gè)服務(wù)線程,當(dāng)?shù)谝粋€(gè)線程等待時(shí),文件服務(wù)進(jìn)程中的第二個(gè)線程可以繼續(xù)運(yùn) 行;當(dāng)?shù)诙€(gè)線程阻塞時(shí),第三個(gè)線程可以繼續(xù)執(zhí)行,從而顯著地提高了文件服務(wù)的質(zhì)量以及系統(tǒng)吞吐量。
3.擁有資源
不論是傳統(tǒng)的操作系統(tǒng),還是設(shè)有線程的操作系統(tǒng),進(jìn)程都是擁有資源的一個(gè)獨(dú)立單位,它可以擁有自己的資源。一般地說,線程自己不擁有系統(tǒng)資源(也有一點(diǎn)必 不可少的資源),但它可以訪問其隸屬進(jìn)程的資源。亦即,一個(gè)進(jìn)程的代碼段、數(shù)據(jù)段以及系統(tǒng)資源,如已打開的文件、I/O設(shè)備等,可供問一進(jìn)程的其它所有線 程共享。
4.系統(tǒng)開銷
由于在創(chuàng)建或撤消進(jìn)程時(shí),系統(tǒng)都要為之分配或回收資源,如內(nèi)存空間、I/o設(shè)備等。因此,操作系統(tǒng)所付出的開銷將顯著地大于在創(chuàng)建或撤消線程時(shí)的開銷。類 似地,在進(jìn)行進(jìn)程切換時(shí),涉及到整個(gè)當(dāng)前進(jìn)程CPU環(huán)境的保存以及新被調(diào)度運(yùn)行的進(jìn)程的CPU環(huán)境的設(shè)置。而線程切換只須保存和設(shè)置少量寄存器的內(nèi)容,并 不涉及存儲(chǔ)器管理方面的操作。可見,進(jìn)程切換的開銷也遠(yuǎn)大于線程切換的開銷。此外,由于同一進(jìn)程中的多個(gè)線程具有相同的地址空間,致使它們之間的同步和通信的實(shí)現(xiàn),也變得比較容易。在有的系統(tǒng)中,線程的切換、同步和通信都無須
?
操作系統(tǒng)的進(jìn)程調(diào)度算法
計(jì)算機(jī)系統(tǒng)的層次存儲(chǔ)結(jié)構(gòu)詳解
數(shù)據(jù)庫事務(wù)是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作。
友情鏈接:數(shù)據(jù)庫事務(wù)的四大特性以及事務(wù)的隔離級(jí)別
MySQL數(shù)據(jù)庫優(yōu)化總結(jié)
MYSQL 優(yōu)化常用方法
MySQL存儲(chǔ)引擎--MyISAM與InnoDB區(qū)別
關(guān)于SQL數(shù)據(jù)庫中的范式
◆ 第一范式(1NF):強(qiáng)調(diào)的是列的原子性,即列不能夠再分成其他幾列。
◆ 第二范式(2NF):首先是 1NF,另外包含兩部分內(nèi)容,一是表必須有一個(gè)主鍵;二是沒有包含在主鍵中的列必須完全依賴于主鍵,而不能只依賴于主鍵的一部分。
◆ 第三范式(3NF):首先是 2NF,另外非主鍵列必須直接依賴于主鍵,不能存在傳遞依賴。即不能存在:非主鍵列 A 依賴于非主鍵列 B,非主鍵列 B 依賴于主鍵的情況。
第二范式(2NF)和第三范式(3NF)的概念很容易混淆,區(qū)分它們的關(guān)鍵點(diǎn)在于,2NF:非主鍵列是否完全依賴于主鍵,還是依賴于主鍵的一部分;3NF:非主鍵列是直接依賴于主鍵,還是直接依賴于非主鍵列。
Hibernate的一級(jí)緩存是由Session提供的,因此它只存在于Session的生命周期中,當(dāng)程序調(diào)用save(),update(),saveOrUpdate()等方法 及調(diào)用查詢接口list,filter,iterate時(shí),如Session緩存中還不存在相應(yīng)的對(duì)象,Hibernate會(huì)把該對(duì)象加入到一級(jí)緩存中,當(dāng)Session關(guān)閉的時(shí)候緩存也會(huì)消失。?
Hibernate的一級(jí)緩存是Session所內(nèi)置的,不能被卸載,也不能進(jìn)行任何配置一級(jí)緩存采用的是key-value的Map方式來實(shí)現(xiàn)的,在緩存實(shí)體對(duì)象時(shí),對(duì)象的主關(guān)鍵字ID是Map的key,實(shí)體對(duì)象就是對(duì)應(yīng)的值。?
Hibernate二級(jí)緩存:把獲得的所有數(shù)據(jù)對(duì)象根據(jù)ID放入到第二級(jí)緩存中。Hibernate二級(jí)緩存策略,是針對(duì)于ID查詢的緩存策略,刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。
更新于2017/3/9
Java I/O 總結(jié)
程序需要讀取數(shù)據(jù)的時(shí)候,就會(huì)建立一個(gè)通向數(shù)據(jù)源的連接,這個(gè)數(shù)據(jù)源可以是文件,內(nèi)存,或是網(wǎng)絡(luò)連接。類似的,當(dāng)程序需要寫入數(shù)據(jù)的時(shí)候,就會(huì)建立一個(gè)通向目的地的連接。
是,就使用轉(zhuǎn)換流,從Stream轉(zhuǎn)化為Reader、Writer:InputStreamReader,OutputStreamWriter
是就加上Buffered:BufferedInputStream, BufferedOuputStream, BufferedReader,?
輸入:輸入流 InputStream Reader
輸出:輸出流 OutputStream Writer
是:字符流 Reader,Writer
否:字節(jié)流 InputStream,OutputStream
- 文件:
讀:FileInputStream,, FileReader,
寫:FileOutputStream,FileWriter- ? ?讀的時(shí)候需要判斷是否讀到-1 證明讀完 ? 寫的時(shí)候如果是Buffered 需要flush 才能寫入 因?yàn)橛芯彌_區(qū) 或者close
JVM(8):JVM知識(shí)點(diǎn)總覽-高級(jí)Java工程師面試必備
類加載
- 加載,查找并加載類的二進(jìn)制數(shù)據(jù),在Java堆中也創(chuàng)建一個(gè)java.lang.Class類的對(duì)象
- 連接,連接又包含三塊內(nèi)容:驗(yàn)證、準(zhǔn)備、初始化。1)驗(yàn)證,文件格式、元數(shù)據(jù)、字節(jié)碼、符號(hào)引用驗(yàn)證;2)準(zhǔn)備,為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值;3)解析,把類中的符號(hào)引用轉(zhuǎn)換為直接引用
- 初始化,為類的靜態(tài)變量賦予正確的初始值
- 使用,new出對(duì)象程序中使用
- 卸載,執(zhí)行垃圾回收
類初始化時(shí)機(jī) ?
、創(chuàng)建類的實(shí)例。例如new語句創(chuàng)建實(shí)例,或者通過反射、克隆及序列號(hào)手段來創(chuàng)建實(shí)例。
2、調(diào)用類的靜態(tài)方法。
3、訪問某個(gè)類或接口的靜態(tài)變量或者對(duì)該靜態(tài)變量賦值。
4、調(diào)用java API中某些反射方法,比如調(diào)用Class.forName("Worker")方法,加入Worker類還沒有被初始化,那么forName()方法就會(huì)初始化Worker類,然后返回代表這個(gè)Worker類的Class實(shí)例。forName()方法是java.lang.Class類的靜態(tài)方法。
5、初始化一個(gè)類的子類。例如對(duì)Sub類的初始化,可看作是它對(duì)父類Base類的主動(dòng)使用,因此會(huì)先初始化Base類。
6、java虛擬機(jī)啟動(dòng)時(shí)被注明為啟動(dòng)類的類。例如對(duì)于“java Sample”命令,Sample類就是啟動(dòng)類,java虛擬機(jī)會(huì)先初始化它。
?在如下幾種情況下,Java虛擬機(jī)將結(jié)束生命周期
– 執(zhí)行了System.exit()方法
– 程序正常執(zhí)行結(jié)束
– 程序在執(zhí)行過程中遇到了異常或錯(cuò)誤而異常終止
– 由于操作系統(tǒng)出現(xiàn)錯(cuò)誤而導(dǎo)致Java虛擬機(jī)進(jìn)程終止
類加載器
- 啟動(dòng)類加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫
- 擴(kuò)展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載DK\jre\lib\ext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器。
- 應(yīng)用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器
類加載機(jī)制
- 全盤負(fù)責(zé),當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class時(shí),該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯示使用另外一個(gè)類加載器來載入
- 父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時(shí)才嘗試從自己的類路徑中加載該類
- 緩存機(jī)制,緩存機(jī)制將會(huì)保證所有加載過的Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從緩存區(qū)尋找該Class,只有緩存區(qū)不存在,系統(tǒng)才會(huì)讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成Class對(duì)象,存入緩存區(qū)。這就是為什么修改了Class后,必須重啟JVM,程序的修改才會(huì)生效
細(xì)數(shù)JDK里的設(shè)計(jì)模式
Java中創(chuàng)建對(duì)象的5種不同方法
關(guān)于Java Collections的幾個(gè)常見問題
類在什么時(shí)候加載和初始化
兩個(gè)棧實(shí)現(xiàn)隊(duì)列 兩個(gè)隊(duì)列實(shí)現(xiàn)棧
更新于2017/3/12
java collection.sort()根據(jù)時(shí)間排序list
單點(diǎn)登錄原理與簡(jiǎn)單實(shí)現(xiàn)
更新于2017/3/13
AQS詳解
Java的concurrent包
Java 并發(fā)工具包 java.util.concurrent 用戶指南
更新于2017/6/12
進(jìn)程和線程的區(qū)別:
進(jìn)程:每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文),進(jìn)程間的切換會(huì)有較大的開銷,一個(gè)進(jìn)程包含1–n個(gè)線程。
線程:同一類線程共享代碼和數(shù)據(jù)空間,每個(gè)線程有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),線程切換開銷小。
線程和進(jìn)程一樣分為五個(gè)階段:創(chuàng)建、就緒、運(yùn)行、阻塞、終止。
多進(jìn)程是指操作系統(tǒng)能同時(shí)運(yùn)行多個(gè)任務(wù)(程序)。
多線程是指在同一程序中有多個(gè)順序流在執(zhí)行。
在java中要想實(shí)現(xiàn)多線程,有三種手段,一種是繼續(xù)Thread類,另外一種是實(shí)現(xiàn)Runable接口,還有就是實(shí)現(xiàn)Callable接口。
Switch能否用string做參數(shù)?
a.在 Java 7 之前, switch 只能支持byte,short,char,int 或者其對(duì)應(yīng)的封裝類以及 Enum 類型。在Java 7中,String 支持被加上了。
Object有哪些公用方法?
a.方法equals測(cè)試的是兩個(gè)對(duì)象是否相等
b.方法clone進(jìn)行對(duì)象拷貝
c.方法getClass返回和當(dāng)前對(duì)象相關(guān)的Class對(duì)象
d.方法notify,notifyall,wait都是用來對(duì)給定對(duì)象進(jìn)行線程同步的
e.toString ?方法
Java的四種引用,強(qiáng)弱軟虛,以及用到的場(chǎng)景
a.利用軟引用和弱引用解決OOM問題:用一個(gè)HashMap來保存圖片的路徑和相應(yīng)圖片對(duì)象關(guān)聯(lián)的軟引用之間的映射關(guān)系,在內(nèi)存不足時(shí),JVM會(huì)自動(dòng)回收這些緩存圖片對(duì)象所占用的空間,從而有效地避免了OOM的問題。
b.通過軟可及對(duì)象重獲方法實(shí)現(xiàn)Java對(duì)象的高速緩存:比如我們創(chuàng)建了一Employee的類,如果每次需要查詢一個(gè)雇員的信息。哪怕是幾秒中之前剛剛查詢過的,都要重新構(gòu)建一個(gè)實(shí)例,這是需要消耗很多時(shí)間的。我們可以通過軟引用和 HashMap 的結(jié)合,先是保存引用方面:以軟引用的方式對(duì)一個(gè)Employee對(duì)象的實(shí)例進(jìn)行引用并保存該引用到HashMap 上,key 為此雇員的 id,value為這個(gè)對(duì)象的軟引用,另一方面是取出引用,緩存中是否有該Employee實(shí)例的軟引用,如果有,從軟引用中取得。如果沒有軟引用,或者從軟引用中得到的實(shí)例是null,重新構(gòu)建一個(gè)實(shí)例,并保存對(duì)這個(gè)新建實(shí)例的軟引用。
c.強(qiáng)引用:如果一個(gè)對(duì)象具有強(qiáng)引用,它就不會(huì)被垃圾回收器回收。即使當(dāng)前內(nèi)存空間不足,JVM也不會(huì)回收它,而是拋出 OutOfMemoryError 錯(cuò)誤,使程序異常終止。如果想中斷強(qiáng)引用和某個(gè)對(duì)象之間的關(guān)聯(lián),可以顯式地將引用賦值為null,這樣一來的話,JVM在合適的時(shí)間就會(huì)回收該對(duì)象。
d.軟引用:在使用軟引用時(shí),如果內(nèi)存的空間足夠,軟引用就能繼續(xù)被使用,而不會(huì)被垃圾回收器回收,只有在內(nèi)存不足時(shí),軟引用才會(huì)被垃圾回收器回收。
e.弱引用:具有弱引用的對(duì)象擁有的生命周期更短暫。因?yàn)楫?dāng) JVM 進(jìn)行垃圾回收,一旦發(fā)現(xiàn)弱引用對(duì)象,無論當(dāng)前內(nèi)存空間是否充足,都會(huì)將弱引用回收。不過由于垃圾回收器是一個(gè)優(yōu)先級(jí)較低的線程,所以并不一定能迅速發(fā)現(xiàn)弱引用對(duì)象。
f.虛引用:顧名思義,就是形同虛設(shè),如果一個(gè)對(duì)象僅持有虛引用,那么它相當(dāng)于沒有引用,在任何時(shí)候都可能被垃圾回收器回收。
Hashcode的作用,與 equal 有什么區(qū)別?
a.同樣用于鑒定2個(gè)對(duì)象是否相等的,java集合中有 list 和 set 兩類,其中 set不允許元素重復(fù)實(shí)現(xiàn),那個(gè)這個(gè)不允許重復(fù)實(shí)現(xiàn)的方法,如果用 equal 去比較的話,如果存在1000個(gè)元素,你 new 一個(gè)新的元素出來,需要去調(diào)用1000次 equal 去逐個(gè)和他們比較是否是同一個(gè)對(duì)象,這樣會(huì)大大降低效率。hashcode實(shí)際上是返回對(duì)象的存儲(chǔ)地址,如果這個(gè)位置上沒有元素,就把元素直接存儲(chǔ)在上面,如果這個(gè)位置上已經(jīng)存在元素,這個(gè)時(shí)候才去調(diào)用equal方法與新元素進(jìn)行比較,相同的話就不存了,散列到其他地址上。
Override和Overload的含義以及區(qū)別
a.Overload顧名思義是重新加載,它可以表現(xiàn)類的多態(tài)性,可以是函數(shù)里面可以有相同的函數(shù)名但是參數(shù)名、返回值、類型不能相同;或者說可以改變參數(shù)、類型、返回值但是函數(shù)名字依然不變。
b.就是ride(重寫)的意思,在子類繼承父類的時(shí)候子類中可以定義某方法與其父類有相同的名稱和參數(shù),當(dāng)子類在調(diào)用這一函數(shù)時(shí)自動(dòng)調(diào)用子類的方法,而父類相當(dāng)于被覆蓋(重寫)了。
具體可前往C++中重載、重寫(覆蓋)的區(qū)別實(shí)例分析查看
抽象類和接口的區(qū)別
a.一個(gè)類只能繼承單個(gè)類,但是可以實(shí)現(xiàn)多個(gè)接口
b.抽象類中可以有構(gòu)造方法,接口中不能有構(gòu)造方法
c.抽象類中的所有方法并不一定要是抽象的,你可以選擇在抽象類中實(shí)現(xiàn)一些基本的方法。而接口要求所有的方法都必須是抽象的
d.抽象類中可以包含靜態(tài)方法,接口中不可以
e.抽象類中可以有普通成員變量,接口中不可以
解析XML的幾種方式的原理與特點(diǎn):DOM、SAX、PULL
a.DOM:消耗內(nèi)存:先把xml文檔都讀到內(nèi)存中,然后再用DOM API來訪問樹形結(jié)構(gòu),并獲取數(shù)據(jù)。這個(gè)寫起來很簡(jiǎn)單,但是很消耗內(nèi)存。要是數(shù)據(jù)過大,手機(jī)不夠牛逼,可能手機(jī)直接死機(jī) ?xml文檔的層級(jí)結(jié)構(gòu), 可以進(jìn)行增刪操作
b.SAX:解析效率高,占用內(nèi)存少,基于事件驅(qū)動(dòng)的:更加簡(jiǎn)單地說就是對(duì)文檔進(jìn)行順序掃描,當(dāng)掃描到文檔(document)開始與結(jié)束、元素(element)開始與結(jié)束、文檔(document)結(jié)束等地方時(shí)通知事件處理函數(shù),由事件處理函數(shù)做相應(yīng)動(dòng)作,然后繼續(xù)同樣的掃描,直至文檔結(jié)束。
c.PULL:與 SAX 類似,也是基于事件驅(qū)動(dòng),我們可以調(diào)用它的next()方法,來獲取下一個(gè)解析事件(就是開始文檔,結(jié)束文檔,開始標(biāo)簽,結(jié)束標(biāo)簽),當(dāng)處于某個(gè)元素時(shí)可以調(diào)用XmlPullParser的getAttributte()方法來獲取屬性的值,也可調(diào)用它的nextText()獲取本節(jié)點(diǎn)的值。
wait()和sleep()的區(qū)別
sleep來自Thread類,和wait來自O(shè)bject類
調(diào)用sleep()方法的過程中,線程不會(huì)釋放對(duì)象鎖。而 調(diào)用 wait 方法線程會(huì)釋放對(duì)象鎖
sleep睡眠后不出讓系統(tǒng)資源,wait讓出系統(tǒng)資源其他線程可以占用CPU
sleep(milliseconds)需要指定一個(gè)睡眠時(shí)間,時(shí)間一到會(huì)自動(dòng)喚醒
JAVA 中堆和棧的區(qū)別,說下java 的內(nèi)存機(jī)制
a.基本數(shù)據(jù)類型比變量和對(duì)象的引用都是在棧分配的
b.堆內(nèi)存用來存放由new創(chuàng)建的對(duì)象和數(shù)組
c.類變量(static修飾的變量),程序在一加載的時(shí)候就在堆中為類變量分配內(nèi)存,堆中的內(nèi)存地址存放在棧中
d.實(shí)例變量:當(dāng)你使用java關(guān)鍵字new的時(shí)候,系統(tǒng)在堆中開辟并不一定是連續(xù)的空間分配給變量,是根據(jù)零散的堆內(nèi)存地址,通過哈希算法換算為一長(zhǎng)串?dāng)?shù)字以表征這個(gè)變量在堆中的”物理位置”,實(shí)例變量的生命周期–當(dāng)實(shí)例變量的引用丟失后,將被GC(垃圾回收器)列入可回收“名單”中,但并不是馬上就釋放堆中內(nèi)存
e.局部變量: 由聲明在某方法,或某代碼段里(比如for循環(huán)),執(zhí)行到它的時(shí)候在棧中開辟內(nèi)存,當(dāng)局部變量一但脫離作用域,內(nèi)存立即釋放
JAVA多態(tài)的實(shí)現(xiàn)原理
a.抽象的來講,多態(tài)的意思就是同一消息可以根據(jù)發(fā)送對(duì)象的不同而采用多種不同的行為方式。(發(fā)送消息就是函數(shù)調(diào)用)
b.實(shí)現(xiàn)的原理是動(dòng)態(tài)綁定,程序調(diào)用的方法在運(yùn)行期才動(dòng)態(tài)綁定,追溯源碼可以發(fā)現(xiàn),JVM 通過參數(shù)的自動(dòng)轉(zhuǎn)型來找到合適的辦法。
轉(zhuǎn)載于:https://www.cnblogs.com/shan1393/p/9017428.html
總結(jié)
以上是生活随笔為你收集整理的面试题:各大公司Java后端开发面试题总结 已看1 背1 有用 链接有必要看看的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL查询指定时间的数据
- 下一篇: Java架构-JavaSE(一)之类与对