Java基礎數據類型
byte 8位(1個字節) short16位 int32位 float32位 long64位 boolean 8位 char16位 double64位
一、計算機網絡
TCP三次握手過程、參數;
第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
SYN:同步序列編號(Synchronize Sequence Numbers)
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手.
TCP四次揮手過程、參數;
1)客戶端進程發出鏈接釋放報文,而且中止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即便不攜帶數據,也要消耗一個序號。
2)服務器收到鏈接釋放報文,發出確認報文,ACK=1,ack=u+1,而且帶上本身的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,可是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
3)客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送鏈接釋放報文(在這以前還須要接受服務器發送的最後的數據)。
4)服務器將最後的數據發送完畢後,就向客戶端發送鏈接釋放報文,FIN=1,ack=u+1,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。
5)客戶端收到服務器的鏈接釋放報文後,必須發出確認,ACK=1,ack=w+1,而本身的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP鏈接尚未釋放,必須通過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
6)服務器只要收到了客戶端發出的確認,當即進入CLOSED狀態。一樣,撤銷TCB後,就結束了此次的TCP鏈接。能夠看到,服務器結束TCP鏈接的時間要比客戶端早一些。
TCP和UDP的區別?應用場景有何不一樣?
首先二者都是傳輸層的協議: TCP(Transmission Control Protocol),又叫傳輸控制協議,UDP(User Datagram Protocol),又叫用戶數據報協議,它們都是傳輸層的協議,但二者的機制不一樣,它們的區別以下:
面向連接: 面向鏈接就是傳輸數據前先創建鏈接而後再傳輸數據
非面向鏈接:就是像ip,直接發送數據包,到達一個節點後找下一條路由,一跳跳的下去
TCP保證可靠,面向鏈接而UDP不保證可靠,非面向鏈接,UDP的報頭長度遠遠小於TCP的報頭長度。TCP使用了三種基礎機制來實現面向鏈接的服務:1 使用序列號進行標記,以便TCP接收服務在向目的應用傳遞數據以前修正錯序的報文排序;2 TCP使用確認,校驗,和定時器系統提供可靠性。3 TCP在應用層數據上附加了一個報頭,報頭包括序列號字段和這些機制的其餘一些必要信息,如叫作端口號的地址字段,該字段能夠標識數據的源點和目標應用程序。
TCP阻塞控制;
TCP的擁塞控制由4個核心算法組成:「慢啓動」(Slow Start)、「擁塞避免」(Congestion voidance)、「快速重傳 」(Fast Retransmit)、「快速恢復」(Fast Recovery)。
-- 慢啓動
早期開發的TCP應用在啓動一個鏈接時會向網絡中發送大量的數據包,這樣很容易致使路由器緩存空間耗盡,網絡發生擁塞,使得TCP鏈接的吞吐量急劇降低。因爲TCP源端一開始並不知道網絡資源當前的利用情況,所以新創建的TCP鏈接不能一開始就發送大量數據,而只能逐步增長每次發送的數據量,以免上述現象的發生,這裏有一個「學習」的過程。 假設client要發送5120字節到server,
慢啓動過程以下:
1.初始狀態,cwnd=1,seq_num=1;client發送第一個segment;
2.server接收到512字節(一個segment),迴應ack_num=513;
3.client接收ack(513),cwnd=1+1=2;如今能夠一次發送2個數據段而沒必要等待ack
4.server接收到2個segment,迴應ack_num=513+512*2=1537
5.client接收ack(1537),cwnd=2+1;一次發送3個數據段
6.server接收到3個segment,迴應2個ack,分別爲ack_num=1537+1024=2561和ack_num=2561+512=3073
7.client接收ack(2561)和ack(3073),cwnd=3+2=5;一次能夠發送5個數據段,可是隻用4個就知足要求了
8.server接收到4個segment,迴應2個ack,分別爲4097,5121 9.已經發送5120字節,任務完成!
總結一下: 當創建新的TCP鏈接時,擁塞窗口(congestion window,cwnd)初始化爲一個數據包大小。源端按cwnd大小發送數據,每收到一個ACK確認,cwnd就增長一個數據包發送量。
-- 擁塞避免 能夠想象,若是按上述慢啓動的邏輯繼續下去而不加任何控制的話,必然會發生擁塞,引入一個慢啓動閾值ssthresh的概念,當cwnd<ssthresh的時候,tcp處於慢啓動狀態,不然,進入擁塞避免階段。(客戶端沒接收到一個ack,cwnd就會增長4個字節,超過閥值以後,當即將cwnd降爲1,作極端處理)
總結一下: 若是當前cwnd達到慢啓動閥值,則試探性的發送一個segment,若是server超時未響應,TCP認爲網絡能力降低,必須下降慢啓動閥值,同時,爲了不形勢惡化,乾脆採起極端措施,把發送窗口降爲1,我的感受應該有更好的方法。
-- 快速重傳和快速恢復
前面講過標準的重傳,client會等待RTO時間再重傳,但有時候,沒必要等這麼久也能夠判斷須要重傳,
快速重傳
例如:client一次發送8個segment,seq_num起始值爲100000,可是因爲網絡緣由,100512丟失,其餘的正常,則server會響應4個ack(100512)(爲何呢,tcp會把接收到的其餘segment緩存起來,ack_num必須是連續的),這時候,client接收到四個重複的ack,它徹底有理由判斷100512丟失,進而重傳,而沒必要傻等RTO時間了。
快速恢復
咱們一般認爲client接收到3個重複的ack後,就會開始快速重傳,可是,若是還有更多的重複ack呢,如何處理?這就是快速恢復要作的,事實上,咱們能夠把快速恢復看做是快速重傳的後續處理,它不是一種單獨存在的形態。
如下是具體的流程:
假設此時cwnd=70000,client發送4096字節到server,也就是8個segment,起始seq_num = _100000:
1.client發送seq_num = _100000
2.seq_num =100512的segment丟失
3.client發送seq_num = _101024
4.server接收到兩個segment,它意識到100512丟失,先把收到的這兩個segment緩存起來
5.server迴應一個ack(100512),表示它還期待這個segment
6.client發送seq_num = _101536
7.server接收到一個segment,它判斷不是100512,依舊把收到的這個segment緩存起來,並回應ack(100512) 。 。 。
8.如下同六、7,直到client收到3個ack(100512),進入快速重發階段:
9.重設慢啓動閥值ssthresh=當前cwnd/2=70000/2=35000
10.client發送seq_num = 100512 如下,進入快速恢復階段:
11.重設cwnd = ssthresh + 3 segments =35000 + 3*512 = 36536,之因此要加3,是由於咱們已經接收到3個ack(100512)了,根據前面說的,每接收到一個ack,cwnd加1
12.client接收到第四個、第五個ack(100512),cwnd=36536+2*512=37560
13.server接收到100512,響應ack_num = _104096 14.此時,cwnd>ssthresh,進入擁塞避免階段。
OSI七層模型、各層所用到的協議;
一些常見協議的原理:ARP、ICMP、FTP等(TCP UDP更不用說啦,必定要了解)
icmp: Internet控制
報文協議,網絡層協議(ping命令)
arp:地址解析協議,網絡層(地址解析,抓包分析)
tcp:文件傳輸協議,基於的傳輸協議是tcp,所以也具備tcp的特性,是處於應用層的協議
二、數據庫知識
數據庫有哪些索引?原理是什麼?
B數索引(B+樹):create ind_t on t1(id) ;若是加上unique,則表示該索引中沒有重複的值
最經常使用的索引,各葉子節點中包括的數據有索引列的值和數據表中對應行的ROWID,簡單的說,在B樹索引中,是經過在索引中保存排過續的索引列值與相對應記錄的ROWID來實現快速查詢的目的。
位圖索引:create bitmap index ind_t on t1(type);【 注:位圖索引不多是惟一索引,也不能進行鍵值壓縮。】
有些字段中使用B樹索引的效率仍然不高,例如性別的字段中,只有「男、女」兩個值,則即使使用了B樹索引,在進行檢索時也將返回接近一半的記錄。
索引中再也不記錄rowid和鍵值,而是將每一個值做爲一列,用0和1表示該行是否等於該鍵值(0表示否;1表示是)。
反向鍵索引:create index ind_t on t1(id) reverse;
反向鍵索引是一種特殊的B樹索引,在存儲構造中與B樹索引徹底相同,可是針對數值時,反向鍵索引會先反向每一個鍵值的字節,而後對反向後的新數據進行索引。例如輸入2008則轉換爲8002,這樣當數值一次增長時,其反向鍵在大小中的分佈仍然是比較平均的。
索引有什麼做用?有什麼特色
這是由於,建立索引能夠大大提升系統的性能。
第一,經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。
第二,能夠大大加快 數據的檢索速度,這也是建立索引的最主要的緣由。
第三,能夠加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。
第四,在使用分組和排序 子句進行數據檢索時,一樣能夠顯著減小查詢中分組和排序的時間。
第五,經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。
索引爲何用B+樹?
一、非葉子節點的子樹指針與關鍵字個數相同;
二、非葉子節點的子樹指針p[i],指向關鍵字值屬於[k[i],k[i+1]]的子樹.(B樹是開區間,也就是說B樹不容許關鍵字重複,B+樹容許重複);
三、爲全部葉子節點增長一個鏈指針;
四、全部關鍵字都在葉子節點出現(稠密索引). (且鏈表中的關鍵字剛好是有序的);
五、非葉子節點至關因而葉子節點的索引(稀疏索引),葉子節點至關因而存儲(關鍵字)數據的數據層;
六、更適合於文件系統;
B+樹和B-樹有什麼區別?(左b+,右b-)
B樹(B-樹)
是一種多路搜索樹(並非二叉的):
1.定義任意非葉子結點最多隻有M個兒子;且M>2;
2.根結點的兒子數爲[2, M];
3.除根結點之外的非葉子結點的兒子數爲[M/2, M];
4.每一個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
5.非葉子結點的關鍵字個數=指向兒子的指針個數-1;
6.非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
7.非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的
子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
8.全部葉子結點位於同一層;
如:(M=3)
對於B+樹來講:
B+樹是B-樹的變體,也是一種多路搜索樹:
1.其定義基本與B-樹同,除了:
2.非葉子結點的子樹指針與關鍵字個數相同;
3.非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹
(B-樹是開區間);
5.爲全部葉子結點增長一個鏈指針;
6.全部關鍵字都在葉子結點出現;
如:(M=3)
mysql中MyISAM和InnoDB的區別?(兩種存儲引擎)
每個MyISAM表都對應於硬盤上的三個文件。這三個文件有同樣的文件名,可是有不一樣的擴展名以指示其類型用途:.frm文件保存表的定義,可是這個文件並非MyISAM引擎的一部分,而是服務器的一部分;.MYD保存表的數據;.MYI是表的索引文件。
InnoDB是MySQL的另外一個存儲引擎,目前MySQL AB所發行新版的標準,被包含在全部二進制安裝包裏,5.5以後做爲默認的存儲引擎。較之於其它的存儲引擎它的優勢是它支持兼容ACID的事務(相似於PostgreSQL),以及參數 完整性(即對外鍵的支持)。
1>.InnoDB支持事物,而MyISAM不支持事物
2>.InnoDB支持行級鎖,而MyISAM支持表級鎖
3>.InnoDB支持MVCC, 而MyISAM不支持
4>.InnoDB支持外鍵,而MyISAM不支持
5>.InnoDB不支持全文索引,而MyISAM支持。
在使用count進行數據庫計數的時候,innodb是要所有的計算一遍,而myisam的效率更快,其中有計算的方法,因此在計數方面myisam更快捷
事務的四大特性(常考)
事物是一條或者多條sql語句組成的執行序列,這個序列中的全部語句都屬於同一個工做單元,要麼同時完成,其中若是有一個失敗,則其餘操做都要回滾。
原子性 (Atomicity)
事物是一個不可分割的數據庫邏輯工做單位,要麼所有完成,要不失敗回滾。
一致性 (Consistency)
事務執行的結果必須使數據庫從一個一致性狀態變到另外一個一致性狀態。
隔離性 (Isolation)
一個事物的執行不能被別的併發事物所幹擾
持久性 (Durability)
事物一旦提交,其對數據庫的改變應該是永久的。
數據庫優化的一些策略;
1,表結構優化
表結構優化是數據庫優化中最重要的,須要結合實際狀況來看怎麼設計更加的優化合理
2,sql語句優化
*sql語法優化,寫出更加便捷的sql語句
*處理邏輯優化,如配合索引和緩存的使用
一個常見的作法是,將涉及到大數據量的sql語句記錄下來,觀察日誌,有側重點的優化
3,分區分表
分區是指將一張表的數據按照必定的規則分到不一樣的區來保存。若一張表中有幾種類型,能夠考慮分表
舉一個例子,分區按照月份來分,將不一樣類型的字段分表,這麼作的好處就是增刪改查數據的時候範圍已經大大縮小了
4,索引優化
索引的原理是在進行增刪改的時候就預先按照指定的字段順序排列後保存了,在查找的時候就能夠從索引找到對應的指針找到數據
優勢:查詢效率很高 缺點:每次增刪改要更新索引,下降增刪改的速度
5,分離活躍數據
將活躍數據單獨存放起來
好比登陸網站,能夠將活躍度高的用戶單獨存放(依據最近登陸時間或者單位時間內登陸次數等),查詢時先從活躍數據查找,沒有再去不活躍處查找
6,讀寫分離
讀寫分離的本質是對數據庫進行集羣,在高併發的狀況降低低單臺服務器的壓力。
通常將寫的服務器叫主服務器,寫入後同步到讀服務器(即從服務器),並將讀請求分配到多個服務器上
增刪改查要熟悉,隨時可能提一個需求讓你寫一個SQL語句;
select * from tablename where a=1
insert into tablename(a,b,c) values (1,2,3)/insert into tablename select a,b,c from tablename1
update tablename set a=1 where id =2019
delect from tablename where id=1
數據庫索引:彙集索引和非彙集索引的區別?
彙集索引的表中記錄的物理順序與索引的排列順序一致,優勢是查詢速度快,由於一旦具備第一個索引值的記錄被找到,具備連續索引值的記錄也必定物理的緊跟其後。
缺點是對錶進行修改速度較慢,這是爲了保持表中的記錄的物理順序與索引 的順序一致,而把記錄插入到數據頁的相應位置,必須在數據頁中進行數據重排,下降了執行速度。在插入新記錄時數據文件爲了維持 B+Tree 的特性而頻繁的 分裂調整,十分低效。
非彙集索引的記錄的物理順序和索引的順序不一致。 非彙集索引添加記錄時,不會引發數據順序的重組。
三、編程語言基礎(以Java爲例)
面向對象的特性?(封裝繼承多態)如何體現出來?
1) 繼承: 繼承都是從已有的類獲得繼承信息 建立新的類的過程.提供信息的類被稱爲父類(超類,基類),獲得信息的類被稱爲子類(派生類).繼承讓變化中的軟件系統有了必定的延續性,同時繼承也是封裝程序中可變因素的重要手段.
2) 封裝: 一般認爲封裝是把數據和操做數據的方法綁定起來,對數據的訪問只能經過已定義的接口.面向對象的本質就是將現實世界描繪成一系列徹底自治,封閉的對象.咱們在類中編寫方法就是對實現細節的一種封裝;咱們編寫了一個類就是對數據的數據操做的封裝,能夠說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口
3) 多態性: 多態性是指容許不一樣子類型的對象對同一消息做出不一樣響應,簡單的說就是用一樣的對象引用調用一樣的對象引用調用一樣的方法可是作了不一樣的事情.多態性分爲編譯時多態性和運行時多態性.若是將對象的方法視爲對象向外界提供服務,那麼運行時多態性能夠解釋爲:當A系統訪問B系統提供的服務是,B系統有多種提供服務的方式.但一切對於A系統來講都是透明的.方法重載(overload) 實現的是編譯時的多態性(也稱爲前綁定), 而方法重寫(override) 實現的是運行時的多態性(也成後綁定). 運行時的多態是面向對象最精髓的東西,要實現多態須要作兩件事: 1. 方法重寫(子類繼承父類並重寫父類中已有的或抽象方法); 2. 對象造型(用父類型引用引用子類型對象,這樣一樣的引用調用一樣的方法就會根據子類對象的不一樣而表現出不一樣的行爲)
重載和重寫有什麼區別?
重載: 在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣甚至是參數順序不一樣)則視爲重載。同時,重載對返回類型沒有要求,能夠相同也能夠不一樣,但不能經過返回類型是否相同來判斷重載。
重載 總結:
1.重載Overload是一個類中多態性的一種表現
2.重載要求同名方法的參數列表不一樣(參數類型,參數個數甚至是參數順序)
3.重載的時候,返回值類型能夠相同也能夠不相同。沒法以返回類型做爲重載函數的區分標準
重寫:從字面上看,重寫就是 從新寫一遍的意思。其實就是在子類中把父類自己有的方法從新寫一遍。子類繼承了父類原有的方法,但有時子類並不想原封不動的繼承父類中的某個方法,因此在方法名,參數列表,返回類型(除過子類中方法的返回值是父類中方法返回值的子類時)都相同的狀況下, 對方法體進行修改或重寫,這就是重寫。但要注意子類函數的訪問修飾權限不能少於父類的。
重寫 總結:
1.發生在父類與子類之間
2.方法名,參數列表,返回類型(除過子類中方法的返回類型是父類中返回類型的子類)必須相同
3.訪問修飾符的限制必定要大於被重寫方法的訪問修飾符(public>protected>default>private)
4.重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常
面試時,問:重載(Overload)和重寫(Override)的區別?
答:方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,然後者實現的是運行時的多態性。重載發生在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣或者兩者都不一樣)則視爲重載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的參數列表,有兼容的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。重載對返回類型沒有特殊的要求,不能根據返回類型進行區分
集合類有哪些?(常考)
Set、List、Map和Queue4大致系。其中,Set表明無序的、不容許有重複元素的集合,List表明有序的、容許有重複元素的集合,Map表明具備映射關係的集合,Queue表明隊列集合。Java集合類主要由2個接口派生過來:Collection接口和Map接口!
Set和List的區別;
List 是可重複集合,list是有序的數據結構
Set 是不可重複集合,set是無序的數據結構
這兩個接口都實現了 Collection 父接口.
ArrayList、Linkedlist、Vector區別?(常考,建議看下底層實現代碼)
一、Vector、ArrayList都是以相似數組的形式存儲在內存中,LinkedList則以鏈表的形式進行存儲。
三、Vector線程同步,ArrayList、LinkedList線程不一樣步。
四、LinkedList適合指定位置插入、刪除操做,不適合查找;ArrayList、Vector適合查找,不適合指定位置的插入、刪除操做。
五、ArrayList在元素填滿容器時會自動擴充容器大小的50%,而Vector則是100%,所以ArrayList更節省空間。
數組:能夠根據下標快速查找,因此大部分狀況下,查詢快。可是若是要進行增刪操做的時候,會須要移動修改元素後面的全部元素,因此增刪的開銷比較大,數組的對增刪操做的執行效率低。而採用數組做爲數據存儲結構的ArrayList、Vector也存在這些特性,查詢速度快(能夠根據下標直接取,比迭代查找更快),增刪慢。
鏈表:增長和刪除元素方便,增長或刪除一個元素,僅需處理結點間的引用便可。就像人手拉手連成一排,要增長或刪除某我的只要附近的兩我的換一我的牽手,對已經牽好手的人沒影響。不管在哪裏換人耗費的資源和時間都是同樣的。可是查詢不方便,須要一個個對比,沒法根據下標直接查找。而採用鏈表結構存儲的LinkedList也有這些特性,增刪方便,查詢慢(指的是隨機查詢,不是順序查詢)。
ArrayList如何擴容?(常考)
當數組裏面的元素一個一個進行增長的時候,每次進行判斷,當新增一個元素以後,數組的長度>初定義的長度的話,那麼就建立一個新的數組:
int newCapacity = oldCapacity + oldCapacity / 2,這樣就是表示新的數組長度是舊的數組長度的1.5倍,而後用copyof的方法將原來數組的內容複製到新的數組中,完成arraylist的動態擴容
Map下Hashmap、TreeMap的區別?
(1)HashMap:適用於在Map中插入、刪除和定位元素。
(2)Treemap:適用於按天然順序或自定義順序遍歷鍵(key)。
(3)HashMap一般比TreeMap快一點(樹和哈希表的數據結構使然),建議多使用HashMap,在須要排序的Map時候才用TreeMap.
(4)HashMap 非線程安全 TreeMap 非線程安全
(5)HashMap的結果是沒有排序的,而TreeMap輸出的結果是排好序的。
(6)HashMap:底層是哈希表數據結構, TreeMap:底層是二叉樹數據結構
TreeMap底層是什麼?紅黑樹仍是二叉樹?
紅黑樹
Map、List下哪些類是線程安全的?(常考)
map的實現類有:hashtable(線程安全) concurrenthashmap(線程安全)hashmap(不安全)treemap(不安全)linkedhashmap(不安全)weakhashmap(不安全)
list的實現類:vector(線程安全)arraylist(不安全)linkendlist(不安全)
Hashmap的擴容機制;
HashMap使用的是懶加載,構造完HashMap對象後,只要不進行put 方法插入元素以前,HashMap並不會去初始化或者擴容table。
當首次調用put方法時,HashMap會發現table爲空而後調用resize方法進行初始化,當添加完元素後,若是HashMap發現size(元素總數)大於threshold(閾值),則會調用resize方法進行擴容
默認的負載因子大小爲0.75,當一個map填滿了75%的bucket時候,就會擴容,擴容後的table大小變爲原來的兩倍
從新調整map的大小,並將原來的對象放入新的bucket數組中。這個過程叫做rehashing
Hashmap如何解決哈希衝突?與HashTable有何不一樣?
哈希衝突:通俗的講就是首先咱們進行一次 put 操做,算出了咱們要在 table 數組的 x 位置放入這個值。那麼下次再進行一個 put 操做的時候,又算出了咱們要在 table 數組的 x 位置放入這個值
hashmap解決哈希衝突:首先判斷二者的key是否是同樣的,由於hashmap是不容許有重複值得,若是二者是重複的,就進行覆蓋,不然就利用鏈地址法,將衝突的節點放在鏈表的最下面。
若是衝突過多,能夠將鏈表轉化爲紅黑樹,時間複雜度是 O(logn)
HashMap和HashTable主要有如下5個方面的區別:
1.繼承的父類不一樣
Hashtable繼承自Dictionary類,而HashMap繼承自
類。但兩者都實現了Map接口。
2.對null對象的支持不一樣
HashMap是支持null鍵和null值的,而HashTable在遇到null時,會拋出NullPointerException異常。這並非由於HashTable有什麼特殊的實現層面的緣由致使不能支持null鍵和null值,這僅僅是由於HashMap在實現時對null作了特殊處理,將null的hashCode值定爲了0,從而將其存放在哈希表的第0個bucket中。
3.容量大小及擴容方式不一樣
HashMap和HashTable都使用哈希表來存儲鍵值對。在數據結構上是基本相同的,都建立了一個繼承自Map.Entry的私有的內部類Entry,每個Entry對象表示存儲在哈希表中的一個鍵值對。
Entry對象惟一表示一個鍵值對,有四個屬性:
-K key 鍵對象
-V value 值對象
-int hash 鍵對象的hash值
-Entry entry 指向鏈表中下一個Entry對象,可爲null,表示當前Entry對象在鏈表尾部。
HashMap/HashTable內部用Entry數組實現哈希表,而對於映射到同一個哈希桶(數組的同一個位置)的鍵值對,使用Entry鏈表來存儲。
HashMap/HashTable初始容量大小和每次擴充容量大小的不一樣:HashTable默認的初始大小爲11,以後每次擴充爲原來的2n+1。HashMap默認的初始化大小爲16,以後每次擴充爲原來的2倍。若是在建立時給定了初始化大小,那麼HashTable會直接使用你給定的大小,而HashMap會將其擴充爲2的冪次方大小。
4.線程安全性不一樣
HashTable是同步的(緣由:公開的方法好比get都使用了synchronized描述符。而遍歷視圖好比keySet都使用了Collections.synchronizedXXX進行了同步包裝),HashMap不是,也就是說HashTable在多線程使用的狀況下,不須要作額外的同步,而HashMap則不行。
因爲Hashtable是線程安全的也是synchronized,因此在單線程環境下它比HashMap速度要慢。
若是要保持線程安全能夠選用ConcurrentHashMap,ConcurrentHashMap引入了分割(segmentation),不論它變得多麼大,僅僅須要鎖定map的某個部分,而其它的線程不須要等到迭代完成才能訪問map。Hashtable則會鎖定整個map,Hashtable的大小增長到必定的時候,性能會急劇降低,由於迭代時須要被鎖定很長的時間。簡而言之,在迭代的過程當中,ConcurrentHashMap僅僅鎖定map的某個部分,而Hashtable則會鎖定整個map,ConcurrentHashMap比Hashtable高效。
5.Hash值不一樣
Hashtable計算hash值,直接用key的hashCode(),而HashMap從新計算了key的hash值,Hashtable在求hash值對應的位置索引時,用取模運算,而HashMap在求位置索引時,則用與運算,且這裏通常先用hash&0x7FFFFFFF後,再對length取模,&0x7FFFFFFF的目的是爲了將負的hash值轉化爲正值,由於hash值有可能爲負數,而&0x7FFFFFFF後,只有符號位改變,然後面的位都不變。
\t 的意思是 橫向跳到下一製表符位置(tab鍵)
\n 的意思是回車換行。
正則表達式;(服務端崗位常考)
接口跟抽象類的區別?
下面比較-下二者的語法區別:
1.抽象類能夠有構造方法,接口中不能有構造方法。
2.抽象類中能夠有普通成員變量,接口中沒有普通成員變量
3.抽象類中能夠包含非抽象的普通方法,接口中的全部方法必須都是抽象的,不能有非抽象的普通方法。
4.抽象類中的抽象方法的訪問類型能夠是public , protected和(默認類型,雖然eclipse下不報錯,但應該也不行 ) , 但接口中的抽象方法只能是public類型的,而且默認即爲public abstract類型。
5.抽象類中能夠包含靜態方法,接口中不能包含靜態方法
6.抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任意,但接口中定義的變星只能是public static final類型,而且默認即爲public static final類型。
7.一個類能夠實現多個接口,但只能繼承一個抽象類。
抽象(abstract)
public abstract class people {
public abstract void func1();
public void func11() {
}
public void a() {}
int a;
public people() {
int a=0;
}
protected void b() {}
final void c() {}
public static void cc() {}
public void aaa() {
}
}
package com.example.cache.test;
/**
* 類的抽象abstract
* */
public abstract class test {
public int a;//抽象類能夠有普通成員變量
public test(){}//抽象的無參構造
public test(int a){}//抽象的有參構造
private int num=0;//抽象的變量
public int num1=0;
final int num2=0;
public abstract int b();//不寫方法主體,必須定義爲abstract修飾
public int test1(){return 0;}//抽象的非抽象普通方法
protected int test3(){return 0;}//抽象的非抽象普通方法
private int test4(){return 0;}//抽象的非抽象普通方法,能夠是私有的,只是子類在繼承的時候,沒辦法繼承這個私有的方法
public final int test2(){return 0;}//抽象類能夠寫final方法,可是不能夠被重寫,並且在子類中能夠調用這個方法
static int a1=0;//能夠有靜態的成員變量,靜態變量就是獨立於其餘的變量,和全局變量的區別就是在多個類文件之間進行調用時,全局能夠調用,靜態不能夠
}
package com.example.cache.test;
/**
* 類的接口
* */
public interface test1 {
public int a;//接口沒有普通成員變量,這樣寫是會報錯的,必需要賦值
public test1(){}//接口沒有構造函數,這樣寫是會報錯的
abstract int test1();//接口的方法不能有主體;
int test21();//接口不能使用final和protected進行修飾
static int a1=0;//能夠有靜態的成員變量,靜態變量就是獨立於其餘的變量,和全局變量的區別就是在多個類文件之間進行調用時,全局能夠調用,靜態不能夠
public int b();//不寫方法主體,必須定義爲abstract修飾
}
Java能夠多繼承嗎?
java不支持多繼承,只支持單繼承(即一個類只能有一個父類)。可是java能夠實現多個接口,即一個子接口能夠有多個父接口。
JVM垃圾回收機制;(常考)
是否符合垃圾回收的標準有兩個:
(一)給對象賦予了null,之後再沒有使用過;
(二)給對象賦予了新值,從新分配了內存空間。
判斷對象是否是須要被回收算法:
一、引用計數法:
給對象添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1;當引用失效時,計數器的值就減1;任什麼時候刻計數器爲0的對象就是不可能被使用的。 可是在主流的JVM中並無選用引用計數法來管理內存,最主要的緣由是它很難解決對象間互相循環引用的問題
二、可達性分析法:
基本思路是經過一系列稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索走過的路徑稱爲「引用鏈」,當一個對象到GC Roots沒通路時(也就是從GC Roots到這個對象不可達),則證實此對象是不可用的。
垃圾處理算法:
一、標記-清除算法:
主要分爲兩個階段:標記和清除兩部分,首先就是標記上全部的須要進行回收的對象,而後在標記完成以後統一將須要進行回收的對象進行回收;
存在兩個問題:1)效率問題(效率不高),2)空間問題,標記清除以後會產生大量的不連續的內存碎片。
二、複製算法:
複製算法的出現是爲了解決效率問題,它將可用內存按照容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊內存用完了,就將還存活着的對象複製到另外一塊上面,而後再把已使用過的內存空間一次性清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮碎片等複雜狀況,只要移動堆頂的指針,按順序分配內存便可,實現簡單,運行高效。只是這種算法的代價是將內存縮小爲了原來的一半。代價有點高了。
三、標記-整理算法:
標記過程和標記-清除算法是同樣的,將須要進行回收的對象進行標記,而後將這些須要進行回收的對象向一端進行移動,而後清除掉端邊界意外的內存
四、分代收集算法:
當前商業虛擬機都採用分代收集算法,將對象存活週期的不一樣將內存劃分爲幾塊。通常是把Java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死亡,只有少許存活,那就選用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。而老年代中由於對象存活率高,沒有額外的空間對它進行分配擔保,就必須使用標記 - 清除算法或者標記 - 整理算法來進行回收。
Java中是值傳遞仍是引用傳遞?
值傳遞
Java中鎖機制;
樂觀鎖: 老是認爲不會產生併發問題,每次去取數據的時候總認爲不會有其餘線程對數據進行修改,所以不會上鎖,可是在更新時會判斷其餘線程在這以前有沒有對數據進行修改,通常會使用版本號機制或CAS操做實現。
悲觀鎖:顧名思義就是採用一種悲觀的態度來對待事務併發問題,咱們認爲系統中的併發更新會很是頻繁,而且事務失敗了之後重來的開銷很大,這樣以來,咱們就須要採用真正意義上的鎖來進行實現。悲觀鎖的基本思想就是每次一個事務讀取某一條記錄後,就會把這條記錄鎖住,這樣其它的事務要想更新,必須等之前的事務提交或者回滾解除鎖。
重入鎖: 重入鎖,也叫作遞歸鎖,指的是同一線程 外層函數得到鎖以後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。
在JAVA環境下 ReentrantLock(輕量級) 和synchronized(重量級) 都是 可重入鎖
讀寫鎖:相比Java中的鎖(Locks in Java)裏Lock實現,讀寫鎖更復雜一些。假設你的程序中涉及到對一些共享資源的讀和寫操做,且寫操做沒有讀操做那麼頻繁。在沒有寫操做的時候,兩個線程同時讀一個資源沒有任何問題,因此應該容許多個線程能在同時讀取共享資源。可是若是有一個線程想去寫這些共享資源,就不該該再有其它線程對該資源進行讀或寫(譯者注:也就是說:讀-讀能共存,讀-寫不能共存,寫-寫不能共存)。這就須要一個讀/寫鎖來解決這個問題
Lock的底層怎麼實現的?源碼怎麼寫的?
sychronized的底層實現?
sychronized修飾靜態方法和修飾普通方法有什麼區別?
異常類有哪些實現類、子類?
(1)NullPointerException 當應用程序試圖訪問空對象時,則拋出該異常。
(2)SQLException 提供關於數據庫訪問錯誤或其餘錯誤信息的異常。
(3)IndexOutOfBoundsException指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。
(4)NumberFormatException當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。
(5)FileNotFoundException當試圖打開指定路徑名錶示的文件失敗時,拋出此異常。
(6)IOException當發生某種I/O異常時,拋出此異常。此類是失敗或中斷的I/O操做生成的異常的通用類。
(7)ClassCastException當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。
(8)ArrayStoreException試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。
(9)IllegalArgumentException 拋出的異常代表向方法傳遞了一個不合法或不正確的參數。
(10)ArithmeticException當出現異常的運算條件時,拋出此異常。例如,一個整數「除以零」時,拋出此類的一個實例。
(11)NegativeArraySizeException若是應用程序試圖建立大小爲負的數組,則拋出該異常。
(12)NoSuchMethodException沒法找到某一特定方法時,拋出該異常。
(13)SecurityException由安全管理器拋出的異常,指示存在安全侵犯。
(14)UnsupportedOperationException當不支持請求的操做時,拋出該異常。
(15)RuntimeExceptionRuntimeException 是那些可能在Java虛擬機正常運行期間拋出的異常的超類。
多線程中如何保證線程安全?
1.原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操做,(atomic,synchronized);
2.可見性:一個線程對主內存的修改能夠及時地被其餘線程看到,(synchronized,volatile);
3.有序性:一個線程觀察其餘線程中的指令執行順序,因爲指令重排序,該觀察結果通常雜亂無序,(happens-before原則)。
多線程有哪些常見的線程安全的類?
Timer,HashTable,StringBuffer,TimerTask,Vector,Stack
如何開啓一個線程?
3 /** 4 * @desc 第一種開啓線程的方式
5 * @author
6 *
7 */ 8 public class Demo1_Thread extends Thread {
9
10 public void run() {
11 for (int i = 0; i < 10; i++) {
12 System.out.println(i + " run()...");
13 }
14 }
15
16 public static void main(String[] args) {
17 Demo1_Thread demo = new Demo1_Thread();
18 demo.start();
19 for (int i = 0; i < 10; i++) {
20 System.out.println(i + " main()...");
21 }
22 }
24 }
能夠繼承thread類,新建一個當前類的對象,而後執行start()方法便可
get請求和post請求有什麼區別?
GET產生一個TCP數據包;POST產生兩個TCP數據包。
GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息。
對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。
GET請求會被瀏覽器主動cache,而POST不會
反射的原理?
java在執行的時候,前後是經過編譯、加載、鏈接進行的
編譯就是java文件編譯後生成.class字節碼文件
加載就是類加載器負責根據一個類的全限定名來讀取此類的二進制字節流到JVM內部,並存儲在運行時內存區的方法區,而後將其轉換爲一個與目標類型對應的java.lang.Class對象實例
Java的反射就是利用上面第二步加載到jvm中的.class文件來進行操做的。.class文件中包含java類的全部信息,當你不知道某個類具體信息時,能夠使用反射獲取class,而後進行各類操做。
Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它的屬性。總結說:反射就是把java類中的各類成分映射成一個個的Java對象,而且能夠進行操做。
ClassLoader和Class.forName()這兩個有什麼區別?(反射源碼的考察)
NIO這一塊有什麼瞭解?
四、項目框架(以Spring爲例)
SSH是 struts+spring+hibernate的一個集成框架
SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis兩個開源框架整合而成(SpringMVC是Spring中的部份內容)
簡述Springmvc的流程;
一、 首先用戶發送請求——>DispatcherServlet,前端控制器收到請求後本身不進行處理,而是委託給其餘的解析器進行處理,做爲統一訪問點,進行全局的流程控制;
二、 DispatcherServlet——>HandlerMapping, HandlerMapping將會把請求映射爲HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象,經過這種策略模式,很容易添加新的映射策略;
三、 DispatcherServlet——>HandlerAdapter,HandlerAdapter將會把處理器包裝爲適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持不少類型的處理器;
四、 HandlerAdapter——>處理器功能處理方法的調用,HandlerAdapter將會根據適配的結果調用真正的處理器的功能處理方法,完成功能處理;並返回一個ModelAndView對象(包含模型數據、邏輯視圖名);
五、 ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver將把邏輯視圖名解析爲具體的View,經過這種策略模式,很容易更換其餘視圖技術;
六、 View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構,所以很容易支持其餘視圖技術;
七、返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。
控制反轉(ioc):
Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:
●誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)。
●爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。
IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。
其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。
IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。
依賴注入(DI)
「依賴注入」:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。
Spring的核心特性是什麼?
Ioc和aop
理解AOP、IoC的基本原理;
AOP:面向切面編程,能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。主要功能有日誌記錄,性能統計,安全控制,事務處理,異常處理等等。主要意圖就是把以上功能從業務邏輯代碼中分離出來,進而改變這些行爲的時候不影響業務邏輯的代碼。說白了,就是擴展功能不修改源代碼實現。
aop有兩種代理方式:
java代理:採用java內置的代理API實現(寫接口,設置代理)
cglib代理:採用第三方API實現
二者的區別:
cglib底層運用了asm這個很是強大的Java字節碼生成框架來生成class, 比jdk代理效率要高
jdk代理只能代理被接口修飾的類,而cglib沒有這個限制
IoC上面有總結:
IoC 在 Spring 裏,只須要低級容器就能夠實現,2 個步驟:
a. 加載配置文件,解析成 BeanDefinition 放在 Map 裏。
b. 調用 getBean 的時候,從 BeanDefinition 所屬的 Map 裏,拿出 Class 對象進行實例化,同時,若是有依賴關係,將遞歸調用 getBean 方法 —— 完成依賴注入。
AOP的一些場景應用;
主要在作日誌記錄,性能統計,安全控制,事務處理,異常處理的時候
Springmvc和Springboot有什麼區別?
Spring MVC的功能
Spring MVC提供了一種輕度耦合的方式來開發web應用。
Spring MVC是Spring的一個模塊,是一個web框架。經過Dispatcher Servlet, ModelAndView 和 View Resolver,開發web應用變得很容易。解決的問題領域是網站應用程序或者服務開發——URL路由、Session、模板引擎、靜態Web資源等等。
Spring Boot的功能
Spring Boot實現了自動配置,下降了項目搭建的複雜度。
衆所周知Spring框架須要進行大量的配置,Spring Boot引入自動配置的概念,讓項目設置變得很容易。Spring Boot自己並不提供Spring框架的核心特性以及擴展功能,只是用於快速、敏捷地開發新一代基於Spring框架的應用程序。也就是說,它並非用來替代Spring的解決方案,而是和Spring框架緊密結合用於提高Spring開發者體驗的工具。同時它集成了大量經常使用的第三方庫配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot應用中這些第三方庫幾乎能夠零配置的開箱即用(out-of-the-box),大部分的Spring Boot應用都只須要很是少許的配置代碼,開發者可以更加專一於業務邏輯。
Spring Boot只是承載者,輔助你簡化項目搭建過程的。若是承載的是WEB項目,使用Spring MVC做爲MVC框架,那麼工做流程和你上面描述的是徹底同樣的,由於這部分工做是Spring MVC作的而不是Spring Boot。
對使用者來講,換用Spring Boot之後,項目初始化方法變了,配置文件變了,另外就是不須要單獨安裝Tomcat這類容器服務器了,maven打出jar包直接跑起來就是個網站,但你最核心的業務邏輯實現與業務流程實現沒有任何變化。
因此,用最簡練的語言歸納就是:
Spring 是一個「引擎」;
Spring MVC 是基於Spring的一個 MVC 框架 ;
Spring Boot 是基於Spring4的條件註冊的一套快速開發整合包。
Springboot爲何配置簡單?(即它自動作了什麼操做才能簡化程序員的操做)
有不少的xxxautoconfigration,爲咱們配置了不少的自動配置
Spring容器的加載順序?
@Resource 和 @Autowired 區別?分別用在什麼場景?
二者均可以寫在字段和setter方法上。二者若是都寫在字段上,那麼就不須要再寫setter方法。
不一樣點
(1)@Autowired(spring的註解)
@Autowired爲Spring提供的註解,須要導入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
@Autowired註解是按照類型(byType)裝配依賴對象,默認狀況下它要求依賴對象必須存在,若是容許null值,能夠設置它的required屬性爲false。若是咱們想使用按照名稱(byName)來裝配,能夠結合@Qualifier註解一塊兒使用。以下:
(2)@Resource(Java的註解)
@Resource默認按照ByName自動注入,由J2EE提供,須要導入包javax.annotation.Resource。@Resource有兩個重要的屬性:name和type,而Spring將@Resource註解的name屬性解析爲bean的名字,而type屬性則解析爲bean的類型。因此,若是使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。若是既不制定name也不制定type屬性,這時將經過反射機制使用byName自動注入策略。
靜態代理和動態代理的區別?
靜態:由程序員建立代理類或特定工具自動生成源代碼再對其編譯。在程序運行前代理類的.class文件就已經存在了。
動態:在程序運行時運用反射機制動態建立而成
Hibernate和mybatis的區別?
2.1 開發方面
在項目開發過程中,就速度而言:
hibernate開發中,sql語句已經被封裝,直接能夠使用,加快系統開發;
Mybatis 屬於半自動化,sql須要手工完成,稍微繁瑣;
可是,凡事都不是絕對的,若是對於龐大複雜的系統項目來講,發雜語句較多,選擇hibernate 就不是一個好方案。
2.2 sql優化方面
Hibernate 自動生成sql,有些語句較爲繁瑣,會多消耗一些性能;
Mybatis 手動編寫sql,能夠避免不須要的查詢,提升系統性能;
2.3 對象管理比對
Hibernate 是完整的對象-關係映射的框架,開發工程中,無需過多關注底層實現,只要去管理對象便可;
Mybatis 須要自行管理 映射關係;
2.4 緩存方面
Hibernate 具備良好的管理機制,用戶不須要關注SQL,若是二級緩存出現髒數據,系統會保存,;
Mybatis 在使用的時候要謹慎,避免緩存CAche 的使用。
Hibernate優點
-
Hibernate的DAO層開發比MyBatis簡單,Mybatis須要維護SQL和結果映射。
-
Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。
-
Hibernate數據庫移植性很好,MyBatis的數據庫移植性很差,不一樣的數據庫須要寫不一樣SQL。
-
Hibernate有更好的二級緩存機制,能夠使用第三方緩存。MyBatis自己提供的緩存機制不佳。
Mybatis優點
-
MyBatis能夠進行更爲細緻的SQL優化,能夠減小查詢字段。
-
MyBatis容易掌握,而Hibernate門檻較高。
mybatis是如何工做的?
一、加載mybatis全局配置文件(數據源、mapper映射文件等),解析配置文件,MyBatis基於XML配置文件生成Configuration,和一個個MappedStatement(包括了參數映射配置、動態SQL語句、結果映射配置),其對應着<select | update | delete | insert>標籤項。
二、SqlSessionFactoryBuilder經過Configuration對象生成SqlSessionFactory,用來開啓SqlSession。
三、SqlSession對象完成和數據庫的交互:
a、用戶程序調用mybatis接口層api(即Mapper接口中的方法, crud)
b、SqlSession經過調用api的Statement ID找到對應的MappedStatement對象
c、經過Executor(負責動態SQL的生成和查詢緩存的維護)將MappedStatement對象進行解析,sql參數轉化、動態sql拼接,生成jdbc Statement對象
d、JDBC執行sql。
e、藉助MappedStatement中的結果映射關係,將返回結果轉化成HashMap、JavaBean等存儲結構並返回。
Hibernate對象有幾個狀態值?
Hibernate 把對象分爲三種狀態:Transient(臨時狀態)、Persistent(持久化狀態)、Detached(遊離狀態)。
1)Transient:剛剛new出來的對象,就是Transient狀態的,此時他沒有OID。
2)Persistent:有持久化標誌OID,已經被歸入到session對象的管理
3) Detached: --有持久化標誌OID,可是沒有被歸入到Session對象的管理
數據庫的鎖機制:
數據庫鎖出現的目的:處理併發問題
併發控制的主要採用的技術手段:樂觀鎖、悲觀鎖和時間戳。
鎖分類
從數據庫系統角度分爲三種:排他鎖、共享鎖、更新鎖。
從程序員角度分爲兩種:一種是悲觀鎖,一種樂觀鎖。
redirect和forward的區別
redirect是間接請求轉發: 有時也叫重定向,它通常用於避免用戶的非正常訪問
response.sendRedirect("../user-jsp/login.jsp");
forward是直接請求轉發: 在請求對象request中,保存的對象對於每一個信息資源是共享的。
request.getRequestDispatcher("/main.jsp").forward(request, response);
oracle數據庫的數據表的交集表現用intersect(從兩個表中查一些要合在一塊兒的數據)
SELECT CODE FROM EMPLOYEE WHERE GENDER = 'M'
INTERSECT
SELECT CODE FROM SALARY WHERE SALARY > 2500
jsp頁面傳遞參數的幾種方法:
一、能夠使用a標籤進行傳遞,例如: <a href="show.jsp?name=ss">點擊</a> //設置參數和賦值,
而後在後臺就能夠使用requset.getParameter的方式進行接收:String name=request.getParameter("name")
二、能夠用request來傳遞參數
請求做用域,客戶端的一次請求。生命週期爲一次請求或使用forward方式執行請求轉發,也就是使用forward方式跳轉多個jsp,在這些頁面裏你均可以使用這個變量。可是隻要刷新頁面,這個變量就失效了。
<body>
<%request.setCharacterEncoding("UTF-8"); %>
<%request.setAttribute("name", "李白"); %>
<jsp:forward page="index.jsp"></jsp:forward>
</body>
而後,在另外進行接收 String name= request.getAttribute("name");
3.能夠使用session進行傳遞參數:
session的做用時間簡單的說就是從瀏覽器打開到瀏覽器關閉這個過程當中有效。
<%session.setAttribute("name", "李白"); %>
另外進行接收:String name= session.getAttribute("name");
4.能夠用application進行參數的傳遞(方法相似session)
appllication是全局做用範圍,整個應用程序共享,生命週期爲從應用程序啓動到中止,在這個過程當中application裏的變量一直在累加,除非你重啓tomcat或是人工刪除,不然它會一直變大。
在一邊: <%application.setAttribute("name", "李白"); %>
在另外一邊進行接收:String name= application.getAttribute("name");
五、使用jstl的方式,前提是引入jstl的jar包:
在一端:
<c:redirect url="main.jsp"> //負責跳轉
<c:param name="user" value="wgh"></c:param> //賦值
在另外一端接收:{param.user}]您好,歡迎訪問我公司的網
String和StringBuffer的區別
String:是對象不是原始類型.
爲不可變對象,一旦被建立,就不能修改它的值.
對於已經存在的String對象的修改都是從新建立一個新的對象,而後把新的值保存進去.
String 是final類,即不能被繼承.
StringBuffer:
是一個可變對象,當對他進行修改的時候不會像String那樣從新創建對象
它只能經過構造函數來創建,
StringBuffer sb = new StringBuffer();
對象被創建之後,在內存中就會分配內存空間,並初始保存一個null.經過它的append方法向其賦值.
sb.append("hello");
字符串鏈接操做中StringBuffer的效率要明顯比String高:
String對象是不可變對象,每次操做String 都會從新創建新的對象來保存新的值.
StringBuffer對象實例化後,只對這一個對象操做。
而且StringBuffer的字符操做效率比String的效率高不少。
刪除索引:(下面二者等價)
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
創建索引的原則:
-
定義主鍵的數據列必定要創建索引。
-
定義有外鍵的數據列必定要創建索引。
-
對於常常查詢的數據列最好創建索引。
-
對於須要在指定範圍內的快速或頻繁查詢的數據列;
-
常常用在WHERE子句中的數據列。
-
常常出如今關鍵字order by、group by、distinct後面的字段,創建索引。若是創建的是複合索引,索引的字段順序要和這些關鍵字後面的字段順序一致,不然索引不會被使用。
-
對於那些查詢中不多涉及的列,重複值比較多的列不要創建索引。
-
對於定義爲text、image和bit的數據類型的列不要創建索引。
-
對於常常存取的列避免創建索引
-
限制表上的索引數目。對一個存在大量更新操做的表,所建索引的數目通常不要超過3個,最多不要超過5個。索引雖然說提升了訪問速度,但太多索引會影響數據的更新操做。
-
對複合索引,按照字段在查詢條件中出現的頻度創建索引。在複合索引中,記錄首先按照第一個字段排序。對於在第一個字段上取值相同的記錄,系統再按照第二個字段的取值排序,以此類推。所以只有複合索引的第一個字段出如今查詢條件中,該索引纔可能被使用,所以將應用頻度高的字段,放置在複合索引的前面,會使系統最大可能地使用此索引,發揮索引的做用。
數據庫的外鍵( foreign key)
數據庫的外鍵建立的必要性:
一、表的組織結構複雜不清晰
二、浪費空間
三、擴展性極差
爲了解決上述的問題,就須要用多張表來存放數據。
表與表的記錄之間存在着三種關係:一對多、多對多、一對一的關係。
處理表之間關係問題就會利用到FOREIGN KEY
create table dep(
id int primary key auto_increment,
dep_name char(10),
dep_comment char(60)
);
create table emp(
id int primary key auto_increment,
name char(16),
gender enum('male','female') not null default 'male',
dep_id int,
foreign key(dep_id) references dep(id)
這樣在建立的時候,就能夠將emp中的dep_id對應上emp中中的id
使用的語法就是
foreign key(xxxb) references tablename(xxxa)
public/private/protected
一、public:public代表該數據成員、成員函數是對全部用戶開放的,全部用戶均可以直接進行調用
二、private:private表示私有,私有的意思就是除了class本身以外,任何人都不能夠直接使用,私有財產神聖不可侵犯嘛,即使是子女,朋友,都不能夠使用。
三、protected:protected對於子女、朋友來講,就是public的,能夠自由使用,沒有任何限制,而對於其餘的外部class,protected就變成private。
Java是否存在內存泄漏問題
內存泄漏是指一個再也不被程序使用的對象或變量還在內存中佔有存儲空間
在java語言中,判斷一個內存空間是否符合垃圾回收的標準有兩個:
(一)給對象賦予了null,之後再沒有使用過;
(二)給對象賦予了新值,從新分配了內存空間。
在java語言中,內存泄漏主要有兩種狀況:
(一)是在堆中申請的空間沒有被釋放;
(二)是對象已經再也不被使用,但仍然在內存中保留。
垃圾回收能夠解決(一),可是沒法保證再也不被使用的對象會被釋放。
在java語言中,內存泄漏的緣由不少:
1.靜態集合類:例如static HashMap和static Vector,因爲它們的生命週期與程序一致,那麼容器中的對象在程序結束以前將不能被釋放。
2.各類鏈接:例如數據庫鏈接、網絡鏈接和IO鏈接等,當再也不使用時,需調用close()方法來釋放鏈接。
3.監聽器:在釋放對象的同時沒有刪除相應的監聽器。
4.變量不合理的做用域:一個變量定義的做用範圍大於其使用範圍(例如一個本能夠定義爲方法內局部變量的變量,卻被定義爲程序對象內的全局變量),而且在使用完後沒有及時地把它設爲null。
棧:後進先出,從一端進,從一端出
隊列:先進先出,從隊尾進,從隊頭出
性能比較:
直接插入排序: 直接插入排序的核心思想就是:將數組中的全部元素依次跟前面已經排好的元素相比較,若是選擇的元素比已排序的元素小,則交換,直到所有元素都比較過。
嵌套兩層循環:
-
第一層循環:遍歷待比較的全部數組元素
-
第二層循環:將本輪選擇的元素(selected)與已經排好序的元素(ordered)相比較。
若是:selected > ordered,那麼將兩者交換
#直接插入排序def insert_sort(L):
#遍歷數組中的全部元素,其中0號索引元素默認已排序,所以從1開始
for x in range(1,len(L)):
#將該元素與已排序好的前序數組依次比較,若是該元素小,則交換
#range(x-1,-1,-1):從x-1倒序循環到0
for i in range(x-1,-1,-1):
#判斷:若是符合條件則交換
if L[i] > L[i+1]:
temp = L[i+1]
L[i+1] = L[i]
L[i] = temp
希爾排序的算法思想:將待排序數組按照步長gap進行分組,而後將每組的元素利用直接插入排序的方法進行排序;每次將gap折半減少,循環上述操做;當gap=1時,利用直接插入,完成排序。(步長gap是本身進行設定的,最後必須是1,而且步長的設定必須小於數組的長度)
嵌套三層循環:
一樣的:從上面的描述中咱們能夠發現:希爾排序的整體實現應該由三個循環完成:
-
第一層循環:將gap依次折半,對序列進行分組,直到gap=1
-
第2、三層循環:也即直接插入排序所須要的兩次循環。具體描述見上。
def insert_shell(L):
#初始化gap值,此處利用序列長度的通常爲其賦值
gap = (int)(len(L)/2)
#第一層循環:依次改變gap值對列表進行分組
while (gap >= 1):
#下面:利用直接插入排序的思想對分組數據進行排序
#range(gap,len(L)):從gap開始
for x in range(gap,len(L)):
#range(x-gap,-1,-gap):從x-gap開始與選定元素開始倒序比較,每一個比較元素之間間隔gap
for i in range(x-gap,-1,-gap):
#若是該組當中兩個元素知足交換條件,則進行交換
if L[i] > L[i+gap]:
temp = L[i+gap]
L[i+gap] = L[i]
L[i] =temp
#while循環條件折半
gap = (int)(gap/2)
簡單選擇排序算法:簡單選擇排序的基本思想:比較+交換。
-
從待排序序列中,找到關鍵字最小的元素;
-
若是最小元素不是待排序序列的第一個元素,將其和第一個元素互換;
-
從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束。
兩層嵌套循環:
第一層循環:依次遍歷序列當中的每個元素
第二層循環:將遍歷獲得的當前元素依次與餘下的元素進行比較,符合最小元素的條件,則交換。
# 簡單選擇排序def select_sort(L):#依次遍歷序列中的每個元素
for x in range(0,len(L)):
#將當前位置的元素定義此輪循環當中的最小值
minimum = L[x]
#將該元素與剩下的元素依次比較尋找最小元素
for i in range(x+1,len(L)):
if L[i] < minimum:
temp = L[i];
L[i] = minimum;
minimum = temp
#將比較後獲得的真正的最小值賦值給當前位置
L[x] = minimum
堆排序:(堆:本質是一種數組對象。特別重要的一點性質:任意的葉子節點小於(或大於)它全部的父節點。對此,又分爲大頂堆和小頂堆,大頂堆要求節點的元素都要大於其孩子,小頂堆要求節點元素都小於其左右孩子,二者對左右孩子的大小關係不作任何要求。利用堆排序,就是基於大頂堆或者小頂堆的一種排序方法。下面,咱們經過大頂堆來實現【大頂堆的根節點必定是最大的數字】)
基本思想:
堆排序能夠按照如下步驟來完成:
-
首先將序列構建稱爲大頂堆;
(這樣知足了大頂堆那條性質:位於根節點的元素必定是當前序列的最大值)
-
取出當前大頂堆的根節點,將其與序列末尾元素進行交換;
(此時:序列末尾的元素爲已排序的最大值;因爲交換了元素,當前位於根節點的堆並不必定知足大頂堆的性質)
-
對交換後的n-1個序列元素進行調整,使其知足大頂堆的性質;
-
重複2.3步驟,直至堆中只有1個元素爲止
(說白了就是每次找一個最大的,而後將它放在最後,而後在剩餘的元素中在找最大的,放在後面,依次類推)
冒泡排序:
-
將序列當中的左右元素,依次比較,保證右邊的元素始終大於左邊的元素;
( 第一輪結束後,序列最後一個元素必定是當前序列的最大值;)
-
對序列當中剩下的n-1個元素再次執行步驟1。
-
對於長度爲n的序列,一共須要執行n-1輪比較
(利用while循環能夠減小執行次數)
#冒泡排序def bubble_sort(L):
length = len(L)
#序列長度爲length,須要執行length-1輪交換
for x in range(1,length):
#對於每一輪交換,都將序列當中的左右元素進行比較#每輪交換當中,因爲序列最後的元素必定是最大的,所以每輪循環到序列未排序的位置便可
for i in range(0,length-x):
if L[i] > L[i+1]:
temp = L[i]
L[i] = L[i+1]
L[i+1] = temp
快速排序:
快排使用的是分治的思想:將要排序的數列分爲兩部分,設定一個分解值,將小於等於分解質的數放在左邊,大於的放在右邊,而後在從左邊的數列裏面設定一個分解值,而後在進行排序,右邊同理,而後在拍完的時候就是從小到大的序列順序
快速排序的基本思想:挖坑填數+分治法
-
從序列當中選擇一個基準數(pivot)
在這裏咱們選擇序列當中第一個數最爲基準數
-
將序列當中的全部數依次遍歷,比基準數大的位於其右側,比基準數小的位於其左側
-
重複步驟1.2,直到全部子集當中只有一個元素爲止。
用僞代碼描述以下:
1.i =L; j = R; 將基準數挖出造成第一個坑a[i]。
2.j--由後向前找比它小的數,找到後挖出此數填前一個坑a[i]中。
3.i++由前向後找比它大的數,找到後也挖出此數填到前一個坑a[j]中。
4.再重複執行2,3二步,直到i==j,將基準數填入a[i]中
#快速排序#L:待排序的序列;start排序的開始index,end序列末尾的index#對於長度爲length的序列:start = 0;end = length-1def quick_sort(L,start,end):
if start < end:
i , j , pivot = start , end , L[start]
while i < j:#從右開始向左尋找第一個小於pivot的值
while (i < j) and (L[j] >= pivot):
j = j-1#將小於pivot的值移到左邊
if (i < j):
L[i] = L[j]
i = i+1
#從左開始向右尋找第一個大於pivot的值
while (i < j) and (L[i] < pivot):
i = i+1#將大於pivot的值移到右邊
if (i < j):
L[j] = L[i]
j = j-1#循環結束後,說明 i=j,此時左邊的值全都小於pivot,右邊的值全都大於pivot#pivot的位置移動正確,那麼此時只需對左右兩側的序列調用此函數進一步排序便可#遞歸調用函數:依次對左側序列:從0 ~ i-1//右側序列:從i+1 ~ end
L[i] = pivot
#左側序列繼續排序
quick_sort(L,start,i-1)
#右側序列繼續排序
quick_sort(L,i+1,end)
-
歸併排序是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法的一個典型的應用。它的基本操做是:將已有的子序列合併,達到徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。
-
歸併排序其實要作兩件事:
-
分解----將序列每次折半拆分
-
合併----將劃分後的序列段兩兩排序合併
所以,歸併排序實際上就是兩個操做,拆分+合併
-
如何合併?
L[first...mid]爲第一段,L[mid+1...last]爲第二段,而且兩端已經有序,如今咱們要將兩端合成達到L[first...last]而且也有序。
-
首先依次從第一段與第二段中取出元素比較,將較小的元素賦值給temp[]
-
重複執行上一步,當某一段賦值結束,則將另外一段剩下的元素賦值給temp[]
-
此時將temp[]中的元素複製給L[],則獲得的L[first...last]有序
-
如何分解?
在這裏,咱們
# 歸併排序#這是合併的函數# 將序列L[first...mid]與序列L[mid+1...last]進行合併def mergearray(L,first,mid,last,temp):#對i,j,k分別進行賦值
i,j,k = first,mid+1,0#當左右兩邊都有數時進行比較,取較小的數
while (i <= mid) and (j <= last):
if L[i] <= L[j]:
temp[k] = L[i]
i = i+1
k = k+1
else:
temp[k] = L[j]
j = j+1
k = k+1#若是左邊序列還有數
while (i <= mid):
temp[k] = L[i]
i = i+1
k = k+1#若是右邊序列還有數
while (j <= last):
temp[k] = L[j]
j = j+1
k = k+1#將temp當中該段有序元素賦值給L待排序列使之部分有序
for x in range(0,k):
L[first+x] = temp[x]
# 這是分組的函數def merge_sort(L,first,last,temp):
if first < last:
mid = (int)((first + last) / 2)
#使左邊序列有序
merge_sort(L,first,mid,temp)
#使右邊序列有序
merge_sort(L,mid+1,last,temp)
#將兩個有序序列合併
mergearray(L,first,mid,last,temp)
# 歸併排序的函數def merge_sort_array(L):#聲明一個長度爲len(L)的空列表
temp = len(L)*[None]
#調用歸併排序
merge_sort(L,0,len(L)-1,temp)
泊松分佈: 若X服從參數爲λ的泊松分佈,則EX=DX=λ ,因此在題目中說」EX=DX「獲得的結果 爲true
ER圖中四種基本成分:實體(矩形框),關係(菱形框),屬性(橢圓形框),鏈接(直線)
Java的try,catch,finally的return問題:
先來看一段代碼:
public abstract class Test {
public static void main(String[] args) {
System.out.println(beforeFinally());
}
public static int beforeFinally(){
int a = 0;
try{
a = 1;
return a;
}finally{
a = 2;
}
}
}
/**output:
1
*/
從結果上看,貌似`finally` 裏的語句是在`return` 以後執行的,其實否則,實際上`finally` 裏的語句是在在`return` 以前執行的。那麼問題來了,既然是在以前執行,那爲何`a` 的值沒有被覆蓋了?
實際過程是這樣的:當程序執行到try{}語句中的return方法時,它會幹這麼一件事,將要返回的結果存儲到一個臨時棧中,而後程序不會當即返回,而是去執行finally{}中的程序, 在執行`a = 2`時,程序僅僅是覆蓋了a的值,但不會去更新臨時棧中的那個要返回的值 。執行完以後,就會通知主程序「finally的程序執行完畢,能夠請求返回了」,這時,就會將臨時棧中的值取出來返回。這下應該清楚了,要返回的值是保存至臨時棧中的。
再來看一個例子,稍微改下上面的程序:
public abstract class Test {
public static void main(String[] args) {
System.out.println(beforeFinally());
}
public static int beforeFinally(){
int a = 0;
try{
a = 1;
return a;
}finally{
a = 2;
return a;
}
}
}
/**output:
2
*/
在這裏,finally{}裏也有一個return,那麼在執行這個return時,就會更新臨時棧中的值。一樣,在執行完finally以後,就會通知主程序請求返回了,即將臨時棧中的值取出來返回。故返回值是2.
繼承初始化問題
java初始化順序。初始化子類必先初始化父類。子類的構造方法會隱式去調用 父類無參的構造方法(不會在代碼中顯示)。但若是父類沒有無參的構造方法,就必須在子類構造方法第一行顯示調用父類的有參構造方法。不然編譯失敗
折半查找斷定( 具備12個關鍵字的有序表,折半查找的平均查找長度())
一些推薦的小Tips:
JVM推薦的書籍:周志明的《深刻了解Java虛擬機》,我以爲講得比較明白比較細,我就看了前一部分已經徹底夠應付全部的面試問到的JVM問題了;
Spring書籍:《Spring源碼深度解析》,我我的以爲不是很好啃下來,可能須要一些Spring項目開發經驗的會好理解一些,硬啃的話不少地方可能看不太懂,建議更多地與實踐相結合;
Java併發:《Java併發編程實戰》,我以爲這一本講得也很好,也建議反覆地看反覆消化,對於面試問到的一些底層原理講解得很清楚;
數據庫:《高性能MySQL》,很厚,慢慢看吧。其實數據庫的話,更多問到的是索引機制這一塊,還有性能調優之類等,問的方向比較固定。
算法及代碼:建議牛客網或者LeeCode,我面試的時候堅持一天一道題,只要消化理解了,其實進步仍是特別大的,特別是思路上的提升真的很快。