① 這兩個方法來自不一樣的類分別是,sleep來自Thread類,和wait來自Object類。 sleep是Thread的靜態類方法,誰調用的誰去睡覺,即便在a線程裏調用b的sleep方法,實際上仍是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。 ② 鎖: 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程能夠使用同步控制塊或者方法。 sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其餘線程能夠佔用CPU。通常wait不會加時間限制,由於若是wait線程的運行資源不夠,再出來也沒用,要等待其餘線程調用notify/notifyAll喚醒等待池中的全部線程,纔會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)能夠用時間指定使它自動喚醒過來,若是時間不到只能調用interrupt()強行打斷。 Thread.sleep(0)的做用是「觸發操做系統馬上從新進行一次CPU競爭」。 ③ 使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用。 synchronized(x){ x.notify() //或者wait() }
下圖單位是bit,非字節 1B=8bit javascript
不能夠,由於String類有final修飾符,而final修飾的類是不能被繼承的,實現細節不容許改變。日常咱們定義的String str=」a」;其實和String str=new String(「a」)仍是有差別的。 css
前者默認調用的是String.valueOf來返回String實例對象,至於調用哪一個則取決於你的賦值,好比String num=1,調用的是 html
public static String valueOf(int i) { 前端
return Integer.toString(i); java
} mysql
後者則是調用以下部分: react
public String(String original) { jquery
this.value = original.value; linux
this.hash = original.hash; nginx
}
最後咱們的變量都存儲在一個char數組中
private final char value[];
String 字符串常量(final修飾,不可被繼承),String是常量,當建立以後即不能更改。(能夠經過StringBuffer和StringBuilder建立String對象(經常使用的兩個字符串操做類)。)
StringBuffer 字符串變量(線程安全),其也是final類別的,不容許被繼承,其中的絕大多數方法都進行了同步處理,包括經常使用的Append方法也作了同步處理(synchronized修飾)。其自jdk1.0起就已經出現。其toString方法會進行對象緩存,以減小元素複製開銷。
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuilder 字符串變量(非線程安全)其自jdk1.5起開始出現。與StringBuffer同樣都繼承和實現了一樣的接口和類,方法除了沒使用synch修飾之外基本一致,不一樣之處在於最後toString的時候,會直接返回一個新對象。
public String toString() {
// Create a copy, don’t share the array
return new String(value, 0, count);
}
ArrayList和LinkedList都實現了List接口,有如下的不一樣點:
一、ArrayList是基於索引的數據接口,它的底層是數組。它能夠以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每個元素都和它的前一個和後一個元素連接在一塊兒,在這種狀況下,查找某個元素的時間複雜度是O(n)。
二、相對於ArrayList,LinkedList的插入,添加,刪除操做速度更快,由於當元素被添加到集合任意位置的時候,不須要像數組那樣從新計算大小或者是更新索引。
三、LinkedList比ArrayList更佔內存,由於LinkedList爲每個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
父類靜態代變量--->父類靜態代碼塊--->子類靜態變量--->子類靜態代碼塊---->父類非靜態變量(父類實例成員變量)--->
父類構造函數--->子類非靜態變量(子類實例成員變量)--->子類構造函數。
測試demo:http://blog.csdn.net/u014042066/article/details/77574956
參閱個人博客《深刻理解類加載》:http://blog.csdn.net/u014042066/article/details/77394480
hashMap是線程不安全的,HashMap是數組+鏈表+紅黑樹(JDK1.8增長了紅黑樹部分)實現的,採用哈希表來存儲的,
參照該連接:https://zhuanlan.zhihu.com/p/21673805
JAVA8 的 ConcurrentHashMap 爲何放棄了分段鎖,有什麼問題嗎,若是你來設計,你如何設計。
參照:https://yq.aliyun.com/articles/36781
TreeMap和LinkedHashMap是有序的(TreeMap默認升序,LinkedHashMap則記錄了插入順序)。
參照:http://uule.iteye.com/blog/1522291
一、抽象類和接口都不能直接實例化,若是要實例化,抽象類變量必須指向實現全部抽象方法的子類對象,接口變量必須指向實現全部接口方法的類對象。
二、抽象類要被子類繼承,接口要被類實現。
三、接口只能作方法申明,抽象類中能夠作方法申明,也能夠作方法實現
四、接口裏定義的變量只能是公共的靜態的常量,抽象類中的變量是普通變量。
五、抽象類裏的抽象方法必須所有被子類所實現,若是子類不能所有實現父類抽象方法,那麼該子類只能是抽象類。一樣,一個實現接口的時候,如不能所有實現接口方法,那麼該類也只能爲抽象類。
六、抽象方法只能申明,不能實現。abstract void abc();不能寫成abstract void abc(){}。
七、抽象類裏能夠沒有抽象方法
八、若是一個類裏有抽象方法,那麼這個類只能是抽象類
九、抽象方法要被實現,因此不能是靜態的,也不能是私有的。
十、接口可繼承接口,並可多繼承接口,但類只能單根繼承。
繼承指的是一個類(稱爲子類、子接口)繼承另外的一個類(稱爲父類、父接口)的功能,並能夠增長它本身的新功能的能力,繼承是類與類或者接口與接口之間最多見的關係;在Java中此類關係經過關鍵字extends明確標識,在設計時通常沒有爭議性;
聚合是關聯關係的一種特例,他體現的是總體與部分、擁有的關係,即has-a的關係,此時總體與部分之間是可分離的,他們能夠具備各自的生命週期,部分能夠屬於多個總體對象,也能夠爲多個總體對象共享;好比計算機與CPU、公司與員工的關係等;表如今代碼層面,和關聯關係是一致的,只能從語義級別來區分;
參考:http://www.cnblogs.com/jiqing9006/p/5915023.html
IO是面向流的,NIO是面向緩衝區的
參考:https://zhuanlan.zhihu.com/p/23488863
http://developer.51cto.com/art/201103/252367.htm
http://www.jianshu.com/p/3f703d3d804c
參照:http://www.jianshu.com/p/3ea4a6b57f87?amp
http://blog.csdn.net/yongjian1092/article/details/7364451
反射中,Class.forName 和 ClassLoader 區別。
https://my.oschina.net/gpzhang/blog/486743
描述動態代理的幾種實現方式,分別說出相應的優缺點。
Jdk cglib jdk底層是利用反射機制,須要基於接口方式,這是因爲
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
Cglib則是基於asm框架,實現了無反射機制進行代理,利用空間來換取了時間,代理效率高於jdk
http://lrd.ele.me/2017/01/09/dynamic_proxy/
動態代理與 cglib 實現的區別
同上(基於invocationHandler和methodInterceptor)
爲何 CGlib 方式能夠對接口實現代理。
同上
final 的用途
類、變量、方法
http://www.importnew.com/7553.html
寫出三種單例模式實現。
懶漢式單例,餓漢式單例,雙重檢查等
參考:https://my.oschina.net/dyyweb/blog/609021
同時複寫hashcode和equals方法,優點能夠添加自定義邏輯,且沒必要調用超類的實現。
參照:http://java-min.iteye.com/blog/1416727
訪問修飾符,主要標示修飾塊的做用域,方便隔離防禦
同一個類 同一個包 不一樣包的子類 不一樣包的非子類
Private √
Default √ √
Protected √ √ √
Public √ √ √ √
public: Java語言中訪問限制最寬的修飾符,通常稱之爲「公共的」。被其修飾的類、屬性以及方法不
僅能夠跨類訪問,並且容許跨包(package)訪問。
private: Java語言中對訪問權限限制的最窄的修飾符,通常稱之爲「私有的」。被其修飾的類、屬性以
及方法只能被該類的對象訪問,其子類不能訪問,更不能容許跨包訪問。
protect: 介於public 和 private 之間的一種訪問修飾符,通常稱之爲「保護形」。被其修飾的類、
屬性以及方法只能被類自己的方法及子類訪問,即便子類在不一樣的包中也能夠訪問。
default:即不加任何訪問修飾符,一般稱爲「默認訪問模式「。該模式下,只容許在同一個包中進行訪
問。
http://www.oschina.net/translate/java-copy-shallow-vs-deep-in-which-you-will-swim
數組和鏈表數據結構描述,各自的時間複雜度
http://blog.csdn.net/snow_wu/article/details/53172721
error 和 exception 的區別,CheckedException,RuntimeException 的區別
http://blog.csdn.net/woshixuye/article/details/8230407
請列出 5 個運行時異常。
同上
類加載無須等到「首次使用該類」時加載,jvm容許預加載某些類。。。。
http://www.cnblogs.com/jasonstorm/p/5663864.html
參考上邊試題
泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數,泛型的好處是在編譯的時候檢查類型安全,而且全部的強制轉換都是自動和隱式的,以提升代碼的重用率
http://baike.baidu.com/item/java%E6%B3%9B%E5%9E%8B
hashcode
hashcode()方法提供了對象的hashCode值,是一個native方法,返回的默認值與System.identityHashCode(obj)一致。
一般這個值是對象頭部的一部分二進制位組成的數字,具備必定的標識對象的意義存在,但毫不定於地址。
做用是:用一個數字來標識對象。好比在HashMap、HashSet等相似的集合類中,若是用某個對象自己做爲Key,即要基於這個對象實現Hash的寫入和查找,那麼對象自己如何實現這個呢?就是基於hashcode這樣一個數字來完成的,只有數字才能完成計算和對比操做。
hashcode是否惟一
hashcode只能說是標識對象,在hash算法中能夠將對象相對離散開,這樣就能夠在查找數據的時候根據這個key快速縮小數據的範圍,但hashcode不必定是惟一的,因此hash算法中定位到具體的鏈表後,須要循環鏈表,而後經過equals方法來對比Key是不是同樣的。
equals與hashcode的關係
equals相等兩個對象,則hashcode必定要相等。可是hashcode相等的兩個對象不必定equals相等。
https://segmentfault.com/a/1190000004520827
有
底層是基於hashmap實現的
http://wiki.jikexueyuan.com/project/java-collection/hashset.html
什麼是序列化,怎麼序列化,爲何序列化,反序列化會遇到什麼問題,如何解決。
http://www.importnew.com/17964.html
① 歷史緣由: Hashtable是給予陳舊的Dictonary類的, HashMap是Java1.2引進的Map接口的一個實現 ② HashMap容許空的鍵值對, 而HashTable不容許 ③ HashTable同步,而HashMap非同步,效率上比HashTable要高
類似點:
這兩種同步方式有不少類似之處,它們都是加鎖方式同步,並且都是阻塞式的同步,也就是說當若是一個線程得到了對象鎖,進入了同步塊,其餘訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進行線程阻塞和喚醒的代價是比較高的(操做系統須要在用戶態與內核態之間來回切換,代價很高,不過能夠經過對鎖優化進行改善)。
區別:
這兩種方式最大區別就是對於Synchronized來講,它是java語言的關鍵字,是原生語法層面的互斥,須要jvm實現。而ReentrantLock它是JDK 1.5以後提供的API層面的互斥鎖,須要lock()和unlock()方法配合try/finally語句塊來完成。
Synchronized進過編譯,會在同步塊的先後分別造成monitorenter和monitorexit這個兩個字節碼指令。在執行monitorenter指令時,首先要嘗試獲取對象鎖。若是這個對象沒被鎖定,或者當前線程已經擁有了那個對象鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器爲0時,鎖就被釋放了。若是獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另外一個線程釋放爲止。
因爲ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級功能,主要有如下3項:
1.等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程能夠選擇放棄等待,這至關於Synchronized來講能夠避免出現死鎖的狀況。
2.公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序得到鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構造函數是建立的非公平鎖,能夠經過參數true設爲公平鎖,但公平鎖表現的性能不是很好。
3.鎖綁定多個條件,一個ReentrantLock對象能夠同時綁定對個對象。
什麼狀況下會發生棧內存溢出。
若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError異常。 若是虛擬機在動態擴展棧時沒法申請到足夠的內存空間,則拋出OutOfMemoryError異常。
參照:http://wiki.jikexueyuan.com/project/java-vm/storage.html
JVM 的內存結構,Eden 和 Survivor 比例。
eden 和 survior 是按8比1分配的
http://blog.csdn.net/lojze_ly/article/details/49456255
jvm 中一次完整的 GC 流程是怎樣的,對象如何晉升到老年代,說說你知道的幾種主要的jvm 參數。
對象誕生即新生代->eden,在進行minor gc過程當中,若是依舊存活,移動到from,變成Survivor,進行標記代數,如此檢查必定次數後,晉升爲老年代,
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html
http://ifeve.com/useful-jvm-flags/
https://wangkang007.gitbooks.io/jvm/content/jvmcan_shu_xiang_jie.html
你知道哪幾種垃圾收集器,各自的優缺點,重點講下 cms,包括原理,流程,優缺點
Serial、parNew、ParallelScavenge、SerialOld、ParallelOld、CMS、G1
https://wangkang007.gitbooks.io/jvm/content/chapter1.html
垃圾回收算法的實現原理。
http://www.importnew.com/13493.html
當出現了內存溢出,你怎麼排錯。
首先分析是什麼類型的內存溢出,對應的調整參數或者優化代碼。
https://wangkang007.gitbooks.io/jvm/content/4jvmdiao_you.html
JVM 內存模型的相關知識瞭解多少,好比重排序,內存屏障,happen-before,主內存,工做內存等。
內存屏障:爲了保障執行順序和可見性的一條cpu指令
重排序:爲了提升性能,編譯器和處理器會對執行進行重拍
happen-before:操做間執行的順序關係。有些操做先發生。
主內存:共享變量存儲的區域便是主內存
工做內存:每一個線程copy的本地內存,存儲了該線程以讀/寫共享變量的副本
http://ifeve.com/java-memory-model-1/
http://www.jianshu.com/p/d3fda02d4cae
http://blog.csdn.net/kenzyq/article/details/50918457
簡單說說你瞭解的類加載器。
類加載器的分類(bootstrap,ext,app,curstom),類加載的流程(load-link-init)
http://blog.csdn.net/gjanyanlig/article/details/6818655/
講講 JAVA 的反射機制。
Java程序在運行狀態能夠動態的獲取類的全部屬性和方法,並實例化該類,調用方法的功能
http://baike.baidu.com/link?url=C7p1PeLa3ploAgkfAOK-4XHE8HzQuOAB7K5GPcK_zpbAa_Aw-nO3997K1oir8N–1_wxXZfOThFrEcA0LjVP6wNOwidVTkLBzKlQVK6JvXYvVNhDWV9yF-NIOebtg1hwsnagsjUhOE2wxmiup20RRa#7
大家線上應用的 JVM 參數有哪些。
-server Xms6000M -Xmx6000M -Xmn500M -XX:PermSize=500M -XX:MaxPermSize=500M -XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=90 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log
g1 和 cms 區別,吞吐量優先和響應優先的垃圾收集器選擇。
Cms是以獲取最短回收停頓時間爲目標的收集器。基於標記-清除算法實現。比較佔用cpu資源,切易形成碎片。
G1是面向服務端的垃圾收集器,是jdk9默認的收集器,基於標記-整理算法實現。可利用多核、多cpu,保留分代,實現可預測停頓,可控。
http://blog.csdn.net/linhu007/article/details/48897597
請解釋以下 jvm 參數的含義:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20
XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。
Server模式啓動
最小堆內存512m
最大512m
每一個線程棧空間1m
永久代256
最大永久代256
最大轉爲老年代檢查次數20
Cms回收開啓時機:內存佔用80%
命令JVM不基於運行時收集的數據來啓動CMS垃圾收集週期
簡單講講 tomcat 結構,以及其類加載器流程。
Server- –多個service
Container級別的:–>engine–》host–>context
Listenter
Connector
Logging、Naming、Session、JMX等等
經過WebappClassLoader 加載class
http://www.ibm.com/developerworks/cn/java/j-lo-tomcat1/
http://blog.csdn.net/dc_726/article/details/11873343
http://www.cnblogs.com/xing901022/p/4574961.html
http://www.jianshu.com/p/62ec977996df
tomcat 如何調優,涉及哪些參數。
硬件上選擇,操做系統選擇,版本選擇,jdk選擇,配置jvm參數,配置connector的線程數量,開啓gzip壓縮,trimSpaces,集羣等
http://blog.csdn.net/lifetragedy/article/details/7708724
講講 Spring 加載流程。
經過listener入口,核心是在AbstractApplicationContext的refresh方法,在此處進行裝載bean工廠,bean,建立bean實例,攔截器,後置處理器等。
https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
講講 Spring 事務的傳播屬性。
七種傳播屬性。
事務傳播行爲
所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。在TransactionDefinition定義中包括了以下幾個表示傳播行爲的常量:
TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
Spring 如何管理事務的。
編程式和聲明式
同上
Spring 怎麼配置事務(具體說出一些關鍵的 xml 元素)。
說說你對 Spring 的理解,非單例注入的原理?它的生命週期?循環注入的原理, aop 的實現原理,說說 aop 中的幾個術語,它們是怎麼相互工做的。
核心組件:bean,context,core,單例注入是經過單例beanFactory進行建立,生命週期是在建立的時候經過接口實現開啓,循環注入是經過後置處理器,aop其實就是經過反射進行動態代理,pointcut,advice等。
Aop相關:http://blog.csdn.net/csh624366188/article/details/7651702/
Springmvc 中 DispatcherServlet 初始化過程。
入口是web.xml中配置的ds,ds繼承了HttpServletBean,FrameworkServlet,經過其中的init方法進行初始化裝載bean和實例,initServletBean是實際完成上下文工做和bean初始化的方法。
http://www.mamicode.com/info-detail-512105.html
Java虛擬機在執行Java程序的時候會把他所管理的內存劃分爲若干個不一樣的數據區域,各個區域有各自的用途,以及建立和銷燬的時間。有的區域隨着虛擬機進程的啓動而存在,有的區域則依賴用戶線程的啓動和結束而建立和銷燬。
Java虛擬機會把運行時的數據區域分爲如下幾個區域:
程序計數器:
程序計數器是一塊很小的內存空間,表明着當前線程執行的字節碼的行號指示器,記錄着所執行的行號,java虛擬機執行的是由後綴名爲.java的文件編譯而來的.class文件(字節碼文件),因此字節碼解釋器根據程序計數器來執行字節碼文件。每一個線程都有本身的程序解釋器,這樣才能保證程序的正確執行,也就是說程序計數器是線程私有的。
Java虛擬機棧:
java虛擬機棧是用來描述java方法的內存模型,每一個方法在執行的同時都會建立一個棧幀,而這個棧幀存儲的是方法中的局部變量,操做的數據,方法的入口返回值等信息,當一個方法被調用的時候,就表明着棧幀的入棧直至方法的結束表明着棧幀的出棧。由於虛擬機棧存儲的數據決定了他也是線程私有的,每一個線程都擁有一個虛擬機棧記錄着方法的內容。咱們平時所說的棧就是指的是虛擬機棧,其中存儲着基本數據類型和指向堆內存中對象的指針(對象的引用)。
本地方法棧:
這塊區域和虛擬機棧執行的操做實際上是一致的,可是他們之間的服務對象不同,虛擬機棧爲java方法服務,而本地方法棧爲native方法服務,咱們在看源碼的時候常常了一看到用native關鍵字修飾的方法,這種方法的實現是用c/c++實現的,咱們在平時是看不到他的源碼實現的。
Java堆:
堆內存是這幾塊內存區域中最大的一塊,堆內存存在的目的是存放對象的實例(經過new建立的對象,對象的引用放在虛擬機棧中指向堆中的實例),在虛擬機啓動的時候堆內存也就被建立了,這塊內存被全部線程共享,在虛擬機運行期間的全部線程建立的對象的實例都被存儲在堆內存中。既然堆被線程所共享,那麼線程建立的對象不能一直存放在這裏,總會有裝不下的時候,在必定條件下,java虛擬機會觸發垃圾回收機制(GC),來回收這裏被看做沒有用的對象,虛擬機所管理的垃圾回收器老是會對這塊區域進行管理操做。關於垃圾回收(GC)機制能夠看另外一篇文章:Java GC垃圾回收機制
方法區:
方法區和堆內存同樣,是各個線程共享的數據區域,看到這個方法區這個名字很快能想到這個區域存方法信息,事實上方法區存放的數據不少,包括被虛擬機加載的類信息,用final修飾的常量,String對象,用static修飾的靜態變量。
運行時常量池:
準確的說這塊區域屬於方法區,也就受到了方法區的空間限制,以前所說的String對象,就是字符串常量就是存放在這裏,編譯期生成各類字面值和符號引用,將在類價在後放入方法區的運行時常量池的。運行時常量池的存儲具備動態性,並非在類加載時才能放入數據,在程序運行期間也能夠有新的常量放入。String類中的intern()方法就是這種特色,詳看以前轉載的一篇文章:String的intern()方法詳解
直接內存:
這塊區域和java中的新的io方式(NIO)有關,不屬於虛擬機的運行時數據區。NIO是一種基於通道,緩衝區的io方式。
XSS是一種常常出如今web應用中的計算機安全漏洞,它容許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。好比這些代碼包括HTML代碼和客戶端腳本。攻擊者利用XSS漏洞旁路掉訪問控制——例如同源策略(same origin policy)。這種類型的漏洞因爲被黑客用來編寫危害性更大的網絡釣魚(Phishing)攻擊而變得廣爲人知。對於跨站腳本攻擊,黑客界共識是:跨站腳本攻擊是新型的「緩衝區溢出攻擊「,而JavaScript是新型的「ShellCode」。
CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本(XSS),但它與XSS很是不一樣,XSS利用站點內的信任用戶,而CSRF則經過假裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊每每不大流行(所以對其進行防範的資源也至關稀少)和難以防範,因此被認爲比XSS更具危險性。
SQL注入,就是經過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。具體來講,它是利用現有應用程序,將(惡意的)SQL命令注入到後臺數據庫引擎執行的能力,它能夠經過在Web表單中輸入(惡意)SQL語句獲得一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。 [1] 好比先前的不少影視網站泄露VIP會員密碼大多就是經過WEB表單遞交查詢字符暴出的,這類表單特別容易受到SQL注入式攻擊.
有兩種實現方法,分別是繼承Thread類與實現Runnable接口用synchronized關鍵字修飾同步方法反對使用stop(),是由於它不安全。它會解除由線程獲取的全部鎖定,並且若是對象處於一種不連貫狀態,那麼其餘線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這以前得到的鎖定。此時,其餘任何線程都不能訪問鎖定的資源,除非被「掛起」的線程恢復運行。
對任何線程來講,若是它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會形成死鎖。因此不該該使用suspend(),而應在本身的Thread類中置入一個標誌,指出線程應該活動仍是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()從新啓動線程。
① throw表明動做,表示拋出一個異常的動做;throws表明一種狀態,表明方法可能有異常拋出 ② throw用在方法實現中,而throws用在方法聲明中 ③ throw只能用於拋出一種異常,而throws能夠拋出多個異常
forward: A -> B -> C redirect: A -> B A -> C 1.從地址欄顯示來講 forward是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,而後把這些內容再發給瀏覽器.瀏覽器根本不知道服務器發送的內容從哪裏來的,因此它的地址欄仍是原來的地址. redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器從新去請求那個地址.因此地址欄顯示的是新的URL. 2.從數據共享來講 forward:轉發頁面和轉發到的頁面能夠共享request裏面的數據. redirect:不能共享數據. 3.從運用地方來講 forward:通常用於用戶登錄的時候,根據角色轉發到相應的模塊. redirect:通常用於用戶註銷登錄時返回主頁面和跳轉到其它的網站等. 4.從效率來講 forward:高. redirect:低.
內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;好比申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。 內存泄露 memory leak,是指程序在申請內存後,沒法釋放已申請的內存空間,一次內存泄露危害能夠忽略,但內存泄露堆積後果很嚴重,不管多少內存,早晚會被佔光。 memory leak會最終會致使out of memory!內存泄漏是指你向系統申請分配內存進行使用(new),但是使用完了之後卻不歸還(delete),結果你申請到的那塊內存你本身也不能再訪問(也許你把它的地址給弄丟了),而系統也不能再次將它分配給須要的程序。
若是不被重寫(原生)的hashCode和equals是什麼樣的?
爲何須要重寫equals和hashCode方法?
在咱們的業務系統中判斷對象時有時候須要的不是一種嚴格意義上的相等,而是一種業務上的對象相等。在這種狀況下,原生的equals方法就不能知足咱們的需求了
因此這個時候咱們須要重寫equals方法,來知足咱們的業務系統上的需求。那麼爲何在重寫equals方法的時候須要重寫hashCode方法呢?
咱們先來看一下Object.hashCode的通用約定(摘自《Effective Java》第45頁)
1.在一個應用程序執行期間,若是一個對象的equals方法作比較所用到的信息沒有被修改的話,那麼,對該對象調用hashCode方法屢次,它必須始終如一地返回 同一個整數。在同一個應用程序的屢次執行過程當中,這個整數能夠不一樣,即這個應用程序此次執行返回的整數與下一次執行返回的整數能夠不一致。
2.若是兩個對象根據equals(Object)方法是相等的,那麼調用這兩個對象中任一個對象的hashCode方法必須產生一樣的整數結果。
3.若是兩個對象根據equals(Object)方法是不相等的,那麼調用這兩個對象中任一個對象的hashCode方法,不要求必須產生不一樣的整數結果。然而,程序員應該意識到這樣的事實,對於不相等的對象產生大相徑庭的整數結果,有可能提升散列表(hash table)的性能。
若是隻重寫了equals方法而沒有重寫hashCode方法的話,則會違反約定的第二條:相等的對象必須具備相等的散列碼(hashCode)
同時對於HashSet和HashMap這些基於散列值(hash)實現的類。HashMap的底層處理機制是以數組的方法保存放入的數據的(Node<K,V>[] table),其中的關鍵是數組下標的處理。數組的下標是根據傳入的元素hashCode方法的返回值再和特定的值異或決定的。
若是該數組位置上已經有放入的值了,且傳入的鍵值相等則不處理,若不相等則覆蓋原來的值,若是數組位置沒有條目,則插入,並加入到相應的鏈表中。檢查鍵是否存在也是根據hashCode值來肯定的。因此若是不重寫hashCode的話,可能致使HashSet、HashMap不能正常的運做、
若是咱們將某個自定義對象存到HashMap或者HashSet及其相似實現類中的時候,若是該對象的屬性參與了hashCode的計算,那麼就不能修改該對象參數hashCode計算的屬性了。有可能會移除不了元素,致使內存泄漏。
擴展:
在重寫equals方法的時候,須要遵照下面的通用約定:
一、自反性。
對於任意的引用值x,x.equals(x)必定爲true。
二、對稱性。
對於任意的引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)也必定返回true。
三、傳遞性。
對於任意的引用值x、y和z,若是x.equals(y)返回true,而且y.equals(z)也返回true,那麼x.equals(z)也必定返回true。
四、一致性。
對於任意的引用值x和y,若是用於equals比較的對象沒有被修改的話,那麼,對此調用x.equals(y)要麼一致地返回true,要麼一致的返回false。
五、對於任意的非空引用值x,x.equals(null)必定返回false。
重寫hashCode方法的大體方式:
a、把某個非零常數值,好比說17(最好是素數),保存在一個叫result的int類型的變量中。
b、對於對象中每個關鍵域f(值equals方法中考慮的每個域),完成一些步驟:
一、爲該域計算int類型的散列嗎c:
1)、若是該域是boolean類型,則計算(f?0:1)。
2)、若是該域是byte、char、short或者int類型,則計算(int)f。
3)、若是該域是float類型,則計算Float.floatToIntBits(f)。
4)、若是該域是long類型,則計算(int)(f ^ (f>>>32))。
5)、若是該域是double類型,則計算Double.doubleToLongBits(f)獲得一個long類型的值,而後按照步驟4,對該long型值計算散列值。
6)、若是該域是一個對象引用,而且該類的equals方法經過遞歸調用equals的方式來比較這個域,則一樣對這個域遞歸調用hashCode。若是要求一個更爲複雜的比較,則爲這個域計算一個「規範表示」,而後針對這個範式表示調用hashCode。若是這個域的值爲null,則返回0(或者其餘某個常數)
7)、若是該域是一個數組,則把每個元素當作單獨的域來處理。也就是說,遞歸地應用上述規則,對每一個重要的元素計算一個散列碼,而後根據步驟下面的作法把這些散列值組合起來。
二、按照下面的公式,把步驟1中計算獲得的散列碼C組合到result中:
result = 31*result+c。
c、返回result。
d、寫完hashCode方法以後,問本身「是否相等的實例具備相等的散列碼」。若是不是的話,找出緣由,並修改。
能夠經過org.apache.commons.lang.builder.HashCodeBuilder這個工具類來方便的重寫hashCode方法。
枚舉類型的那些事:http://www.javashuo.com/article/p-gaxjjntx-a.html
枚舉類型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚舉類型的名稱。枚舉類型的每個值都將映射到 protected Enum(String name, int ordinal) 構造函數中,在這裏,每一個值的名稱都被轉換成一個字符串,而且序數設置表示了此設置被建立的順序。
301:- 永久移動。請求的資源已被永久的移動到新URI,返回信息會包括新的URI,瀏覽器會自動定向到新URI。從此任何新的請求都應使用新的URI代替
302:- 臨時移動。與301相似。但資源只是臨時被移動。客戶端應繼續使用原有URI
304:- 若是客戶端發送了一個帶條件的GET 請求且該請求已被容許,而文檔的內容(自上次訪問以來或者根據請求的條件)並無改變,則服務器應當返回這個304狀態碼..
403:- 表明客戶端錯誤,指的是服務器端有能力處理該請求,可是拒絕受權訪問。
404:- 服務器沒法根據客戶端的請求找到資源(網頁)。經過此代碼,網站設計人員可設置"您所請求的資源沒法找到"的個性頁面
500:- 服務器內部錯誤,沒法完成請求
1.start()方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼:
經過調用Thread類的start()方法來啓動一個線程,這時此線程是處於就緒狀態,並無運行。
而後經過此Thread類調用方法run()來完成其運行操做的,這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容,
Run方法運行結束,此線程終止,而CPU再運行其它線程。
2.run()方法看成普通方法的方式調用,程序仍是要順序執行,仍是要等待run方法體執行完畢後纔可繼續執行下面的代碼:
而若是直接用Run方法,這只是調用一個方法而已,程序中依然只有主線程--這一個線程,其程序執行路徑仍是隻有一條,
這樣就沒有達到寫線程的目的。
若是一個類繼承Thread,則不適合資源共享。可是若是實現了Runable接口的話,則很容易的實現資源共享。
實現Runnable接口比繼承Thread類所具備的優點:
1):適合多個相同的程序代碼的線程去處理同一個資源
2):能夠避免java中的單繼承的限制
3):增長程序的健壯性,代碼能夠被多個線程共享,代碼和數據獨立。
提醒一下你們:main方法其實也是一個線程。在java中因此的線程都是同時啓動的,至於何時,哪一個先執行,徹底看誰先獲得CPU的資源。在java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。由於每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每個JVM實際就是在操做系統中啓動了一個進程。
在java程序中,只要前臺有一個線程在運行,整個java程序進程不會小時,因此此時能夠設置一個後臺線程,這樣即便java進程小時了,此後臺線程依然可以繼續運行。
線程狀態從大的方面來講,可歸結爲:初始狀態、可運行狀態、不可運行狀態和消亡狀態,具體可細分爲上圖所示7個狀態,說明以下:
1)線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable接口,但無論怎樣,當咱們new了thread實例後,線程就進入了初始狀態;
2)當該對象調用了start()方法,就進入可運行狀態;
3)進入可運行狀態後,當該對象被操做系統選中,得到CPU時間片就會進入運行狀態;
4)進入運行狀態後case就比較多,大體有以下情形: ﹒run()方法或main()方法結束後,線程就進入終止狀態; 當線程調用了自身的sleep()方法或其餘線程的join()方法,就會進入阻塞狀態(該狀態既停 止當前線程,但並不釋放所佔有的資源)。當
sleep()結束或join()結束後,該線程進入可運行狀態,繼續等待OS分配時間片; 當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被鎖牢(synchroniza,lock),將會當即進入鎖池狀態,等待獲取鎖標記(這時的鎖池裏也許已經有了其餘線
程在等待獲取鎖標記,這時它們處於隊列狀態,既先到先得),一旦線程得到鎖標記後,就轉入可運行狀態,等待OS分配 CPU時間片; 當線程調用wait()方法後會進入等待隊列(進入這個狀態會釋放所佔有的全部資源,與阻塞狀態不一樣),進入這個狀態後,
是不能自動喚醒的,必須依靠其餘線程調用notify()或notifyAll()方法才能被喚醒(因爲notify()只是喚醒一個線程,但咱們由不能肯定具體喚醒的是哪個線程,也許咱們須要喚醒的線程不可以被喚醒,所以在實際使用時,通常都用notifyAll()方法,喚醒
有所線程),線程被喚醒後會進入鎖池,等待獲取鎖標記。 當線程調用stop方法,便可使線程進入消亡狀態,可是因爲stop方法是不安全的,不鼓勵使用,你們能夠經過run方法裏的條件變通實現線程的 stop。
boolean數據類型非true即false。
這個數據類型表示1 bit,可是它的大小並無精肯定義。
《Java虛擬機規範》中如是說:「雖然定義了boolean這種數據類型,可是隻對它提供了很是有限的支持。在Java虛擬機中沒有任何供boolean值專用的字節碼指令,Java語言表達式所操做的boolean值,在編譯以後都使用Java虛擬機中的int數據類型來代替,而boolean數組將會被編碼成Java虛擬機的byte數組,每一個元素boolean元素佔8位」。這樣咱們能夠得出boolean類型單獨使用是4個字節,在數組中又是1個字節。
那虛擬機爲何要用int來代替boolean呢?爲何不用byte或short,這樣不是更節省內存空間嗎?
實際上,使用int的緣由是,對於當下32位的CPU來講,一次進行32位的數據交換更加高效。
綜上,咱們能夠知道:官方文檔對boolean類型沒有給出精確的定義,《Java虛擬機規範》給出了「單獨時使用4個字節,boolean數組時1個字節」的定義,具體還要看虛擬機實現是否按照規範來,因此1個字節、4個字節都是有可能的。這實際上是一種時空權衡。 boolean類型的封裝類是Boolean。
案例演示 >>,>>>,<<的用法:
<<:左移左邊最高位丟棄,右邊補齊0
>>:右移最高位是0,左邊補齊0;最高爲是1,左邊補齊1
>>>:無符號右移 不管最高位是0仍是1,左邊補齊0
最有效率的算出2 * 8的結果
//最有效率的算出2 * 8的結果
System.out.println(2 << 3);
《Think in Java》中說:「關係操做符生成的是一個boolean結果,它們計算的是操做數的值之間的關係」。 "=="判斷的是兩個對象的內存地址是否同樣,適用於原始數據類型和枚舉類型(它們的變量存儲的是值自己,而引用類型變量存儲的是引用);equals是Object類的方法,Object對它的實現是比較內存地址,咱們能夠重寫這個方法來自定義「相等」這個概念。好比類庫中的String、Date等類就對這個方法進行了重寫。 綜上,對於枚舉類型和原始數據類型的相等性比較,應該使用"==";對於引用類型的相等性比較,應該使用equals方法。
請參考散列表的基本原理與實現
更詳細的說明請參考官方文檔,對相關數據結構不太熟悉的同窗能夠參考《算法導論》或其餘相關書籍。
簡單的說,HashMap的底層實現是「基於拉鍊法的散列表」。
詳細分析請參考 Map源碼解析之HashMap源碼分析
ConcurrentHashMap是支持併發讀寫的HashMap,它的特色是讀取數據時無需加鎖,寫數據時能夠保證加鎖粒度儘量的小。因爲其內部採用「分段存儲」,只需對要進行寫操做的數據所在的「段」進行加鎖。關於ConcurrentHashMap底層實現的詳細分析請參考 Java併發編程:併發容器之ConcurrentHashMap
會執行。只有兩種狀況finally塊中的語句不會被執行:
Java中的異常層次結構
Java中的異常層次結構以下圖所示:
咱們能夠看到Throwable類是異常層級中的基類。
Error類表示內部錯誤,這類錯誤使咱們沒法控制的;Exception表示異常,RuntimeException及其子類屬於未檢查異常,這類異常包括ArrayIndexOutOfBoundsException、NullPointerException等,咱們應該經過條件判斷等方式語句避免未檢查異常的發生。IOException及其子類屬於已檢查異常,編譯器會檢查咱們是否爲全部可能拋出的已檢查異常提供了異常處理器,若沒有則會報錯。對於未檢查異常,咱們無需捕獲(固然Java也容許咱們捕獲,但咱們應該作的事避免未檢查異常的發生)。
三大特徵:封裝、繼承、多態。
抽象類中能夠包含屬性,方法(包含抽象方法與有着具體實現的方法),常量;接口只能包含常量和方法聲明。
抽象類中的方法和成員變量能夠定義可見性(好比 public、private等);而接口中的方法只能爲public(缺省爲public)。
一個子類只能有一個父類(具體類或抽象類);而一個接口能夠繼承一個多個接口,一個類也能夠實現多個接口。
子類中實現父類中的抽象方法時,可見性能夠大於等於父類中的;而接口實現類中的接口 方法的可見性只能與接口中相同(public)。
靜態內部類不會持有外圍類的引用,而非靜態內部類會隱式持有外圍類的一個引用。
所謂多態,指的就是父類引用指向子類對象,調用方法時會調用子類的實現而不是父類的實現。多態的實現的關鍵在於「動態綁定」。詳細介紹請戳 Java動態綁定的內部實現機制
Java中能夠對類、對象、方法或是代碼塊上鎖。
給出「生產者-消費者」問題的一種解決方案
使用阻塞隊列:
public class BlockingQueueTest { private int size = 20; private ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(size); public static void main(String[] args) { BlockingQueueTest test = new BlockingQueueTest(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { while(true){ try { //從阻塞隊列中取出一個元素 queue.take(); System.out.println("隊列剩餘" + queue.size() + "個元素"); } catch (InterruptedException e) { } } } } class Producer extends Thread{ @Override public void run() { while (true) { try { //向阻塞隊列中插入一個元素 queue.put(1); System.out.println("隊列剩餘空間:" + (size - queue.size())); } catch (InterruptedException e) { } } } } }
ThreadLocal的做用是提供線程內的局部變量,在多線程環境下訪問時能保證各個線程內的ThreadLocal變量各自獨立。也就是說,每一個線程的ThreadLocal變量是本身專用的,其餘線程是訪問不到的。ThreadLocal最經常使用於如下這個場景:多線程環境下存在對非線程安全對象的併發訪問,並且該對象不須要在線程間共享,可是咱們不想加鎖,這時候能夠使用ThreadLocal來使得每一個線程都持有一個該對象的副本。
關於這個問題咱們直接看《Effective Java》給咱們作的解答:
for-each可以讓代碼更加清晰,而且減小了出錯的機會。 下面的慣用代碼適用於集合與數組類型:
for (Element e : elements) {
doSomething(e);
}
使用for-each循環與常規的for循環相比,並不存在性能損失,即便對數組進行迭代也是如此。實際上,在有些場合下它還能帶來微小的性能提高,由於它只計算一次數組索引的上限。
Java IO是面向流的,這意味着咱們須要每次從流中讀取一個或多個字節,直到讀取完全部字節;NIO是面向緩衝的,也就是說會把數據讀取到一個緩衝區中,而後對緩衝區中的數據進行相應處理。
Java NIO中存在一個稱爲選擇器(selector)的東西,它容許你把多個通道(channel)註冊到一個選擇器上,而後使用一個線程來監視這些通道:若這些通道里有某個準備好能夠開始進行讀或寫操做了,則開始對相應的通道進行讀寫。而在等待某通道變爲可讀/寫期間,請求對通道進行讀寫操做的線程能夠去幹別的事情。
反射的做用歸納地說是運行時獲取類的各類定義信息,好比定義了哪些屬性與方法。原理是經過類的class對象來獲取它的各類信息。
關於泛型機制的詳細介紹請直接戳 Java核心技術點之泛型
所謂「設計模式」,不過是面向對象編程中一些經常使用的軟件設計手法,而且通過實踐的檢驗,這些設計手法在各自的場景下能解決一些需求,所以它們就成爲了現在廣爲流傳的」設計模式「。也就是說,正式由於在某些場景下產生了一些棘手的問題,才催生了相應的設計模式。明確了這一點,咱們在學習某種設計模式時要充分理解它產生的背景以及它所解決的主要矛盾是什麼。
經常使用的設計模式能夠分爲如下三大類:
註解能夠看做是「加強版的註釋」,它能夠向編譯器、虛擬機說明一些事情。 註解是描述Java代碼的代碼,它可以被編譯器解析,註解處理工具在運行時也可以解析註解。註解自己是「被動」的信息,只有主動解析它纔有意義。 除了向編譯器/虛擬機傳遞信息,咱們也能夠使用註解來生成一些「模板化」的代碼。
1.一、HashMap的實現原理
1.1.一、結構
HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體,HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。以下圖所示:
image.png
當新建一個HashMap的時候,就會初始化一個數組。哈希表是由數組+鏈表組成的,一個長度爲16的數組中,每一個元素存儲的是一個鏈表的頭結點。這些元素通常狀況是經過hash(key)%len的規則存儲到數組中,也就是元素的key的哈希值對數組長度取模獲得。
1.1.二、核心變量
image.png
1.1.三、put存儲邏輯
當咱們往HashMap中put元素的時候,先根據key的hashCode從新計算hash值,根據hash值獲得這個元素在數組中的位置(即下標), 若是數組該位置上已經存放有其餘元素了,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放在鏈尾。若是數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
這裏有一個特殊的地方。在JDK1.6中,HashMap採用位桶+鏈表實現,即便用鏈表處理衝突,同一hash值的鏈表都存儲在一個鏈表裏。可是當位於一個桶中的元素較多,即hash值相等的元素較多時,經過key值依次查找的效率較低。而JDK1.8中,HashMap採用位桶+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換爲紅黑樹,這樣大大減小了查找時間。
紅黑樹
紅黑樹和平衡二叉樹(AVL樹)相似,都是在進行插入和刪除操做時經過特定操做保持二叉查找樹的平衡,從而得到較高的查找性能。
紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的很是嚴格的平衡。
1.1.四、get讀取邏輯
從HashMap中get元素時,首先計算key的hashCode,找到數組中對應位置的某一元素,而後經過key的equals方法在對應位置的鏈表中找到須要的元素。
若是第一個節點是TreeNode,說明採用的是數組+紅黑樹結構處理衝突,遍歷紅黑樹,獲得節點值。
1.1.五、概括
簡單地說,HashMap 在底層將 key-value 當成一個總體進行處理,這個總體就是一個 Node 對象。HashMap 底層採用一個 Node<K,V>[] 數組來保存全部的 key-value 對,當須要存儲一個 Node 對象時,會根據hash算法來決定其在數組中的存儲位置,在根據equals方法決定其在該數組位置上的鏈表中的存儲位置;當須要取出一個Entry時,也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Node。
1.1.六、HashMap的resize(rehash)
當HashMap中的元素愈來愈多的時候,hash衝突的概率也就愈來愈高,由於數組的長度是固定的。因此爲了提升查詢的效率,就要對HashMap的數組進行擴容,在對HashMap數組進行擴容時,就會出現性能問題:原數組中的數據必須從新計算其在新數組中的位置,並放進去,這就是resize。
那麼HashMap何時進行擴容呢?當HashMap中的元素個數超過數組大小loadFactor時,就會進行數組擴容,loadFactor的默認值爲0.75,這是一個折中的取值。也就是說,默認狀況下,數組大小爲16,那麼當HashMap中元素個數超過160.75=12的時候,就把數組的大小擴展爲 216=32,即擴大一倍,而後從新計算每一個元素在數組中的位置,而這是一個很是消耗性能的操做,因此若是咱們已經預知HashMap中元素的個數,那麼預設元素的個數可以有效的提升HashMap的性能。
1.二、HashMap在併發場景下的問題和解決方案
1.2.一、多線程put後可能致使get死循環
問題緣由就是HashMap是非線程安全的,多個線程put的時候形成了某個key值Entry key List的死循環,問題就這麼產生了。
當另一個線程get 這個Entry List 死循環的key的時候,這個get也會一直執行。最後結果是愈來愈多的線程死循環,最後致使服務器dang掉。咱們通常認爲HashMap重複插入某個值的時候,會覆蓋以前的值,這個沒錯。可是對於多線程訪問的時候,因爲其內部實現機制(在多線程環境且未做同步的狀況下,對同一個HashMap作put操做可能致使兩個或以上線程同時作rehash動做,就可能致使循環鍵表出現,一旦出現線程將沒法終止,持續佔用CPU,致使CPU使用率居高不下),就可能出現安全問題了。
爲HashMap以鏈表組形式存在,初始數組16位(爲什麼16位,又是一堆移位算法,下一篇文章再寫吧),若是長度超過75%,長度增長一倍,多線程操做的時候,恰巧兩個線程插入的時候都須要擴容,造成了兩個鏈表,這時候讀取,size不同,報錯了。其實這時候報錯都是好事,至少不會陷入死循環讓cpu死了,有種狀況,假如兩個線程在讀,還有個線程在寫,恰巧擴容了,這時候你死都不知道咋死的,直接就是死循環,假如你是雙核cpu,cpu佔用率就是50%,兩個線程恰巧都進入死循環了,得!中獎了。
1.2.二、多線程put的時候可能致使元素丟失
主要問題出在addEntry方法的new Entry (hash, key, value, e),若是兩個線程都同時取得了e,則他們下一個元素都是e,而後賦值給table元素的時候有一個成功有一個丟失。
1.2.三、解決方案
ConcurrentHashMap替換HashMap
Collections.synchronizedMap將HashMap包裝起來
1.三、ConcurrentHashMap PK HashTable
ConcurrentHashMap具體是怎麼實現線程安全的呢,確定不多是每一個方法加synchronized,那樣就變成了HashTable。
從ConcurrentHashMap代碼中能夠看出,它引入了一個「分段鎖」的概念,具體能夠理解爲把一個大的Map拆分紅N個小的HashTable,根據key.hashCode()來決定把key放到哪一個HashTable中。
以空間換時間的結構,跟分佈式緩存結構有點像,建立的時候,內存直接分爲了16個segment,每一個segment實際上仍是存儲的哈希表(Segment其實就是一個HashMap ),寫入的時候,先找到對應的segment,而後鎖這個segment,寫完,解鎖,鎖segment的時候,其餘segment還能夠繼續工做。
ConcurrentHashMap如此的設計,優點主要在於: 每一個segment的讀寫是高度自治的,segment之間互不影響。這稱之爲「鎖分段技術」;
2.一、線程的各個狀態及切換
Java中的線程的生命週期大致可分爲5種狀態:新建、可運行、運行、阻塞、死亡。
一、新建(NEW):新建立了一個線程對象。
二、可運行(RUNNABLE):線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。
三、運行(RUNNING):可運行狀態(runnable)的線程得到了cpu 時間片(timeslice) ,執行程序代碼。
四、阻塞(BLOCKED):阻塞狀態是指線程由於某種緣由放棄了cpu 使用權,也即讓出了cpu timeslice,暫時中止運行。直到線程進入可運行(runnable)狀態,纔有機會再次得到cpu timeslice 轉到運行(running)狀態。
阻塞的狀況分三種:
1)等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。
2)同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。
3)其餘阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入可運行(runnable)狀態。
五、死亡(DEAD):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。
幾個方法的比較:
1)Thread.sleep(long millis),必定是當前線程調用此方法,當前線程進入阻塞,但不釋放對象鎖,millis後線程自動甦醒進入可運行狀態。做用:給其它線程執行機會的最佳方式。
2)Thread.yield(),必定是當前線程調用此方法,當前線程放棄獲取的cpu時間片,由運行狀態變會可運行狀態,讓OS再次選擇線程。做用:讓相同優先級的線程輪流執行,但並不保證必定會輪流執行。實際中沒法保證yield()達到讓步目的,由於讓步的線程還有可能被線程調度程序再次選中。Thread.yield()不會致使阻塞。
3)t.join()/t.join(long millis),當前線程裏調用其它線程1的join方法,當前線程阻塞,但不釋放對象鎖,直到線程1執行完畢或者millis時間到,當前線程進入可運行狀態。
4)obj.wait(),當前線程調用對象的wait()方法,當前線程釋放對象鎖,進入等待隊列。依靠notify()/notifyAll()喚醒或者wait(long timeout)timeout時間到自動喚醒。
5)obj.notify(),喚醒在此對象監視器上等待的單個線程,選擇是任意性的。notifyAll()喚醒在此對象監視器上等待的全部線程。
2.二、多線程的實現方式Thread、Runnable、Callable
繼承Thread類,實現Runnable接口,實現Callable接口。
這三種方法的介紹和比較:
一、實現Runnable接口相比繼承Thread類有以下優點:
1)能夠避免因爲Java的單繼承特性而帶來的侷限
2)加強程序的健壯性,代碼可以被多個線程共享,代碼與數據是獨立的
3)適合多個相同程序代碼的線程去處理同一資源的狀況
二、實現Runnable接口和實現Callable接口的區別
1)Runnable是自從java1.1就有了,而Callable是1.5以後才加上去的
2)實現Callable接口的任務線程能返回執行結果,而實現Runnable接口的任務線程不能返回結果
3)Callable接口的call()方法容許拋出異常,而Runnable接口的run()方法的異常只能在內部消化,不能繼續上拋
4)加入線程池運行,Runnable使用ExecutorService的execute方法,Callable使用submit方法
注:Callable接口支持返回執行結果,此時須要調用FutureTask.get()方法實現,此方法會阻塞主線程直到獲取返回結果,當不調用此方法時,主線程不會阻塞
2.三、線程池原理和運行機制
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類。
在ThreadPoolExecutor類中提供了四個構造方法,主要參數包括下面的參數:
一、int corePoolSize:核心池的大小。
線程池的基本大小,即在沒有任務須要執行的時候線程池的大小,而且只有在工做隊列滿了的狀況下才會建立超出這個數量的線程。這裏須要注意的是:在剛剛建立ThreadPoolExecutor的時候,線程並不會當即啓動,而是要等到有任務提交時纔會啓動,除非調用了prestartCoreThread/prestartAllCoreThreads事先啓動核心線程。再考慮到keepAliveTime和allowCoreThreadTimeOut超時參數的影響,因此沒有任務須要執行的時候,線程池的大小不必定是corePoolSize。
二、int maximumPoolSize:線程池最大線程數,它表示在線程池中最多能建立多少個線程,注意與corePoolSize區分。
線程池中容許的最大線程數,線程池中的當前線程數目不會超過該值。若是隊列中任務已滿,而且當前線程個數小於maximumPoolSize,那麼會建立新的線程來執行任務。
三、long keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。
四、TimeUnit unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性。
五、BlockingQueue<Runnable> workQueue:一個阻塞隊列,用來存儲等待執行的任務。
六、ThreadFactory threadFactory:線程工廠,主要用來建立線程。
七、RejectedExecutionHandler handler:表示當拒絕處理任務時的策略。
還有一個成員變量比較重要:poolSize
線程池中當前線程的數量,當該值爲0的時候,意味着沒有任何線程,線程池會終止。同一時刻,poolSize不會超過maximumPoolSize。
2.四、線程池對任務的處理
一、若是當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會建立一個線程去執行這個任務;
二、若是當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閒線程將其取出去執行;若添加失敗(通常來講是任務緩存隊列已滿),則會嘗試建立新的線程去執行這個任務(maximumPoolSize);
三、若是當前線程池中的線程數目達到maximumPoolSize(此時線程池的任務緩存隊列已滿),則會採起任務拒絕策略進行處理;
任務拒絕策略,一般有如下四種策略:
1)ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
2)ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,可是不拋出異常。
3)ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,而後從新嘗試執行任務(重複此過程)
4)ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
四、若是線程池中的線程數量大於 corePoolSize時,若是某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;若是容許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。
2.五、線程池的狀態
一、線程池的狀態說明:
RUNNING(運行):接受新任務並處理排隊任務。
SHUTDOWN(關閉):不接受新任務,會處理隊列任務。
STOP(中止):不接受新任務,不處理隊列任務,而且中斷進程中的任務。
TIDYING(整理):全部任務都已終止,工做計數爲零,線程將執行terminated()方法終止線程。
TERMINATED(已終止):terminated()方法已完成。
二、各個狀態之間的轉換
RUNNING -> SHUTDOWN:調用shutdown()方法。
RUNNING or SHUTDOWN-> STOP:調用shutdownNow()方法。
SHUTDOWN -> TIDYING:當隊列和池均都爲空時。
STOP -> TIDYING:當池爲空時。
TIDYING -> TERMINATED:當terminated()方法已完成。
進程和線程的區別:
對於進程來講,子進程是父進程的複製品,從父進程那裏得到父進程的數據空間,堆和棧的複製品。
而線程,相對於進程而言,是一個更加接近於執行體的概念,能夠和同進程的其餘線程之間直接共享數據,並且擁有本身的棧空間,擁有獨立序列。
共同點: 它們都能提升程序的併發度,提升程序運行效率和響應時間。線程和進程在使用上各有優缺點。 線程執行開銷比較小,但不利於資源的管理和保護,而進程相反。同時,線程適合在SMP機器上運行,而進程能夠跨機器遷移。
他們之間根本區別在於 多進程中每一個進程有本身的地址空間,線程則共享地址空間。全部其餘區別都是由於這個區別產生的。好比說:
1. 速度。線程產生的速度快,通信快,切換快,由於他們處於同一地址空間。
2. 線程的資源利用率好。
3. 線程使用公共變量或者內存的時候須要同步機制,但進程不用。
而他們通訊方式的差別也仍然是因爲這個根本緣由形成的。
通訊方式之間的差別
由於那個根本緣由,實際上只有進程間須要通訊,同一進程的線程共享地址空間,沒有通訊的必要,但要作好同步/互斥,保護共享的全局變量。
而進程間通訊不管是信號,管道pipe仍是共享內存都是由操做系統保證的,是系統調用.
線程間的通訊目的主要是用於線程同步,因此線程沒有像進程通訊中的用於數據交換的通訊機制。
3.一、JVM的結構
每一個JVM都包含:方法區、Java堆、Java棧、本地方法棧、程序計數器等。
image.png
一、方法區:共享
各個線程共享的區域,存放類信息、常量、靜態變量。
二、Java堆:共享
也是線程共享的區域,咱們的類的實例就放在這個區域,能夠想象你的一個系統會產生不少實例,所以Java堆的空間也是最大的。若是Java堆空間不足了,程序會拋出OutOfMemoryError異常。
三、Java棧:私有
每一個線程私有的區域,它的生命週期與線程相同,一個線程對應一個Java棧,每執行一個方法就會往棧中壓入一個元素,這個元素叫「棧幀」,而棧幀中包括了方法中的局部變量、用於存放中間狀態值的操做棧。若是Java棧空間不足了,程序會拋出StackOverflowError異常,想想什麼狀況下會容易產生這個錯誤,對,遞歸,遞歸若是深度很深,就會執行大量的方法,方法越多Java棧的佔用空間越大。
四、本地方法棧:私有
角色和Java棧相似只不過它是用來表示執行本地方法的,本地方法棧存放的方法調用本地方法接口,最終調用本地方法庫,實現與操做系統、硬件交互的目的。
五、程序計數器:私有
說到這裏咱們的類已經加載了,實例對象、方法、靜態變量都去了本身該去的地方,那麼問題來了,程序該怎麼執行,哪一個方法先執行,哪一個方法後執行,這些指令執行的順序就是程序計數器在管,它的做用就是控制程序指令的執行順序。
六、執行引擎固然就是根據PC寄存器調配的指令順序,依次執行程序指令。
3.二、Java堆的介紹及典型的垃圾回收算法介紹
3.2.一、Java堆的介紹
Java堆是虛擬機管理的最大的一塊內存,堆上的全部線程共享一塊內存區域,在啓動虛擬機時建立。此內存惟一目的就是存放對象實例,幾乎全部對象實例都在這裏分配,這一點Java虛擬機規範中的描述是:全部對象實例及數組都要在堆上分配。
Java堆是垃圾收集器管理的主要區域,也被稱爲「GC堆」,因爲如今收集器基本都採用分代收集算法,因此Java堆中還能夠分爲:新生代和老年代。
堆的內存模型大體爲:
image.png
默認的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值爲 1:2 ( 該值能夠經過參數 –XX:NewRatio 來指定 ),即:新生代 ( Young ) = 1/3 的堆空間大小,老年代 ( Old ) = 2/3 的堆空間大小。
其中,新生代 ( Young ) 被細分爲 Eden 和 兩個 Survivor 區域,這兩個 Survivor 區域分別被命名爲 from 和 to以示區分。 默認的,Edem : from : to = 8 : 1 : 1 ( 能夠經過參數 –XX:SurvivorRatio 來設定 ),即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小。
JVM 每次只會使用 Eden 和其中的一塊 Survivor 區域來爲對象服務,因此不管何時,老是有一塊 Survivor 區域是空閒着的。 所以,新生代實際可用的內存空間爲 9/10 ( 即90% )的新生代空間。
新生代是 GC 收集垃圾的頻繁區域。
當對象在 Eden ( 包括一個 Survivor 區域,這裏假設是 from 區域 ) 出生後,在通過一次 Minor GC 後,若是對象還存活,而且可以被另一塊 Survivor 區域所容納 ( 上面已經假設爲 from 區域,這裏應爲 to 區域,即 to 區域有足夠的內存空間來存儲 Eden 和 from 區域中存活的對象 ),則使用複製算法將這些仍然還存活的對象複製到另一塊 Survivor 區域 ( 即 to 區域 ) 中,而後清理所使用過的 Eden 以及 Survivor 區域 ( 即 from 區域 ),而且將這些對象的年齡設置爲1,之後對象在 Survivor 區每熬過一次 Minor GC,就將對象的年齡 + 1,當對象的年齡達到某個值時 ( 默認是 15 歲,能夠經過參數 -XX:MaxTenuringThreshold 來設定 ),這些對象就會成爲老年代。
但這也不是必定的,對於一些較大的對象 ( 即須要分配一塊較大的連續內存空間 ) 則是直接進入到老年代。
From Survivor區域與To Survivor區域是交替切換空間,在同一時間內二者中只有一個不爲空。
3.2.二、如何肯定某個對象是可回收的(垃圾)
一、引用計數法
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器都爲0的對象就是不可能再被使用的。
這種方式的問題是沒法解決循環引用的問題,當兩個對象循環引用時,就算把兩個對象都設置爲null,由於他們的引用計數都不爲0,這就會使他們永遠不會被清除。
二、根搜索算法(可達性分析)
爲了解決引用計數法的循環引用問題,Java使用了可達性分析的方法。經過一系列的「GC roots」對象做爲起點搜索。若是在「GC roots」和一個對象之間沒有可達路徑,則稱該對象是不可達的。要注意的是,不可達對象不等價於可回收對象,不可達對象變爲可回收對象至少要通過兩次標記過程。兩次標記後仍然是可回收對象,則將面臨回收。
比較常見的將對象視爲可回收對象的緣由:
顯式地將對象的惟一強引用指向新的對象。
顯式地將對象的惟一強引用賦值爲Null。
局部引用所指向的對象(如,方法內對象)。
只有弱引用與其關聯的對象。
1)強引用(StrongReference)
強引用是使用最廣泛的引用。若是一個對象具備強引用,那垃圾回收器毫不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題。 ps:強引用其實也就是咱們平時A a = new A()這個意思。
2)軟引用(SoftReference)
若是一個對象只具備軟引用,則內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存(下文給出示例)。
軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
3)弱引用(WeakReference)
弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。
弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
4)虛引用(PhantomReference)
「虛引用」顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之 關聯的引用隊列中。
3.2.三、典型的垃圾回收算法介紹
一、標記-清除算法(Mark-Sweep)
最基礎的垃圾回收算法,分爲「標註」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收掉全部被標記的對象。
標記過程:爲了可以區分對象是live的,能夠爲每一個對象添加一個marked字段,該字段在對象建立的時候,默認值是false。
清除過程:去遍歷堆中全部對象,並找出未被mark的對象,進行回收。與此同時,那些被mark過的對象的marked字段的值會被從新設置爲false,以便下次的垃圾回收。
缺點:效率低,空間問題(產生大量不連續的內存碎片),後續可能發生大對象不能找到可利用空間的問題。
image.png
二、複製算法(Copying)——新生代的收集算法就是這種,可是比例不是1:1,而是(8+1):1
爲了解決Mark-Sweep算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分爲大小相等的兩塊,每次只使用其中一塊。當這一塊內存滿後將尚存活的對象複製到另外一塊上去,把已使用的內存空間一次清理掉。這種算法雖然實現簡單,內存效率高,不易產生碎片,可是最大的問題是可用內存被壓縮到了本來的一半。且存活對象增多的話,Copying算法的效率會大大下降。
image.png
三、標記-整理算法(Mark-Compact)——老年代的收集算法
結合了以上兩個算法,標記階段和Mark-Sweep算法相同,標記後不是清理對象,而是將全部存活對象移向內存的一端,而後清除端邊界外的對象。如圖:
image.png
四、分代收集算法(Generational Collection)
分代收集法是目前大部分JVM所採用的方法,其核心思想是根據對象存活的不一樣生命週期將內存劃分爲不一樣的域,通常狀況下將GC堆劃分爲老生代(Tenured/Old Generation)和新生代(Young Generation)。
老生代的特色是每次垃圾回收時只有少許對象須要被回收,新生代的特色是每次垃圾回收時都有大量垃圾須要被回收,所以能夠根據不一樣區域選擇不一樣的算法。
目前大部分JVM的GC對於新生代都採起復制算法(Copying),由於新生代中每次垃圾回收都要回收大部分對象,即要複製的操做比較少,但一般並非按照1:1來劃分新生代。通常將新生代劃分爲一塊較大的Eden空間和兩個較小的Survivor空間(From Space, To Space),每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將該兩塊空間中還存活的對象複製到另外一塊Survivor空間中。
老年代中的對象存活率高,沒有額外空間對它進行分配,使用「標記-清理」或「標記-整理」算法來進行回收。
3.三、JVM處理FULLGC經驗
3.3.一、內存泄漏
一、產生緣由
1)JVM內存太小。
2)程序不嚴密,產生了過多的垃圾。
二、通常狀況下,在程序上的體現爲:
1)內存中加載的數據量過於龐大,如一次從數據庫取出過多數據。
2)集合類中有對對象的引用,使用完後未清空,使得JVM不能回收。
3)代碼中存在死循環或循環產生過多重複的對象實體。
4)使用的第三方軟件中的BUG。
5)啓動參數內存值設定的太小。
3.3.二、Java內存泄漏的排查案例
一、肯定頻繁Full GC現象,找出進程惟一ID
使用jps(jps -l)或ps(ps aux | grep tomat)找出這個進程在本地虛擬機的惟一ID(LVMID,Local Virtual Machine Identifier)
二、再使用「虛擬機統計信息監視工具:jstat」(jstat -gcutil 20954 1000)查看已使用空間站總空間的百分比,能夠看到FGC的頻次。
image.png
三、找出致使頻繁Full GC的緣由,找出出現問題的對象
分析方法一般有兩種:
1)把堆dump下來再用MAT等工具進行分析,但dump堆要花較長的時間,而且文件巨大,再從服務器上拖回本地導入工具,這個過程有些折騰,不到萬不得已最好別這麼幹。
2)更輕量級的在線分析,使用「Java內存影像工具:jmap」生成堆轉儲快照(通常稱爲headdump或dump文件)。
jmap命令格式:jmap -histo:live 20954
四、一個工具:BTrace,沒有使用過
5.一、HTTP請求報文和響應報文
image.png
5.二、HTTPS爲何是安全的?HTTPS的加密方式有哪些?
5.2.一、HTTPS的工做原理說明HTTPS是安全的
image.png
客戶端在使用HTTPS方式與Web服務器通訊時有如下幾個步驟,如圖所示。
一、客戶使用https的URL訪問Web服務器,要求與Web服務器創建SSL鏈接。
二、Web服務器收到客戶端請求後,會將網站的證書信息(證書中包含公鑰)傳送一份給客戶端。
三、客戶端的瀏覽器與Web服務器開始協商SSL鏈接的安全等級,也就是信息加密的等級。
四、客戶端的瀏覽器根據雙方贊成的安全等級,創建會話密鑰,而後利用網站的公鑰將會話密鑰加密,並傳送給網站。
五、Web服務器利用本身的私鑰解密出會話密鑰。
六、Web服務器利用會話密鑰加密與客戶端之間的通訊。
image.png
5.2.二、HTTPS的加密方式有哪些?
一、對稱加密
對稱加密是指加密和解密使用相同密鑰的加密算法。它要求發送方和接收方在安全通訊以前,商定一個密鑰。對稱算法的安全性依賴於密鑰,泄漏密鑰就意味着任何人均可以對他們發送或接收的消息解密,因此密鑰的保密性對通訊相當重要。
二、更多的加密方式的瞭解
5.三、TCP三次握手協議,四次揮手
第一次握手:主機A發送位碼爲syn=1,隨機產生seq number=1234567的數據包到服務器,主機B由SYN=1知道,A要求創建聯機;
第二次握手:主機B收到請求後要確認聯機信息,向A發送ack number=(主機A的seq+1),syn=1,ack=1,隨機產生seq=7654321的包;
第三次握手:主機A收到後檢查ack number是否正確,即第一次發送的seq number+1,以及位碼ack是否爲1,若正確,主機A會再發送ack number=(主機B的seq+1),ack=1,主機B收到後確認seq值與ack=1則鏈接創建成功。
完成三次握手,主機A與主機B開始傳送數據。
5.四、OAuth協議介紹
image.png
5.五、防盜鏈Referer
Referer請求頭: 表明當前訪問時從哪一個網頁鏈接過來的。
當Referer未存在或者是從其餘站點訪問咱們資源的時候就直接重定向到咱們的主頁,這樣既能夠防止咱們的資源被竊取。
6.一、AOP的實現原理
spring框架對於這種編程思想的實現基於兩種動態代理模式,分別是JDK動態代理 及 CGLIB的動態代理,這兩種動態代理的區別是 JDK動態代理須要目標對象實現接口,而 CGLIB的動態代理則不須要。下面咱們經過一個實例來實現動態代理,進而幫助咱們理解面向切面編程。
JDK的動態代理要使用到一個類 Proxy 用於建立動態代理的對象,一個接口 InvocationHandler用於監聽代理對象的行爲,其實動態代理的本質就是對代理對象行爲的監聽。
6.二、Spring MVC工做原理
Spring的MVC框架主要由DispatcherServlet、處理器映射、處理器(控制器)、視圖解析器、視圖組成。
6.2.一、SpringMVC原理圖
image.png
6.2.二、SpringMVC運行原理
一、客戶端請求提交到DispatcherServlet
二、由DispatcherServlet控制器查詢一個或多個HandlerMapping,找處處理請求的Controller
三、DispatcherServlet將請求提交到Controller
四、Controller調用業務邏輯處理後,返回ModelAndView
五、DispatcherServlet查詢一個或多個ViewResoler視圖解析器,找到ModelAndView指定的視圖
六、視圖負責將結果顯示到客戶端
6.2.三、SpringMVC核心組件
一、DispatcherServlet:中央控制器,把請求給轉發到具體的控制類
二、Controller:具體處理請求的控制器
三、HandlerMapping:映射處理器,負責映射中央處理器轉發給controller時的映射策略
四、ModelAndView:服務層返回的數據和視圖層的封裝類
五、ViewResolver:視圖解析器,解析具體的視圖
六、Interceptors :攔截器,負責攔截咱們定義的請求而後作處理工做
6.2.四、Servlet 生命週期
Servlet 生命週期可被定義爲從建立直到毀滅的整個過程。如下是 Servlet 遵循的過程:
一、Servlet 經過調用 init () 方法進行初始化。
二、Servlet 調用 service() 方法來處理客戶端的請求。
三、Servlet 經過調用 destroy() 方法終止(結束)。
四、最後,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的。
6.2.五、Spring容器初始化過程
Spring 啓動時讀取應用程序提供的Bean配置信息,並在Spring容器中生成一份相應的Bean配置註冊表,而後根據這張註冊表實例化Bean,裝配號Bean之間的依賴關係,爲上層應用提供準備就緒的運行環境。
image.png
7.一、分佈式下如何保證事務一致性
分佈式事務,常見的兩個處理辦法就是兩段式提交和補償。
7.1.一、兩段式提交
分佈式事務將提交分紅兩個階段:
prepare;
commit/rollback
在分佈式系統中,每一個節點雖然能夠知曉本身的操做是成功或者失敗,卻沒法知道其餘節點的操做的成功或失敗。當一個事務跨越多個節點時,爲了保持事務的ACID特性,須要引入一個做爲協調者的組件來統一掌控全部節點(參與者)的操做結果並最終指示這些節點是否須要把操做結果進行真正的提交。算法步驟以下:
第一階段:
一、協調者會問全部的參與者,是否能夠執行提交操做。
二、各個參與者開始事務執行的準備工做,如:爲資源上鎖,預留資源。
三、參與者響應協調者,若是事務的準備工做成功,則迴應「能夠提交」,不然迴應「拒絕提交」。
第二階段:
一、若是全部的參與者都回應「能夠提交」。那麼協調者向全部的參與者發送「正式提交」的命令。參與者完成正式提交併釋放全部資源,而後迴應「完成」,協調者收集各節點的「完成」迴應後結束這個Global Transaction
二、若是有一個參與者迴應「拒絕提交」,那麼協調者向全部的參與者發送「回滾操做」,並釋放全部資源,而後迴應「回滾完成」,協調者收集各節點的「回滾」迴應後,取消這個Global Transaction。
7.1.二、三段式提交
三段提交的核心理念是:在詢問的時候並不鎖定資源,除非全部人都贊成了,纔開始鎖資源。他把二段提交的第一個段break成了兩段:詢問,而後再鎖資源。最後真正提交。
7.1.二、事務補償,最終一致性
補償比較好理解,先處理業務,而後定時或者回調裏,檢查狀態是否是一致的,若是不一致採用某個策略,強制狀態到某個結束狀態(通常是失敗狀態)。
9.一、kafka消息隊列
一、避免數據丟失
producer:
加大重試次數
同步發送
對於單條數據過大,要設置可接收的單條數據的大小
對於異步發送,經過回調函數來感知丟消息
block.on.buffer.full = true
consumer:
enable.auto.commit=false 關閉自動提交位移
二、避免消息亂序
假設a,b兩條消息,a先發送後因爲發送失敗重試,這時順序就會在b的消息後面,能夠設置max.in.flight.requests.per.connection=1來避免。
max.in.flight.requests.per.connection:限制客戶端在單個鏈接上可以發送的未響應請求的個數,設置此值是1表示kafka broker在響應請求以前client不能再向同一個broker發送請求,但吞吐量會降低。
三、避免消息重複
使用第三方redis的set
9.二、ZooKeeper的原理
9.三、SOA相關,RPC兩種實現方式:基於HTTP和基於TCP
9.四、Netty
何時會觸發full gc
JAVA 線程狀態轉換圖示
爲何說B+比B樹更適合實際應用中操做系統的文件索引和數據庫索引?
JVM :英文名稱(Java Virtual Machine),就是咱們耳熟能詳的 Java 虛擬機。它只認識 xxx.class 這種類型的文件,它可以將 class 文件中的字節碼指令進行識別並調用操做系統向上的 API 完成動做。因此說,jvm 是 Java 可以跨平臺的核心,具體的下文會詳細說明。
JRE :英文名稱(Java Runtime Environment),咱們叫它:Java 運行時環境。它主要包含兩個部分,jvm 的標準實現和 Java 的一些基本類庫。它相對於 jvm 來講,多出來的是一部分的 Java 類庫。
JDK :英文名稱(Java Development Kit),Java 開發工具包。jdk 是整個 Java 開發的核心,它集成了 jre 和一些好用的小工具。例如:javac.exe,java.exe,jar.exe 等。
顯然,這三者的關係是:一層層的嵌套關係。JDK>JRE>JVM。
借一張圖簡單說明一下
而後是前端技術一些概念,雖然如今基本是先後端分離的模式開發,但也須要理解一些基礎吧!
javascript==js
js是一種腳本語言,在html中,css管理位置,html主管內容,而js主管動做,減輕後臺的操做,能夠很簡單的就實現對輸入的數據的驗證。好比說註冊的時候,用js中的ajax到後臺實時驗證本用戶名是否已經被註冊,驗證碼是否正確或者用來實現異步更新,爲用戶帶來更好的體驗。用jquery來驗證密碼的合法性,輸入密碼與確認密碼是否同樣,從而反饋到html頁面上,能夠經過操控css或者html頁面內容來確認輸入內容是否有錯。可是ajax和jquery都是js的一個庫。
js!=jsp
js(javascript) jsp(java server pages)
之前在知乎裏面看到了一句至關經典的回答,js與jsp的關係就是雷鋒和雷峯塔的關係。從這句話能夠看出它們倆沒有任何聯繫,而實際上也是這樣。jsp其實能夠看作一種腳本語言,須要用servlet來編譯實現,然而做爲一種腳本語言它有至關強大,在其中能夠嵌入java代碼,jsp中幾乎能夠使用所有的java類 。其實就是能夠把jsp當作html來做爲網頁顯示出來,並且其上還能夠嵌套java語言,也能夠嵌套其餘的語言相似,固然都序言用servlet來編譯實現。jsp做爲字節碼文件執行後能夠直接運行,沒必要每次都要編譯,速度快。可能我表述仍是有點問題,可是jsp和js大多都應用於web網頁的編寫上,jsp能夠看作html和java的結合體,固然js就能夠在jsp上實現一些動做,特效,驗證功能,與在html中所實現的效果同樣。由於jsp是在服務器端解釋執行的,服務器執行轉化後的.class程序,客戶端接收到的只是服務器發來的html代碼,看不到jsp代碼。而js能夠在客戶端經過查看源代碼顯示出來。
java、jsp
java是一種編程語言,jsp只是至關於java裏面的servlet部分
後言
記得剛開始開發網站的時候,個人一個學長叫我去看jsp,而後我就用了一個星期看js去了,我還覺得js就是jsp,而後就jj了,javascript和jsp也沒差多少,因此作了不少的無用功,多走了不應走的彎路,不事後來兩個東西都用上了,並且用處都還很是大。
重要的事情說三遍,
js!=jsp,js!=jsp,js!=jsp。
js==javascript
jsp==java server pages
基礎題目
問了對Java的Map瞭解嗎,請說出其中的幾個接口,怎麼獲取key,怎麼判斷是否包含key,哪些實現了Map接口,map的hash以及數據結構,1.7和1.8的區別等。
二、寫一個棧的實現,用數組,確保最大隊列長度爲k(我當時問了滿了要不要什麼策略),第一次我用front和end兩個指針,而後他問我一個變量能夠不(我說的不能夠),而後我說front + size(隊列中的元素)能夠,而後手寫。
三、問了一些juc併發庫有哪些類(futrue,futruetask,excutors,executorthreadpool,countdownlatch,cyclicbarrier,symphone等)
四、怎麼確保當全部線程執行到某個點等待,直到全部線程都執行到時一塊兒往下執行(cyclicbarrier)。
五、cyclicbarrier和countdownlatch有什麼區別,以及應用場景。
怎麼查看是哪一條SQL執行慢(這個以前在面拼多多的時候也被問到過,但樓主沒去看,被本身不善於總結坑了),slow_query_log,long_query_time,slow_query_log_file,log_queries_not_using_indexes這個命令你們能夠去看下
四、ACID的意義(原子性,一致性,隔離性,持久性,當時腦子懵逼了,只說出了兩個)
五、數據庫的四種隔離級別,怎麼避免髒讀
六、hashmap和currenthashmap的數據結構,volatile瞭解嗎,寫個volatile的應用場景(我就說了單列的雙重檢驗)
七、jdk1.8的特性。
九、還有什麼我沒有問到的嗎(我說我什麼方向的都有一點研究),而後面試官看了下學歷,渣渣本科,好吧不問了。
1. junit用法,before,beforeClass,after, afterClass的執行順序
2. 分佈式鎖
3. nginx的請求轉發算法,如何配置根據權重轉發
4. 用hashmap實現redis有什麼問題(死鎖,死循環,可用ConcurrentHashmap)
5. 線程的狀態
5. 線程的阻塞的方式
6. sleep和wait的區別
7. hashmap的底層實現
8. 一萬我的搶100個紅包,如何實現(不用隊列),如何保證2我的不能搶到同一個紅包,可用分佈式鎖
9. java內存模型,垃圾回收機制,不可達算法
10. 兩個Integer的引用對象傳給一個swap方法在方法內部交換引用,返回後,兩個引用的值是否會發現變化
11. aop的底層實現,動態代理是如何動態,假若有100個對象,如何動態的爲這100個對象代理
12. 是否用過maven install。 maven test。git(make install是安裝本地jar包)
13. tomcat的各類配置,如何配置docBase
14. spring的bean配置的幾種方式
15. web.xml的配置
16. spring的監聽器。
17. zookeeper的實現機制,有緩存,如何存儲註冊服務的
18. IO會阻塞嗎?readLine是否是阻塞的
19. 用過spring的線程池仍是java的線程池?
20. 字符串的格式化方法 (20,21這兩個問題問的過低級了)
21. 時間的格式化方法
22. 定時器用什麼作的
23. 線程如何退出結束
24. java有哪些鎖?樂觀鎖 悲觀鎖 synchronized 可重入鎖 讀寫鎖,用過reentrantlock嗎?reentrantlock與synmchronized的區別
25. ThreadLocal的使用場景
26. java的內存模型,垃圾回收機制
27. 爲何線程執行要調用start而不是直接run(直接run,跟普通方法沒什麼區別,先調start,run纔會做爲一個線程方法運行)
28. qmq消息的實現機制(qmq是去哪兒網本身封裝的消息隊列)
29. 遍歷hashmap的三種方式
30. jvm的一些命令
31. memcache和redis的區別
32. mysql的行級鎖加在哪一個位置
33. ConcurrentHashmap的鎖是如何加的?是否是分段越多越好
34. myisam和innodb的區別(innodb是行級鎖,myisam是表級鎖)
35. mysql其餘的性能優化方式
36. linux系統日誌在哪裏看
37. 如何查看網絡進程
38. 統計一個整數的二進制表示中bit爲1的個數
39. jvm內存模型,java內存模型
40. 如何把java內存的數據所有dump出來
41. 如何手動觸發全量回收垃圾,如何當即觸發垃圾回收
42. hashmap若是隻有一個寫其餘全讀會出什麼問題
43. git rebase
44. mongodb和hbase的區別
45. 如何解決併發問題
46. volatile的用途
47. java線程池(好像以前個人理解有問題)
48. mysql的binlog
49. 代理模式
50. mysql是如何實現事務的
51. 讀寫分離什麼時候強制要讀主庫,讀哪一個從庫是經過什麼方式決定的,從庫的同步mysql用的什麼方式
52. mysql的存儲引擎
53. mysql的默認隔離級別,其餘隔離級別
54. 將一個鏈表反轉(用三個指針,可是每次只發轉一個)
55. spring Aop的實現原理,具體說說
56. 什麼時候會內存泄漏,內存泄漏會拋哪些異常
57. 是否用過Autowire註解
58. spring的注入bean的方式
59. sql語句各類條件的執行順序,如select, where, order by, group by
60. select xx from xx where xx and xx order by xx limit xx; 如何優化這個(看explain)
61. 四則元算寫代碼
62. 統計100G的ip文件中出現ip次數最多的100個ip
63. zookeeper的事物,結點,服務提供方掛了如何告知消費方
64. 5臺服務器如何選出leader(選舉算法)
65. 適配器和代理模式的區別
66. 讀寫鎖
67. static加鎖
68. 事務隔離級別
69. 門面模式,類圖(外觀模式)
70. mybatis如何映射表結構
71. 二叉樹遍歷
72. 主從複製
73. mysql引擎區別
74. 靜態內部類加載到了哪一個區?方法區
75. class文件編譯後加載到了哪
76. web的http請求如何總體響應時間變長致使處理的請求數變少,該如何處理?用隊列,當處理不了那麼多http請求時將請求放到隊列
中慢慢處理,web如何實現隊列
77. 線程安全的單例模式
78. 快速排序性能考慮
79. volatile關鍵字用法
80. 求表的size,或作數據統計可用什麼存儲引擎
81. 讀多寫少可用什麼引擎
82. 假如要統計多個表應該用什麼引擎
83. concurrenhashmap求size是如何加鎖的,若是剛求完一段後這段發生了變化該如何處理
84. 1000個蘋果放10個籃子,怎麼放,能讓我拿到全部可能的個數
85. 可重入的讀寫鎖,可重入是如何實現的?
86. 是否用過NIO
87. java的concurrent包用過沒
88. sting s=new string("abc")分別在堆棧上新建了哪些對象
89. java虛擬機的區域分配,各區分別存什麼
90. 分佈式事務(JTA)
91. threadlocal使用時注意的問題(ThreadLocal和Synchonized都用於解決多線程併發訪問。可是ThreadLocal與synchronized有本質的區別。synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal爲每個線程都提供了變量的副本,使得每一個線程在某一時間訪問到的並非同一個對象,這樣就隔離了多個線程對數據的數據共享。而Synchronized卻正好相反,它用於在多個線程間通訊時可以得到數據共享)
92. java有哪些容器(集合,tomcat也是一種容器)
93. 二分查找算法
94. myisam的優勢,和innodb的區別
95. redis能存哪些類型
96. http協議格式,get和post的區別
97. 可重入鎖中對應的wait和notify
98. redis能把內存空間交換進磁盤中嗎(這個應該是能夠的,可是那個面試官非跟我說不能夠)
99. java線程池中基於緩存和基於定長的兩種線程池,當請求太多時分別是如何處理的?定長的事用的隊列,若是隊列也滿了呢?交換進磁盤?基於緩存的線程池解決方法呢?
100. synchronized加在方法上用的什麼鎖
101. 可重入鎖中的lock和trylock的區別
102. innodb對一行數據的讀會枷鎖嗎?不枷鎖,讀實際讀的是副本
103. redis作緩存是分佈式存的?不一樣的服務器上存的數據是否重複?guava cache呢?是否重複?不一樣的機器存的數據不一樣
104. 用awk統計一個ip文件中top10
105. 對錶作統計時可直接看schema info信息,即查看錶的系統信息
106. mysql目前用的版本
107. 公司經驗豐富的人給了什麼幫助?(通常boss面會問這些)
108. 本身相對於同樣的應屆生有什麼優點
109. 本身的好的總結習慣給本身從此的工做帶了什麼幫助,舉例爲證
110. 原子類,線程安全的對象,異常的處理方式
111. 4億個int數,如何找出重複的數(用hash方法,建一個2的32次方個bit的hash數組,每取一個int數,可hash下2的32次方找到它在hash數組中的位置,而後將bit置1表示已存在)
112. 4億個url,找出其中重複的(考慮內存不夠,經過hash算法,將url分配到1000個文件中,不一樣的文件間確定就不會重複了,再分別找出重複的)
有1萬個數組,每一個數組有1000個整數,每一個數組都是降序的,從中找出最大的N個數,N<1000
113. LinkedHashmap的底層實現
114. 類序列化時類的版本號的用途,若是沒有指定一個版本號,系統是怎麼處理的?若是加了字段會怎麼樣?
115. Override和Overload的區別,分別用在什麼場景
116. java的反射是如何實現的
手撕字符串轉int
2.數據庫事務隔離級別
3.spring 事務
4.數據庫索引失效的場景
5.組合索引(A,B)若查詢B列是否用到了索引
6.若是A是string類型,而查詢的時候是1314151617 用到了索引嗎?能查到這條數據嗎?
7.數據庫怎麼實現樂觀鎖?
8.redis pipeline瞭解嗎?
9.秒殺場景?怎麼實現。redis怎麼限流,限流算法。
10.redis實現隊列、實現優先級隊列。
11.分佈式鎖。
0.基礎部分
不管是哪種編程語言,基礎永遠是你不能忽視的部分。如下是比較常出現的十個點,固然最好是全都能熟悉。
1.Java高級部分
一、Java內存結構,spring的aop的實現方法,java數據庫問題定位和性能調優;
二、關於Java異常的續承層次結構,講述異常的續承關係;
三、java中重載和重寫有什麼區別,分別用什麼關鍵字;
四、關於分佈式消息隊列,分佈式緩存;
五、關於hashmap源碼實現, jdk
六、關於設計模式,uml,jvm 內存回收機制問題
七、java線程如何啓動?java中加鎖的方式有哪些,怎麼個寫法?
八、對樂觀鎖和悲觀鎖的理解;
九、ORACLE中的SQL如何進行優化,都有哪些方式?事務有哪些特性,在ORACLE中隔離有哪些級別?
十、介紹一下本身最近作的一個典型的項目;
十一、在項目中遇到了哪些問題,本身是如何解決的 ;
十二、目前系統支撐的用戶量是多少,假如用戶量提高10倍,系統會出現什麼樣的問題,如何從新設計系統【這裏主要是想了解您的問題預見能力以及問題解決能力,考查思路】
1三、使用memcached是一個什麼樣的原理
1四、如何存放數據到memcached集羣中,介紹一下這個過程。跟進的問題,講一下一致性哈希算法的實現原理。
1五、JVM中堆是如何管理的,JVM的內存回收機制,介紹一下
1六、分佈式事務實現方式
1七、熱點帳戶問題(項目中有就會問)
阿里技術面試(電面)涉及Java基礎點(可參考):
Java面試題分享:
2.框架部分
關於這部分,主要考的也是一些框架部門中較爲基礎的內容。
3.數據庫
5.前端基礎
Set接口
Set不容許包含相同的元素,若是試圖把兩個相同元素加入同一個集合中,add方法返回false。
Set判斷兩個對象相同不是使用==運算符,而是根據equals方法。也就是說,只要兩個對象用equals方法比較返回true,Set就不會接受這兩個對象。
HashSet與TreeSet都是基於Set接口的實現類。其中TreeSet是Set的子接口SortedSet的實現類。Set接口及其子接口、實現類的結構以下所示:
|——SortedSet接口——TreeSet實現類
Set接口——|——HashSet實現類
|——LinkedHashSet實現類
HashSet
HashSet有如下特色
不能保證元素的排列順序,順序有可能發生變化
不是同步的
集合元素能夠是null,但只能放入一個null
當向HashSet結合中存入一個元素時,HashSet會調用該對象的hashCode()方法來獲得該對象的hashCode值,而後根據 hashCode值來決定該對象在HashSet中存儲位置。
簡單的說,HashSet集合判斷兩個元素相等的標準是兩個對象經過equals方法比較相等,而且兩個對象的hashCode()方法返回值相等
注意,若是要把一個對象放入HashSet中,重寫該對象對應類的equals方法,也應該重寫其hashCode()方法。其規則是若是兩個對象經過equals方法比較返回true時,其 hashCode也應該相同。另外,對象中用做equals比較標準的屬性,都應該用來計算 hashCode的值。
TreeSet
TreeSet類型是J2SE中惟一可實現自動排序的類型
TreeSet是SortedSet接口的惟一實現類,TreeSet能夠確保集合元素處於排序狀態。TreeSet支持兩種排序方式,天然排序 和定製排序,其中天然排序爲默認的排序方式。向 TreeSet中加入的應該是同一個類的對象。
TreeSet判斷兩個對象不相等的方式是兩個對象經過equals方法返回false,或者經過CompareTo方法比較沒有返回0
天然排序
天然排序使用要排序元素的CompareTo(Object obj)方法來比較元素之間大小關係,而後將元素按照升序排列。
Java提供了一個Comparable接口,該接口裏定義了一個compareTo(Object obj)方法,該方法返回一個整數值,實現了該接口的對象就能夠比較大小。
obj1.compareTo(obj2)方法若是返回0,則說明被比較的兩個對象相等,若是返回一個正數,則代表obj1大於obj2,若是是 負數,則代表obj1小於obj2。
若是咱們將兩個對象的equals方法老是返回true,則這兩個對象的compareTo方法返回應該返回0
定製排序
天然排序是根據集合元素的大小,以升序排列,若是要定製排序,應該使用Comparator接口,實現 int compare(To1,To2)方法
LinkedHashSet
LinkedHashSet集合一樣是根據元素的hashCode值來決定元素的存儲位置,可是它同時使用鏈表維護元素的次序。這樣使得元素看起 來像是以插入順 序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。
LinkedHashSet在迭代訪問Set中的所有元素時,性能比HashSet好,可是插入時性能稍微遜色於HashSet。
有許多人學了很長時間的Java,但一直不明白hashCode方法的做用,
我來解釋一下吧。首先,想要明白hashCode的做用,你必需要先知道Java中的集合。
java的HashCode方法
總的來講,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
你知道它們的區別嗎?前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複。
那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?
這就是Object.equals方法了。可是,若是每增長一個元素就檢查一次,那麼當元素不少時,後添加到集合中的元素比較的次數就很是多了。 也就是說,若是集合中如今已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大下降效率。
因而,Java採用了哈希表的原理。哈希(Hash)其實是我的名,因爲他提出一哈希算法的概念,因此就以他的名字命名了。 哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上。若是詳細講解哈希算法,那須要更多的文章篇幅,我在這裏就不介紹了。
初學者能夠這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並非)。 這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。 若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了, 就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。 因此這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。 因此,Java對於eqauls方法和hashCode方法是這樣規定的:
一、若是兩個對象相同,那麼它們的hashCode值必定要相同;
二、若是兩個對象的hashCode相同,它們並不必定相同
上面說的對象相同指的是用eqauls方法比較。你固然能夠不按要求去作了,但你會發現,相同的對象能夠出如今Set集合中。同時,增長新元素的效率會大大降低。
hashcode這個方法是用來鑑定2個對象是否相等的。 那你會說,不是還有equals這個方法嗎? 不錯,這2個方法都是用來判斷2個對象是否相等的。可是他們是有區別的。 通常來說,equals這個方法是給用戶調用的,若是你想判斷2個對象是否相等,你能夠重寫equals方法,而後在代碼中調用,就能夠判斷他們是否相等 了。簡單來說,equals方法主要是用來判斷從表面上看或者從內容上看,2個對象是否是相等。
舉個例子,有個學生類,屬性只有姓名和性別,那麼咱們能夠 認爲只要姓名和性別相等,那麼就說這2個對象是相等的。
hashcode方法通常用戶不會去調用,好比在hashmap中,因爲key是不能夠重複的,他在判斷key是否是重複的時候就判斷了hashcode 這個方法,並且也用到了equals方法。這裏不能夠重複是說equals和hashcode只要有一個不等就能夠了!因此簡單來說,hashcode相 當因而一個對象的編碼,就好像文件中的md5,他和equals不一樣就在於他返回的是int型的,比較起來不直觀。咱們通常在覆蓋equals的同時也要 覆蓋hashcode,讓他們的邏輯一致。
舉個例子,仍是剛剛的例子,若是姓名和性別相等就算2個對象相等的話,那麼hashcode的方法也要返回姓名 的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。 要從物理上判斷2個對象是否相等,用==就能夠了。
10.一、系統作了哪些安全防禦
一、XSS(跨站腳本攻擊)
全稱是跨站腳本攻擊(Cross Site Scripting),指攻擊者在網頁中嵌入惡意腳本程序。
XSS防範:
XSS之因此會發生,是由於用戶輸入的數據變成了代碼。所以,咱們須要對用戶輸入的數據進行HTML轉義處理,將其中的「尖括號」、「單引號」、「引號」之類的特殊字符進行轉義編碼。
二、CSRF(跨站請求僞造)
攻擊者盜用了你的身份,以你的名義向第三方網站發送惡意請求。
CSRF的防護:
1)儘可能使用POST,限制GET
2)將cookie設置爲HttpOnly
3)增長token
4)經過Referer識別
三、SQL注入
使用預編譯語句(PreparedStatement),這樣的話即便咱們使用sql語句僞形成參數,到了服務端的時候,這個僞造sql語句的參數也只是簡單的字符,並不能起到攻擊的做用。
作最壞的打算,即便被’拖庫‘('脫褲,數據庫泄露')。數據庫中密碼不該明文存儲的,能夠對密碼使用md5進行加密,爲了加大破解成本,因此能夠採用加鹽的(數據庫存儲用戶名,鹽(隨機字符長),md5後的密文)方式。
四、DDOS
最直接的方法增長帶寬。可是攻擊者用各地的電腦進行攻擊,他的帶寬不會耗費不少錢,但對於服務器來講,帶寬很是昂貴。
雲服務提供商有本身的一套完整DDoS解決方案,而且能提供豐富的帶寬資源
1. 什麼是spring?
Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用,可是有些擴展是針對構建J2EE平臺的web應用。Spring 框架目標是簡化Java企業級應用開發,並經過POJO爲基礎的編程模型促進良好的編程習慣。
2. 使用Spring框架的好處是什麼?
3. Spring由哪些模塊組成?
如下是Spring 框架的基本模塊:
4. 核心容器(應用上下文) 模塊
這是基本的Spring模塊,提供spring 框架的基礎功能,BeanFactory 是 任何以spring爲基礎的應用的核心。Spring 框架創建在此模塊之上,它使Spring成爲一個容器。
5. BeanFactory – BeanFactory 實現舉例
Bean 工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和依賴從正真的應用代碼中分離。最經常使用的BeanFactory 實現是XmlBeanFactory 類。
6. XMLBeanFactory
最經常使用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去建立一個徹底配置的系統或應用。
7. 解釋AOP模塊
AOP模塊用於發給咱們的Spring應用作面向切面的開發, 不少支持由AOP聯盟提供,這樣就確保了Spring和其餘AOP框架的共通性。這個模塊將元數據編程引入Spring。
8. 解釋JDBC抽象和DAO模塊
經過使用JDBC抽象和DAO模塊,保證數據庫代碼的簡潔,並能避免數據庫資源錯誤關閉致使的問題,它在各類不一樣的數據庫的錯誤信息之上,提供了一個統一的異常訪問層。它還利用Spring的AOP 模塊給Spring應用中的對象提供事務管理服務。
9. 解釋對象/關係映射集成模塊
Spring 經過提供ORM模塊,支持咱們在直接JDBC之上使用一個對象/關係映射映射(ORM)工具,Spring 支持集成主流的ORM框架,如Hiberate,JDO和 iBATIS SQL Maps。Spring的事務管理一樣支持以上全部ORM框架及JDBC。
10. 解釋WEB 模塊
Spring的WEB模塊是構建在application context 模塊基礎之上,提供一個適合web應用的上下文。這個模塊也包括支持多種面向web的任務,如透明地處理多個文件上傳請求和程序級請求參數的綁定到你的業務對象。它也有對Jakarta Struts的支持。
12. Spring配置文件
Spring配置文件是個XML 文件,這個文件包含了類信息,描述瞭如何配置它們,以及如何相互調用。
13. 什麼是Spring IOC 容器?
Spring IOC 負責建立對象,管理對象(經過依賴注入(DI),裝配對象,配置對象,而且管理這些對象的整個生命週期。
14. IOC的優勢是什麼?
IOC 或 依賴注入把應用的代碼量降到最低。它使應用容易測試,單元測試再也不須要單例和JNDI查找機制。最小的代價和最小的侵入性使鬆散耦合得以實現。IOC容器支持加載服務時的餓漢式初始化和懶加載。
15. ApplicationContext一般的實現是什麼?
16. Bean 工廠和 Application contexts 有什麼區別?
Application contexts提供一種方法處理文本消息,一個一般的作法是加載文件資源(好比鏡像),它們能夠向註冊爲監聽器的bean發佈事件。另外,在容器或容器內的對象上執行的那些不得不禁bean工廠以程序化方式處理的操做,能夠在Application contexts中以聲明的方式處理。Application contexts實現了MessageSource接口,該接口的實現以可插拔的方式提供獲取本地化消息的方法。
17. 一個Spring的應用看起來象什麼?
依賴注入
18. 什麼是Spring的依賴注入?
依賴注入,是IOC的一個方面,是個一般的概念,它有多種解釋。這概念是說你不用建立對象,而只須要描述它如何被建立。你不在代碼裏直接組裝你的組件和服務,可是要在配置文件裏描述哪些組件須要哪些服務,以後一個容器(IOC容器)負責把他們組裝起來。
19. 有哪些不一樣類型的IOC(依賴注入)方式?
20. 哪一種依賴注入方式你建議使用,構造器注入,仍是 Setter方法注入?
你兩種依賴方式均可以使用,構造器注入和Setter方法注入。最好的解決方案是用構造器參數實現強制依賴,setter方法實現可選依賴。
Spring Beans
21.什麼是Spring beans?
Spring beans 是那些造成Spring應用的主幹的java對象。它們被Spring IOC容器初始化,裝配,和管理。這些beans經過容器中配置的元數據建立。好比,以XML文件中<bean/> 的形式定義。
Spring 框架定義的beans都是單件beans。在bean tag中有個屬性」singleton」,若是它被賦爲TRUE,bean 就是單件,不然就是一個 prototype bean。默認是TRUE,因此全部在Spring框架中的beans 缺省都是單件。點擊這裏一圖Spring Bean的生命週期。
22. 一個 Spring Bean 定義 包含什麼?
一個Spring Bean 的定義包含容器必知的全部配置元數據,包括如何建立一個bean,它的生命週期詳情及它的依賴。
23. 如何給Spring 容器提供配置元數據?
這裏有三種重要的方法給Spring 容器提供配置元數據。
XML配置文件。
基於註解的配置。
基於java的配置。
24. 你怎樣定義類的做用域?
當定義一個<bean> 在Spring裏,咱們還能給這個bean聲明一個做用域。它能夠經過bean 定義中的scope屬性來定義。如,當Spring要在須要的時候每次生產一個新的bean實例,bean的scope屬性被指定爲prototype。另外一方面,一個bean每次使用的時候必須返回同一個實例,這個bean的scope 屬性 必須設爲 singleton。
25. 解釋Spring支持的幾種bean的做用域
Spring框架支持如下五種bean的做用域:
缺省的Spring bean 的做用域是Singleton。
26. Spring框架中的單例bean是線程安全的嗎?
不,Spring框架中的單例bean不是線程安全的。
27. 解釋Spring框架中bean的生命週期
點擊這裏一圖Spring Bean的生命週期。
28. 哪些是重要的bean生命週期方法? 你能重載它們嗎?
有兩個重要的bean 生命週期方法,第一個是setup , 它是在容器加載bean的時候被調用。第二個方法是 teardown 它是在容器卸載類的時候被調用。
The bean 標籤有兩個重要的屬性(init-method和destroy-method)。用它們你能夠本身定製初始化和註銷方法。它們也有相應的註解(@PostConstruct和@PreDestroy)。
29. 什麼是Spring的內部bean?
當一個bean僅被用做另外一個bean的屬性時,它能被聲明爲一個內部bean,爲了定義inner bean,在Spring 的 基於XML的 配置元數據中,能夠在 <property/>或 <constructor-arg/> 元素內使用<bean/> 元素,內部bean一般是匿名的,它們的Scope通常是prototype。
30. 在 Spring中如何注入一個java集合?
Spring提供如下幾種集合的配置元素:
31. 什麼是bean裝配?
裝配,或bean 裝配是指在Spring 容器中把bean組裝到一塊兒,前提是容器須要知道bean的依賴關係,如何經過依賴注入來把它們裝配到一塊兒。
32. 什麼是bean的自動裝配?
Spring 容器可以自動裝配相互合做的bean,這意味着容器不須要<constructor-arg>和<property>配置,能經過Bean工廠自動處理bean之間的協做。
33. 解釋不一樣方式的自動裝配
有五種自動裝配的方式,能夠用來指導Spring容器用自動裝配方式來進行依賴注入
34.自動裝配有哪些侷限性?
自動裝配的侷限性是:
35. 你能夠在Spring中注入一個null 和一個空字符串嗎?
能夠。
Spring註解
36. 什麼是基於Java的Spring註解配置? 給一些註解的例子
基於Java的配置,容許你在少許的Java註解的幫助下,進行你的大部分Spring配置而非經過XML文件。
以@Configuration 註解爲例,它用來標記類能夠當作一個bean的定義,被Spring IOC容器使用。另外一個例子是@Bean註解,它表示此方法將要返回一個對象,做爲一個bean註冊進Spring應用上下文。點擊這裏學習JAVA幾大元註解。
37. 什麼是基於註解的容器配置?
相對於XML文件,註解型的配置依賴於經過字節碼元數據裝配組件,而非尖括號的聲明。
開發者經過在相應的類,方法或屬性上使用註解的方式,直接組件類中進行配置,而不是使用xml表述bean的裝配關係。
38. 怎樣開啓註解裝配?
註解裝配在默認狀況下是不開啓的,爲了使用註解裝配,咱們必須在Spring配置文件中配置 <context:annotation-config/>元素。
39. @Required 註解
這個註解代表bean的屬性必須在配置的時候設置,經過一個bean定義的顯式的屬性值或經過自動裝配,若@Required註解的bean屬性未被設置,容器將拋出BeanInitializationException。
40. @Autowired 註解
@Autowired 註解提供了更細粒度的控制,包括在何處以及如何完成自動裝配。它的用法和@Required同樣,修飾setter方法、構造器、屬性或者具備任意名稱和/或多個參數的PN方法。
41. @Qualifier 註解
當有多個相同類型的bean卻只有一個須要自動裝配時,將@Qualifier 註解和@Autowire 註解結合使用以消除這種混淆,指定須要裝配的確切的bean。點擊這裏學習更多經常使用註解。
Spring數據訪問
42.在Spring框架中如何更有效地使用JDBC?
使用SpringJDBC 框架,資源管理和錯誤處理的代價都會被減輕。因此開發者只需寫statements 和 queries從數據存取數據,JDBC也能夠在Spring框架提供的模板類的幫助下更有效地被使用,這個模板叫JdbcTemplate (例子見這裏here)
43. JdbcTemplate
JdbcTemplate 類提供了不少便利的方法解決諸如把數據庫數據轉變成基本數據類型或對象,執行寫好的或可調用的數據庫操做語句,提供自定義的數據錯誤處理。
44. Spring對DAO的支持
Spring對數據訪問對象(DAO)的支持旨在簡化它和數據訪問技術如JDBC,Hibernate or JDO 結合使用。這使咱們能夠方便切換持久層。編碼時也不用擔憂會捕獲每種技術特有的異常。
45. 使用Spring經過什麼方式訪問Hibernate?
在Spring中有兩種方式訪問Hibernate:
46. Spring支持的ORM
Spring支持如下ORM:
47.如何經過HibernateDaoSupport將Spring和Hibernate結合起來?
用Spring的 SessionFactory 調用 LocalSessionFactory。集成過程分三步:
48. Spring支持的事務管理類型
Spring支持兩種類型的事務管理:
49. Spring框架的事務管理有哪些優勢?
50. 你更傾向用那種事務管理類型?
大多數Spring框架的用戶選擇聲明式事務管理,由於它對應用代碼的影響最小,所以更符合一個無侵入的輕量級容器的思想。聲明式事務管理要優於編程式事務管理,雖然比編程式事務管理(這種方式容許你經過代碼控制事務)少了一點靈活性。
Spring面向切面編程(AOP)
51. 解釋AOP
面向切面的編程,或AOP, 是一種編程技術,容許程序模塊化橫向切割關注點,或橫切典型的責任劃分,如日誌和事務管理。
52. Aspect 切面
AOP核心就是切面,它將多個類的通用行爲封裝成可重用的模塊,該模塊含有一組API提供橫切功能。好比,一個日誌模塊能夠被稱做日誌的AOP切面。根據需求的不一樣,一個應用程序能夠有若干切面。在Spring AOP中,切面經過帶有@Aspect註解的類實現。
52. 在Spring AOP 中,關注點和橫切關注的區別是什麼?
關注點是應用中一個模塊的行爲,一個關注點可能會被定義成一個咱們想實現的一個功能。
橫切關注點是一個關注點,此關注點是整個應用都會使用的功能,並影響整個應用,好比日誌,安全和數據傳輸,幾乎應用的每一個模塊都須要的功能。所以這些都屬於橫切關注點。
54. 鏈接點
鏈接點表明一個應用程序的某個位置,在這個位置咱們能夠插入一個AOP切面,它其實是個應用程序執行Spring AOP的位置。
55. 通知
通知是個在方法執行前或執行後要作的動做,其實是程序執行時要經過SpringAOP框架觸發的代碼段。
Spring切面能夠應用五種類型的通知:
56. 切點
切入點是一個或一組鏈接點,通知將在這些位置執行。能夠經過表達式或匹配的方式指明切入點。
57. 什麼是引入?
引入容許咱們在已存在的類中增長新的方法和屬性。
58. 什麼是目標對象?
被一個或者多個切面所通知的對象。它一般是一個代理對象。也指被通知(advised)對象。
59. 什麼是代理?
代理是通知目標對象後建立的對象。從客戶端的角度看,代理對象和目標對象是同樣的。
60. 有幾種不一樣類型的自動代理?
BeanNameAutoProxyCreator
DefaultAdvisorAutoProxyCreator
Metadata autoproxying
61. 什麼是織入。什麼是織入應用的不一樣點?
織入是將切面和到其餘應用類型或對象鏈接或建立一個被通知對象的過程。
織入能夠在編譯時,加載時,或運行時完成。
62. 解釋基於XML Schema方式的切面實現
在這種狀況下,切面由常規類以及基於XML的配置實現。
63. 解釋基於註解的切面實現
在這種狀況下(基於@AspectJ的實現),涉及到的切面聲明的風格與帶有java5標註的普通java類一致。
Spring 的MVC
64. 什麼是Spring的MVC框架?
Spring 配備構建Web 應用的全功能MVC框架。Spring能夠很便捷地和其餘MVC框架集成,如Struts,Spring 的MVC框架用控制反轉把業務對象和控制邏輯清晰地隔離。它也容許以聲明的方式把請求參數和業務對象綁定。
65. DispatcherServlet
Spring的MVC框架是圍繞DispatcherServlet來設計的,它用來處理全部的HTTP請求和響應。
66. WebApplicationContext
WebApplicationContext 繼承了ApplicationContext 並增長了一些WEB應用必備的特有功能,它不一樣於通常的ApplicationContext ,由於它能處理主題,並找到被關聯的servlet。
67. 什麼是Spring MVC框架的控制器?
控制器提供一個訪問應用程序的行爲,此行爲一般經過服務接口實現。控制器解析用戶輸入並將其轉換爲一個由視圖呈現給用戶的模型。Spring用一個很是抽象的方式實現了一個控制層,容許用戶建立多種用途的控制器。
68. @Controller 註解
該註解代表該類扮演控制器的角色,Spring不須要你繼承任何其餘控制器基類或引用Servlet API。
69. @RequestMapping 註解
該註解是用來映射一個URL到一個類或一個特定的方處理法上。
【學習參考】
[薦]https://www.toutiao.com/c/user/84982888621/#mid=1589035076683780
[薦]https://www.toutiao.com/i6592700941210747405/
https://www.toutiao.com/a6590673631498469901/