目錄css
https://www.cnblogs.com/ilren/p/9451616.htmlhtml
數據類型 | 字節數 | 二進制位數 | 範圍 | 規律 |
---|---|---|---|---|
byte | 1 | 8 | -128~127 | -27~27-1 |
short | 2 | 16 | -32768~32767 | -215~215-1 |
int | 4 | 32 | -2147483648~2147483647 | -231~231-1 |
long | 8 | 64 | -9223372036854775808 ~ 9223372036854775807 | -263~263-1 |
float | 4 | 32 | 1.4E-45~3.4028235E38 | |
double | 8 | 64 | 4.9E-324~1.7976931348623157E308 | |
char | 2 | 16 | 0~65535 | 0~216-1 |
boolean | 1 | 8 | true或false | true或false |
Java虛擬機主要分爲五大模塊:類裝載器子系統、運行時數據區、執行引擎、本地方法接口和垃圾收集模塊。java
1.加載:經過類的全路徑將這個類從外部加載到jvm中,同時在方法區生成該類的描述信息並在內存生成該類的Claas類型。做爲方法區這個類的數據訪問入口。變爲字節流。node
2.驗證:字節碼格式驗證,如對jvm是否有害,釋放符合當前虛擬機的要求 ,是否符合規範,有無語法錯誤。mysql
3.準備:爲類的靜態變量分配內存並根據這個靜態變量所屬的數據類型進行初始化。web
4.解析:將符號引用替換成直接引用面試
5.初始化:當初始化一個類的時候,若是發現其父類尚未進行過初始化、則須要先出發其父類的初始化。 初始化就是把變量賦爲默認值,把控件設爲默認狀態,把沒準備的準備好。正則表達式
是一塊較小內存空間
可看作當前線程執行的字節碼的行號指示器,多線程執行時線程輪流切換時恢復到正確執行位置
線程私有
線程執行Java方法,記錄虛擬機字節碼指令地址,執行Native方法,計數器爲空
惟一一個在java虛擬機規範中沒有規定任何OutOfMemoryError區域算法
線程私有,生命週期與線程相同
虛擬機棧描述Java方法執行的內存模型,每一個方法執行都會建立一個棧幀,方法的調用和執行完成與該方法棧幀的入棧和出棧對應
棧幀是方法運行時的基礎數據結構,用於存儲局部變量表、操做數棧、動態連接、方法出口等信息
此區域包含兩種異常:若是線程請求的棧深度大於虛擬機所容許的深度,將拋出StackOverflowError;若是虛擬機能夠動態擴展,若是擴展時沒法申請到足夠的內存,就會拋出OutOfMemoryErrorspring
與虛擬機棧所發揮做用類似,區別在於一個爲虛擬機執行Java方法服務,一個爲虛擬機使用到的Native方法服務
虛擬機規範中未規定具體實現,虛擬機能夠自由實現(最流行的Sun HotSpot虛擬機將本地方法棧和虛擬機棧合二爲一)
與虛擬機棧同樣,拋出StackOverflowError和OutOfMemoryError異常
Java虛擬機管理的內存中最大一塊
全部線程共享
虛擬機啓動時建立。惟一目的就是存放對象的實例,幾乎全部的對象實例都在這裏分配內存
Java堆時垃圾收集器的主要區域
java堆能夠處於物理不連續的內存空間中,邏輯上連續便可。實現時,既能夠固定大小,也能夠時可擴展的。不過當前主流虛擬機都是可擴展的(-Xmx -Xms)
若是在堆中沒有完成實例分配,而且堆沒法再擴展,將會拋出OutOfMemoryError
全部線程共享
用於存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據
Java虛擬機規範將其描述爲堆的一個邏輯部分,可是它又一個別名叫作Non-Heap
習慣HotSpot開發部署人員更願意把方法區稱爲永久代(Permanent Generation),本質上並不等價,只是HotSpot團隊選擇把GC分代收集擴展至方法區,以使垃圾收集像管理堆同樣管理這部份內存,省去編寫方法區內存管理工做,其餘虛擬機(J9)不存在永久代概念,實現方法區不受虛擬機規範約束
使用永久代由於經過-XX:MaxPermSize(jdk8已經取消)限制的上限,更容易內存溢出。極少數方法(如String.intern())會因這個致使在不一樣虛擬機下有不一樣表現。JDK1.7的HotSpot中,已經把本來放在永久代的字符串常量池移出
此區域內存回收主要針對常量池的回收和對類型的卸載。可是回收成績老是不好,可是確實又是有必要的,不然會有內存泄漏隱患
方法區沒法知足內存分佈需求時,拋出OutOfMemoryError
方法區的一部分
Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項是常量池,用於存放編譯期生成的各類字面量和符號引用,這部份內容在類加載後進入方法區的運行時常量池中存放
Java虛擬機對於Class文件每一部分的格式都有嚴格規定,可是運行時常量池沒有特殊要求。不過通常來講,除了保存Class文件中描述的符號引用外,還會把翻譯出來的直接引用也存在其中
運行時常量池時方法區的一部分,受方法區內存的限制,沒法申請到內存是拋出OutOfMemoryError
不屬於虛擬機運行時數據區的一部分,也不是虛擬機規範中定義的內存區域,可是這部份內存也被頻繁的使用,也可能致使OutOfMemoryError異常
JDK1.4中加入了NIO,經過Native函數分配堆外內存,而後經過一個存儲在Java堆中的DirectbyteBuffer對象做爲對這塊內存的引用。
這塊內存收到本機總內存的限制,設置虛擬機內存時,直接內存也不能夠忽略
https://www.cnblogs.com/blogtech/p/10073560.html
https://blog.csdn.net/viola8104/article/details/95201321
原有的 IO 是面向流的、阻塞的,NIO 則是面向塊的、非阻塞的。
java1.4之前的io模型,一鏈接對一個線程。
原始的IO是面向流的,不存在緩存的概念。Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取全部字節,它們沒有被緩存在任何地方。此外,它不能先後移動流中的數據。若是須要先後移動從流中讀取的數據,須要先將它緩存到一個緩衝區
Java IO的各類流是阻塞的,這意味着當一個線程調用read或 write方法時,該線程被阻塞,直到有一些數據被讀取,或數據徹底寫入,該線程在此期間不能再幹任何事情了。
NIO是面向緩衝區的。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動,這就增長了處理過程當中的靈活性。
Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,可是它僅能獲得目前可用的數據,若是目前沒有數據可用時,就什麼都不會獲取,而不是保持線程阻塞,因此直至數據變的能夠讀取以前,該線程能夠繼續作其餘的事情。 非阻塞寫也是如此,一個線程請求寫入一些數據到某通道,但不須要等待它徹底寫入,這個線程同時能夠去作別的事情。
通俗理解:NIO是能夠作到用一個線程來處理多個操做的。假設有10000個請求過來,根據實際狀況,能夠分配50或者100個線程來處理。不像以前的阻塞IO那樣,非得分配10000個。
在標準IO API中,你能夠操做字節流和字符流,但在新IO中,你能夠操做通道和緩衝,數據老是從通道被讀取到緩衝中或者從緩衝寫入到通道中。
NIO核心API Channel, Buffer, Selector
NIO的通道相似於流,但有些區別以下:
\1. 通道能夠同時進行讀寫,而流只能讀或者只能寫
\2. 通道能夠實現異步讀寫數據
\3. 通道能夠從緩衝讀數據,也能夠寫數據到緩衝:
緩衝區本質上是一個能夠寫入數據的內存塊,而後能夠再次讀取,該對象提供了一組方法,能夠更輕鬆地使用內存塊,使用緩衝區讀取和寫入數據一般遵循如下四個步驟:
\1. 寫數據到緩衝區;
\2. 調用buffer.flip()方法;
\3. 從緩衝區中讀取數據;
\4. 調用buffer.clear()或buffer.compat()方法;
當向buffer寫入數據時,buffer會記錄下寫了多少數據,一旦要讀取數據,須要經過flip()方法將Buffer從寫模式切換到讀模式,在讀模式下能夠讀取以前寫入到buffer的全部數據,一旦讀完了全部的數據,就須要清空緩衝區,讓它能夠再次被寫入。
Buffer在與Channel交互時,須要一些標誌:
buffer的大小/容量 - Capacity
做爲一個內存塊,Buffer有一個固定的大小值,用參數capacity表示。
當前讀/寫的位置 - Position
當寫數據到緩衝時,position表示當前待寫入的位置,position最大可爲capacity – 1;當從緩衝讀取數據時,position表示從當前位置讀取。
信息末尾的位置 - limit
在寫模式下,緩衝區的limit表示你最多能往Buffer裏寫多少數據; 寫模式下,limit等於Buffer的capacity,意味着你還能從緩衝區獲取多少數據。
一個組件,能夠檢測多個NIO channel,看看讀或者寫事件是否就緒。
多個Channel以事件的方式能夠註冊到同一個Selector,從而達到用一個線程處理多個請求成爲可能。
所謂單例,就是整個程序有且僅有一個實例。該類負責建立本身的對象,同時確保只有一個對象被建立。在Java,通常經常使用在工具類的實現或建立對象須要消耗資源。
特色
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
餓漢模式
線程安全,比較經常使用,但容易產生垃圾,由於一開始就初始化
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
雙重鎖模式
線程安全,延遲初始化。這種方式採用雙鎖機制,安全且在多線程狀況下能保持高性能。
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
雙重檢查模式,進行了兩次的判斷,第一次是爲了不不要的實例,第二次是爲了進行同步,避免多線程問題。因爲singleton=new Singleton()
對象的建立在JVM中可能會進行重排序,在多線程訪問下存在風險,使用volatile
修飾signleton
實例變量有效,解決該問題。
靜態內部類單例模式
public class Singleton { private Singleton(){ } public static Singleton getInstance(){ return Inner.instance; } private static class Inner { private static final Singleton instance = new Singleton(); } }
只有第一次調用getInstance方法時,虛擬機才加載 Inner 並初始化instance ,只有一個線程能夠得到對象的初始化鎖,其餘線程沒法進行初始化,保證對象的惟一性。目前此方式是全部單例模式中最推薦的模式,但具體仍是根據項目選擇。
java提供的動態執行機制,能夠動態的加載類,動態建立對象,動態執行方法。
step1:javac(java編譯器)將java文件編譯生成*.class文件
step2:jvm在運行過程當中,根據class.forName("")中的來找到硬盤中的***.class文件(這個過程叫作類加載——classLoad)。加載並放到方法區中,這就是
爲什麼咱們在建立類的時候,類名要和文件名相同,否則jvm沒法找到*.class文件。
step3:jvm根據***.class文件建立一個以cls命名的Class對象。這個對象能夠通向方法區,咱們能夠操做cls來得到類的全部信息。從而動態調用方法,建立對象,訪問屬性(甚至經過setAccessible來打開屬性訪問權限)。
java.lang.reflect可以獲取java中class文件的字節碼,而後將文件中的方法,變量和構造方法映射出來。
1信道
2信號量
3消息隊列
4共享內存
5套接字
根本區別:進程是操做系統資源分配的基本單位,而線程是任務調度和執行的基本單位
在開銷方面:每一個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程能夠看作輕量級的進程,同一類線程共享代碼和數據空間,每一個線程都有本身獨立的運行棧和程序計數器(PC),線程之間切換的開銷小。
所處環境:在操做系統中能同時運行多個進程(程序);而在同一個進程(程序)中有多個線程同時執行(經過CPU調度,在每一個時間片中只有一個線程執行)
內存分配方面:系統在運行的時候會爲每一個進程分配不一樣的內存空間;而對線程而言,除了CPU外,系統不會爲線程分配內存(線程所使用的資源來自其所屬進程的資源),線程組之間只能共享資源。
包含關係:沒有線程的進程能夠看作是單線程的,若是一個進程內有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,因此線程也被稱爲輕權進程或者輕量級進程。
數組不是面向對象的,存在明顯的缺陷,集合彌補了數組的缺點,比數組更靈活更實用,並且不一樣的集合框架類可適用不一樣場合。以下:
1:數組能存放基本數據類型和對象,而集合類存放的都是對象的引用,而非對象自己!
2:數組容易固定沒法動態改變,集合類容量動態改變。
3:數組沒法判斷其中實際存有多少元素,length只告訴了數組的容量,而集合的size()能夠確切知道元素的個數
4:集合有多種實現方式和不一樣適用場合,不像數組僅採用順序表方式
5:集合以類的形式存在,具備封裝、繼承、多態等類的特性,經過簡單的方法和屬性便可實現各類複雜操做,大大提升了軟件的開發效率
https://www.cnblogs.com/chenglc/p/8073049.html
Set:接口 ---實現類: HashSet、LinkedHashSet
Set的子接口SortedSet接口---實現類:TreeSet
List:接口---實現類: LinkedList,Vector,ArrayList
有序列表,容許存放重複的元素;
實現類:
ArrayList:數組實現,查詢快,增刪慢,輕量級;(線程不安全)
LinkedList:雙向鏈表實現,增刪快,查詢慢 (線程不安全)
Vector:數組實現,重量級 (線程安全、使用少)
ArrayList
底層是Object數組,因此ArrayList具備數組的查詢速度快的優勢以及增刪速度慢的缺點。而在LinkedList的底層是一種雙向循環鏈表。在此鏈表上每個數據節點都由三部分組成:前指針(指向前面的節點的位置),數據,後指針(指向後面的節點的位置)。最後一個節點的後指針指向第一個節點的前指針,造成一個循環。雙向循環鏈表的查詢效率低可是增刪效率高。ArrayList和LinkedList在用法上沒有區別,可是在功能上仍是有區別的。
LinkedList
LinkedList是採用雙向循環鏈表實現的。利用LinkedList實現棧(stack)、隊列(queue)、雙向隊列(double-ended queue )。它具備方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。常常用在增刪操做較多而查詢操做不多的狀況下:
隊列和堆棧
隊列:先進先出的數據結構。
棧:後進先出的數據結構。
注意:使用棧的時候必定不能提供方法讓不是最後一個元素的元素得到出棧的機會。
Vector
(與ArrayList類似,區別是Vector是重量級的組件,使用使消耗的資源比較多。)結論:在考慮併發的狀況下用Vector(保證線程的安全)。在不考慮併發的狀況下用ArrayList(不能保證線程的安全)。
ArrayList | Vector | |
---|---|---|
初始化策略 | 用懶加載策略,第一次add時才初始化內部數組,默認初始化大小爲10。 | 產生對象時就初始化內部數組,默認大小爲10。 |
擴容策略 | 擴容爲原數組的1.5倍 | 擴容爲原數組的2倍 |
線程安全問題 | 採用異步處理線程不安全,性能較高 | 採用synchronized修飾增刪查改方法,線程安全,性能較低(鎖粒度太粗,將當前集合對象鎖住,讀讀互斥) |
其餘 | JDK內置的Stack是Vector的子類 | |
使用場景 | 在大部分場合都是適合使用的,常適用於頻繁查找、在集合末端插入與刪除元素 | 不建議使用 |
LinkedList採用異步處理,線程不安全,頻繁在任意位置的插入與刪除考慮使用,LinkedList是Queue接口的經常使用子類。
擴展Collection接口
無序集合,不容許存放重複的元素;容許使用null元素
對 add()、equals() 和 hashCode() 方法添加了限制
HashSet和TreeSet是Set的實現
Set—》hashSet linkedHashSet
SortedSet —》 TreeSet
HashSet 的後臺有一個HashMap;初始化後臺容量;只不過生成一個HashSet的話,系統只提供key的訪問; 若是有兩個Key重複,那麼會覆蓋以前的;
實現類 :
HashSet:equals返回true,hashCode返回相同的整數;哈希表;存儲的數據是無序的。
LinkedHashSet:此實現與 HashSet 的不一樣以外在於,後者維護着一個運行於全部條目的雙重連接列表。存儲的數據是有序的。
TreeSet:有序的。
HashSet類直接實現了Set接口, 其底層實際上是包裝了一個HashMap去實現的。HashSet採用HashCode算法來存取集合中的元素,所以具備比較好的讀取和查找性能。
HashSet經常使用方法:
public boolean contains(Object o) :若是set包含指定元素,返回true
public Iterator iterator()返回set中元素的迭代器
public Object[] toArray() :返回包含set中全部元素的數組public Object[] toArray(Object[] a) :返回包含set中全部元素的數組,返回數組的運行時類型是指定數組的運行時類型
public boolean add(Object o) :若是set中不存在指定元素,則向set加入
public boolean remove(Object o) :若是set中存在指定元素,則從set中刪除
public boolean removeAll(Collection c) :若是set包含指定集合,則從set中刪除指定集合的全部元素
public boolean containsAll(Collection c) :若是set包含指定集合的全部元素,返回true。若是指定集合也是一個set,只有是當前set的子集時,方法返回true
實現Set接口的HashSet,依靠HashMap來實現的。
咱們應該爲要存放到散列表的各個對象定義hashCode()和equals()。
前面說過,Set集合是不容許重複元素的,不然將會引起各類奇怪的問題。那麼HashSet如何判斷元素重複呢?
HashSet須要同時經過equals和HashCode來判斷兩個元素是否相等,具體規則是,若是兩個元素經過equals爲true,而且兩個元素的hashCode相等,則這兩個元素相等(即重複)。
因此若是要重寫保存在HashSet中的對象的equals方法,也要重寫hashCode方法,重寫先後hashCode返回的結果相等(即保證保存在同一個位置)。全部參與計算 hashCode() 返回值的關鍵屬性,都應該用於做爲 equals() 比較的標準。
試想若是重寫了equals方法但不重寫hashCode方法,即相同equals結果的兩個對象將會被HashSet看成兩個元素保存起來,這與咱們設計HashSet的初衷不符(元素不重複)。另外若是兩個元素哈市Code相等但equals結果不爲true,HashSet會將這兩個元素保存在同一個位置,並將超過一個的元素以鏈表方式保存,這將影響HashSet的效率。若是重寫了equals方法但沒有重寫hashCode方法,則HashSet可能沒法正常工做。
如何達到不能存在重複元素的目的?
「鍵」就是咱們要存入的對象,「值」則是一個常量。這樣能夠確保,咱們所須要的存儲的信息
之是「鍵」。而「鍵」在Map中是不能重複的,這就保證了咱們存入Set中的全部的元素都不重複。
HashSet如何過濾重複元素
調用元素HashCode得到哈希碼--》判斷哈希碼是否相等,不相等則錄入
---》相等則判斷equals()後是否相等,不相等在進行 hashcode錄入,相等不錄入
LinkedHashSet的特徵
LinkedHashSet是HashSet的一個子類,LinkedHashSet也根據HashCode的值來決定元素的存儲位置,但同時它還用一個鏈表來維護元素的插入順序,插入的時候即要計算hashCode又要維護鏈表,而遍歷的時候只須要按鏈表來訪問元素。查看LinkedHashSet的源碼發現它是樣的,
TreeSet實現了SortedSet接口,顧名思義這是一種排序的Set集合,查看jdk源碼發現底層是用TreeMap實現的,本質上是一個紅黑樹原理。 正由於它是排序了的,因此相對HashSet來講,TreeSet提供了一些額外的按排序位置訪問元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().
TreeSet的排序分兩種類型,一種是天然排序,另外一種是定製排序。
TreeSet 會調用compareTo方法比較元素大小,而後按升序排序。因此天然排序中的元素對象,都必須實現了Comparable接口,不然會跑出異常。對於TreeSet判斷元素是否重複的標準,也是調用元素從Comparable接口繼承而來額compareTo方法,若是返回0則是重複元素(兩個元素I相等)。Java的常見類都已經實現了Comparable接口,下面舉例說明沒有實現Comparable存入TreeSet時引起異常的狀況。
還有個重要問題是,由於TreeSet會調用元素的compareTo方法,這就要求全部元素的類型都相同,不然也會發生異常。也就是說,TreeSet只容許存入同一類的元素。例以下面這個例子就會拋出類型轉換異常
TreeSet還有一種排序就是定製排序,定製排序時候,須要關聯一個 Comparator對象,由Comparator提供排序邏輯。
TreeSet是依靠TreeMap來實現的。
TreeSet是一個有序集合,TreeSet中元素將按照升序排列,缺省是按照天然順序進行排列,意味着TreeSet中元素要實現Comparable接口
咱們能夠在構造TreeSet對象時,傳遞實現了Comparator接口的比較器對象。
Comparable和Comparator
Comparable 接口以提供天然排序順序。
對於那些沒有天然順序的類、或者當您想要一個不一樣於天然順序的順序時,您能夠實現
Comparator 接口來定義您本身的排序函數。能夠將Comparator傳遞給Collections.sort或Arrays.sort。
Comparator接口
當一個類並未實現Comparable,或者不喜歡缺省的Comaparable行爲。能夠實現Comparator接口
直接實現Comparator的compare接口完成自定義比較類。
幾種Set的比較:
HashSet外部無序地遍歷成員。
成員可爲任意Object子類的對象,但若是覆蓋了equals方法,同
時注意修改hashCode方法。
TreeSet外部有序地遍歷成員;
附加實現了SortedSet, 支持子集等要求順序的操做
成員要求實現Comparable接口,或者使用Comparator構造
TreeSet。成員通常爲同一類型。
LinkedHashSet外部按成員的插入順序遍歷成員
成員與HashSet成員相似
HashSet是基於Hash算法實現的,其性能一般都優於TreeSet。咱們一般都應該使用HashSet,在咱們須要排序的功能時,咱們才使用TreeSet。
HashSet的元素存放順序和咱們添加進去時候的順序沒有任何關係,而LinkedHashSet 則保持元素的添加順序。TreeSet則是對咱們的Set中的元素進行排序存放。
通常來講,當您要從集合中以有序的方式抽取元素時,TreeSet 實現就會有用處。爲了能順利進行,添加到 TreeSet 的元素必須是可排序的。 而您一樣須要對添加到TreeSet中的類對象實現 Comparable 接口的支持。通常說來,先把元素添加到 HashSet,再把集合轉換爲 TreeSet 來進行有序遍歷會更快。
集合框架的第二類接口樹。
它提供了一組鍵值的映射。其中存儲的每一個對象都有一個相應的關鍵字(key),關鍵字決定了對象在Map中的存儲位置。
關鍵字應該是惟一的,每一個key 只能映射一個value。
實現類:
HashMap、TreeMap、LinkedHashMap、Hashtable等
HashMap:鍵值對,key不能重複,可是value能夠重複;key的實現就是HashSet;value對應着放;容許null的鍵或值;
Hashtable:線程安全的,不容許null的鍵或值;
Properties::key和value都是String類型,用來讀配置文件;
TreeMap:對key排好序的Map; key 就是TreeSet, value對應每一個key; key要實現Comparable接口或TreeMap有本身的構造器;
LinkedHashMap: 此實現與 HashMap 的不一樣之處在於,後者維護着一個運行於全部條目的雙重連接列表。存儲的數
據是有序的。
HashMap:
Map 主要用於存儲鍵(key)值(value)對,根據鍵獲得值,所以鍵不容許重複,但容許值重複。
HashMap 是一個最經常使用的Map,它根據鍵的HashCode 值存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。
HashMap最多隻容許一條記錄的鍵爲Null;容許多條記錄的值爲 Null;
HashMap不支持線程的同步,即任一時刻能夠有多個線程同時寫HashMap;可能會致使數據的不一致。若是須要同步,能夠用 Collections的synchronizedMap方法使HashMap具備同步的能力。
HashMap實現原理---散列
Hash哈希算法的意義在於提供了一種快速存取數據的方法,它用一種算法創建鍵值與真實值之間的對應關係。散列表又稱爲哈希表。散列表算法的基本思想是:以結點的關鍵字爲自變量,經過必定的函數關係(散列函數)計算出對應的函數值,以這個值做爲該結點存儲在散列表中地址。
當散列表中的元素存放太滿,就必須進行再散列,將產生一個新的散列表,全部元素存放到新的散列表中,原先的散列表將被刪除。在Java語言中,經過負載因子(load factor)來決定什麼時候對散列表進行再散列。例如:若是負載因子0.75,當散列表中已經有75%位置已經放滿,那麼將進行再散列。
負載因子越高(越接近1.0),內存的使用效率越高,元素的尋找時間越長。負載因子越低(越接近0.0),元素的尋找時間越短,內存浪費越多。
什麼時候需重寫equals?
當一個類有本身特有的「邏輯相等」概念(不一樣於對象身份的概念);
Object類僅僅提供了一個對引用的比較,若是兩個引用不是同一個那就返回false,這是沒法知足大多數對象比較的須要的,因此要覆蓋;
使用==操做符檢查實參是否爲指向對象的引用」
使用instanceof操做符檢查實參是否爲正確的類型
把實參轉換到正確的類型;
對於該類中每個「關鍵」域,檢查實參中的域與當前對象中對應的域值是否匹 配。對於既不是float也不是double類型的基本類型的域,能夠使用==操做符 進行比較;對於對象引用類型的域,能夠遞歸地調用所引用的對象的equals方法,對於float和double類型的域,先轉換成int或long類型的值,而後使用==操做符比較;
當你編寫完成了equals方法以後,應該問本身三個問題:它是不是對稱的、傳 遞的、一致的? 若是答案是否認的,那麼請找到 這些特性未能知足的緣由,再修改equals方法的代碼
equals()和hashCode()同時覆寫
尤爲強調當一個對象被看成鍵值(或索引)來使用的時候要重寫這兩個方法;
覆寫equals後,兩個不一樣實例可能在邏輯上相等,可是根據Object.hashCode方法卻產生不一樣的散列碼,違反「相等的對象必須具備相等的散列碼」。
致使,當你用其中的一個做爲鍵保存到hashMap、hasoTable或hashSet中,再以「相等的」找另 一個做爲鍵值去查找他們的時候,則根本找不到
不一樣類型的hashCode取值
若是該域是布爾型的,計算(f?0:1)
若是是char,short,byte或int,計算(int)f
若是是long類型,計算(int)(f^(f>>>32))
若是是float類型,計算Float.floatToIntBits(f)
若是是double類型,計算Dobule.doubleToLongBits(f)
若是該域是一個對象引用,遞歸調用hashCode
若是該域是一個數組,則把每一個元素當作單獨的域來處理,對每一個重要的元素計算一個散列碼,
Map集合比較:
HashMap的存入順序和輸出順序無關。
LinkedHashMap 則保留了鍵值對的存入順序。
TreeMap則是對Map中的元素進行排序。
由於HashMap和LinkedHashMap 存儲數據的速度比直接使用TreeMap 要快,存取效率要高。
當完成了全部的元素的存放後,咱們再對整個的Map中的元素進行排序。這樣能夠提升整個程序的運行的效率,縮短執行時間。
注意:TreeMap中是根據鍵(Key)進行排序的。而若是咱們要使用TreeMap來進行正常的排序的話,Key 中存放的對象必須實現Comparable 接口。
Map經常使用方法:
Object put(Object key,Object value):用來存放一個鍵-值對Map中
Object remove(Object key):根據key(鍵),移除鍵-值對,並將值返回
void putAll(Map mapping) :將另一個Map中的元素存入當前的Map中
void clear() :清空當前Map中的元素
Object get(Object key) :根據key(鍵)取得對應的值
boolean containsKey(Object key) :判斷Map中是否存在某鍵(key)
boolean containsValue(Object value):判斷Map中是否存在某值(value)
public Set keySet() :返回全部的鍵(key),並使用Set容器存放
public Collection values() :返回全部的值(Value),並使用Collection存放
public Set entrySet() :返回一個實現 Map.Entry 接口的元素 Set
集合遍歷
一、加強for循環 for(Obj o:c){syso(o)}
二、使用iterator , Iterator it=c.iterator;
while(it.hasNext()){Object o = it.next()}
三、普通循環:for(Iterator it=c.iterator();it.hasNext();){it.next() }
1.ArrayList: 元素單個,效率高,多用於查詢
2.Vector: 元素單個,線程安全,多用於查詢
3.LinkedList:元素單個,多用於插入和刪除
4.HashMap: 元素成對,元素可爲空
5.HashTable: 元素成對,線程安全,元素不可爲空
HashMap | Hashtable | |
---|---|---|
同步性 | 線程不安全 | 線程安全 |
擴容 | old*2(初始16) | old*2 +1(初始11) |
值 | 可爲空 | 不可爲空 |
HashMap和Hashtable的區別:
HashMap和Hashtable都是java的集合類,均可以用來存放java對象,這是他們的相同點
如下是他們的區別:
1.歷史緣由:
Hashtable是基於陳舊的Dictionary類的,HashMap是java 1.2引進的Map接口的一個現實。
2.同步性:
Hashtable是同步的,這個類中的一些方法保證了Hashtable中的對象是線程安全的,而HashMap則是異步的,所以HashMap中的對象並非線程安全的,由於同步的要求會影響執行的效率,因此若是你不須要線程安全的結合那麼使用HashMap是一個很好的選擇,這樣能夠避免因爲同步帶來的沒必要要的性能開銷,從而提升效率,咱們通常所編寫的程序都是異步的,但若是是服務器端的代碼除外。
3.值:
HashMap可讓你將空值做爲一個表的條目的key或value
Hashtable是不能放入空值(null)的
ArrayList和Vector的區別:
ArrayList與Vector都是java的集合類,都是用來存放java對象,這是他們的相同點,
區別:
1.同步性:
Vector是同步的,這個類的一些方法保證了Vector中的對象的線程安全的,而ArrayList則是異步的,所以ArrayList中的對象並不 是線程安全的,由於同步要求會影響執行的效率,因此你不須要線程安全的集合那麼使用ArrayList是一個很好的選擇,這樣能夠避免因爲同步帶來的沒必要 要的性能開銷。
2.數據增加:
從內部實現的機制來說,ArrayList和Vector都是使用數組(Array)來控制集合中的對象,當你向兩種類型中增長元素的時候,若是元素的數目超過了內部數組目前的長度他們都須要擴展內部數組的長度,Vector缺省狀況下自動增加原來一倍的數組長度,ArrayList是原來的50%,因此最後你得到的這個集合所佔的空間老是比你實際須要的要大,因此若是你要在集合中保存大量的數據,那麼使用Vector有一些優點,由於你能夠經過設置集合的初始大小來避免沒必要要的資源開銷。
總結:
1)若是要求線程安全,使用Vector,Hashtable
2)若是不要求線程安全,使用ArrayList,LinkedList,HashMap
3)若是要求鍵值對,則使用HashMap,Hashtable
4)若是數據量很大,又要求線程安全考慮Vector
arraylist和linkedlist聯繫與區別
1.ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。
2.對於隨機訪問get和set,ArrayList以爲優於LinkedList,由於LinkedList要移動指針。
3.對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。 這一點要看實際狀況的。若只對單條數據插入或刪除,ArrayList的速度反而優於LinkedList。但如果批量隨機的插入刪除數據,LinkedList的速度大大優於ArrayList. 由於ArrayList每插入一條數據,要移動插入點及以後的全部數據。
HashMap與TreeMap聯繫與區別一、 HashMap經過hashcode對其內容進行快速查找,而TreeMap中全部的元素都保持着某種固定的順序,若是你須要獲得一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。
二、在Map 中插入、刪除和定位元素,HashMap是最好的選擇。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。使用HashMap要求添加的鍵類明肯定義了hashCode()和 equals()的實現。
兩個map中的元素同樣,但順序不同,致使hashCode()不同。
一樣作測試:
在HashMap中,一樣的值的map,順序不一樣,equals時,false;
而在treeMap中,一樣的值的map,順序不一樣,equals時,true,說明,treeMap在equals()時是整理了順序了的。
棧 | |
---|---|
Stack() | 定義一個空棧 |
empty() | 棧空返回真,不然返回假 |
peek() | 獲取棧頂值,不出棧 |
pop() | 棧頂值出棧 |
push() | 入棧 |
search(Object o) | 回對象O在棧中的位置(以1開始) |
隊列 | |
add() | 入隊(若失敗則拋出IllegalStateException異常) |
offer() | 將指定元素插入隊列,成功返回true,不然返回false |
element() | 獲取隊頭的值,但不出隊(若隊列爲空則拋出異常NoSuchElementException) |
peek() | 獲取隊頭的值,但不出隊(若隊列爲空則返回null) |
poll() | 獲取並移除隊頭(若隊列空則返回null) |
remove() | 獲取並移除隊頭(若隊列空則拋出NoSuchElementException異常) |
前序遍歷:
1.訪問根節點
2.前序遍歷左子樹
3.前序遍歷右子樹
中序遍歷:
1.中序遍歷左子樹
2.訪問根節點
3.中序遍歷右子樹
後序遍歷:
1.後序遍歷左子樹
2.後序遍歷右子樹
3.訪問根節點s
1.new
2.反射
3.clone
4.反序列化
1.public
2.default
3.protect
4.private
//Date類型轉爲String Date date = new Date(); //設置字符串格式 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String dateString = simpleDateFormat.format(date); //String類型轉爲Date String dateString = "2018-10-25"; //注意String的格式要與simpleDateFormat設置的格式相同! SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = simpleDateFormat.parse(dateString);
查找二進制中1的個數。
public class Solution { public int NumberOf1(int n) { int t=0; char[]ch=Integer.toBinaryString(n).toCharArray(); for(int i=0;i<ch.length;i++){ if(ch[i]=='1'){ t++; } } return t; } }
import java.util.Scanner; public class number { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入一個二進制數: "); String temp = sc.nextLine(); int two = Integer.parseInt(temp, 2); // 2進制 int eight = Integer.parseInt(temp, 8); // 8進制 System.out.println("二進制轉爲十進制: " + two); System.out.println("八進制轉爲十進制: " + eight); } } 結果: 請輸入一個二進制數: 1001 二進制轉爲十進制: 9 八進制轉爲十進制: 513
// String 轉int String str = 「123」; int a = Integer.parseInt(str); // int 轉 String int b = 1; String str = String.valueOf(b);
System.out.println(Integer.toBinaryString(9^3));//輸出二進制時9與3不一樣的位數 結果: 1010
1,打開windows--->Preferences---->java---->Editor---->content assist; 2.在右邊的框中 Auto-Activation框中有一個Auto activation triggers for Java:選項,在默認的狀況下框中只有一個點,在框中"."的後面輸入"abcdefghijklmnopqrstuvwxyz ",而後點擊apply而後點擊OK; 3.這樣輸入每個字母都有語法提示
輸出:7 -> 0 -> 8
緣由:342 + 465 = 807
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode head = new ListNode(0); ListNode result = head; int temp = 0; while (l1 != null || l2 != null) { int val1 = l1 == null ? 0 : l1.val; int val2 = l2 == null ? 0 : l2.val; int val = val1 + val2 + temp; temp = val / 10; val = val % 10; result.next = new ListNode(val); result = result.next; if (l1 != null) l1 = l1.next; if (l2 != null) l2 = l2.next; } if (temp > 0) { result.next = new ListNode(temp); } return head.next; } }
class Solution { public int lengthOfLongestSubstring(String s) { if (s.isEmpty()){ return 0; }else{ int max=1; int sum=1; String s1=""+s.charAt(0); if(s.length()==1){ return 1; } for (int i = 1; i < s.length(); i++) { if(s1.indexOf(""+s.charAt(i))!=-1){//判斷是否有重複的 int n1=s1.length()-s1.indexOf(""+s.charAt(i))-1; sum=n1+1; s1=s1.substring(s1.indexOf(""+s.charAt(i))+1,s1.length())+s.charAt(i); }else{ sum=sum+1; s1=s1+s.charAt(i); } if(sum>max){ max=sum; } } return max; } } }
'.' 匹配任意單個字符 '*' 匹配零個或多個前面的那一個元素 class Solution { public boolean isMatch(String s, String p) { if(s==null&&p==null){ return true; } if(s==null&&p!=null||p==null&&s!=null){ return false; } if (s.equals(p)) { return true; } return s.matches(p);//秀死我了 } }
public class Solution { public String replaceSpace(StringBuffer str) { String s=new String(str); s=s.replaceAll(" ","%20"); return s; } }
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindKthToTail(ListNode list,int k) { if (list == null) return list; ListNode node = list; int count = 0; while (node != null) { count++; node = node.next; } if (count < k) return null; ListNode p = list; for (int i = 0; i < count - k; i++) { p = p.next; } return p; } }
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode ReverseList(ListNode head) { if(head==null) return null; ListNode newHead = null; ListNode pNode = head; ListNode pPrev = null; while(pNode!=null){ ListNode pNext = pNode.next; if(pNext==null) newHead = pNode; pNode.next = pPrev; pPrev = pNode; pNode = pNext; } return newHead; } }
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { ListNode newList =new ListNode(0); if(list1==null&&list2==null) return null; else if(list1==null) return list2; else if(list2==null) return list1; else { if(list1.val<list2.val){ newList=list1; list1=list1.next; }else{ newList=list2; list2=list2.next; } newList.next=Merge(list1,list2); return newList; } } }
Iterator | ListIterator |
---|---|
hasNext | add(E e) |
next() | hasNext() |
remove() | hasPrevious() |
next() | |
nextIndex() | |
previous() | |
previousIndex() | |
remove() | |
set(E e) |
Iterator迭代器包含的方法:
hasNext():若是迭代器指向位置後面還有元素,則返回 true,不然返回false
next():返回集合中Iterator指向位置後面的元素
remove():刪除集合中Iterator指向位置後面的元素
ListIterator迭代器包含的方法:
add(E e):將指定的元素插入列表,插入位置爲迭代器當前位置以前
hasNext():以正向遍歷列表時,若是列表迭代器後面還有元素,則返回 true,不然返回false
hasPrevious():若是以逆向遍歷列表,列表迭代器前面還有元素,則返回 true,不然返回false
next():返回列表中ListIterator指向位置後面的元素
nextIndex():返回列表中ListIterator所需位置後面元素的索引
previous():返回列表中ListIterator指向位置前面的元素
previousIndex():返回列表中ListIterator所需位置前面元素的索引
remove():從列表中刪除next()或previous()返回的最後一個元素(有點拗口,意思就是對迭代器使用hasNext()方法時,刪除ListIterator指向位置後面的元素;當對迭代器使用hasPrevious()方法時,刪 除ListIterator指向位置前面的元素)
set(E e):從列表中將next()或previous()返回的最後一個元素返回的最後一個元素更改成指定元素e
Iterator 和 ListIterator 的不一樣點
1.使用範圍不一樣,Iterator能夠應用於全部的集合,Set、List和Map和這些集合的子類型。而ListIterator只能用於List及其子類型。
2.ListIterator有add方法,能夠向List中添加對象,而Iterator不能。
3.ListIterator和Iterator都有hasNext()和next()方法,能夠實現順序向後遍 歷,可是ListIterator有hasPrevious()和previous()方法,能夠實現逆向(順序向前)遍歷。Iterator不能夠。
4.ListIterator能夠定位當前索引的位置,nextIndex()和previousIndex()能夠實現。Iterator沒有此功能。
5.均可實現刪除操做,可是ListIterator能夠實現對象的修改,set()方法能夠實現。Iterator僅能遍歷,不能修改。
1.n個節點的二叉樹一共有((2n)!)/(n! * (n+1)!)種
2.n層二叉樹的第n層最多爲2^(n-1)個
3.二叉樹節點計算公式 N = n0+n1+n2,度爲0的葉子節點比度爲2的節點數多一個。N=1n1+2n2+1
4.對任何一棵二叉樹T,若是其終端節點數爲n0,度爲2的節點數爲n2,則n0=n2+1
5.具備n個節點的徹底二叉樹的深度爲log2(n) + 1
也就是當你看到heap相關的時候就確定是堆棧溢出了,此時若是代碼沒有問題的狀況下,適當調整-Xmx和-Xms是能夠避免的,不過必定是代碼沒有問題的前提,爲何會溢出呢,要麼代碼有問題,要麼訪問量太多而且每一個訪問的時間太長或者數據太多,致使數據釋放不掉,由於垃圾回收器是要找到那些是垃圾才能回收,這裏它不會認爲這些東西是垃圾,天然不會去回收了;主意這個溢出以前,可能系統會提早先報錯關鍵字爲:
這種狀況是當系統處於高頻的GC狀態,並且回收的效果依然不佳的狀況,就會開始報這個錯誤,這種狀況通常是產生了不少不能夠被釋放的對象,有多是引用使用不當致使,或申請大對象致使,可是java heap space的內存溢出有可能提早不會報這個錯誤,也就是可能內存就直接不夠致使,而不是高頻GC.
關鍵信息爲:
java.lang.OutOfMemoryError: PermGen space
緣由:系統的代碼很是多或引用的第三方包很是多、或代碼中使用了大量的常量、或經過intern注入常量、或者經過動態代碼加載等方法,致使常量池的膨脹,雖然JDK 1.5之後能夠經過設置對永久帶進行回收,可是咱們但願的是這個地方是不作GC的,它夠用就行,因此通常狀況下今年少作相似的操做,因此在面對這種狀況經常使用的手段是:增長-XX:PermSize和-XX:MaxPermSize的大小。
溢出關鍵字:
java.lang.OutOfMemoryError: Direct buffer memory
若是你在直接或間接使用了ByteBuffer中的allocateDirect方法的時候,而不作clear的時候就會出現相似的問題,常規的引用程序IO輸出存在一個內核態與用戶態的轉換過程,也就是對應直接內存與非直接內存,若是常規的應用程序你要將一個文件的內容輸出到客戶端須要經過OS的直接內存轉換拷貝到程序的非直接內存(也就是heap中),而後再輸出到直接內存由操做系統發送出去,而直接內存就是由OS和應用程序共同管理的,而非直接內存能夠直接由應用程序本身控制的內存,jvm垃圾回收不會回收掉直接內存這部分的內存,因此要注意了哦。
若是常常有相似的操做,能夠考慮設置參數:-XX:MaxDirectMemorySize
溢出關鍵字:
java.lang.StackOverflowError
這個參數直接說明一個內容,就是-Xss過小了,咱們申請不少局部調用的棧針等內容是存放在用戶當前所持有的線程中的,線程在jdk 1.4之前默認是256K,1.5之後是1M,若是報這個錯,只能說明-Xss設置得過小,固然有些廠商的JVM不是這個參數,本文僅僅針對Hotspot VM而已;不過在有必要的狀況下能夠對系統作一些優化,使得-Xss的值是可用的。
溢出關鍵字:
java.lang.OutOfMemoryError: unable to create new native thread
上面第四種溢出錯誤,已經說明了線程的內存空間,其實線程基本只佔用heap之外的內存區域,也就是這個錯誤說明除了heap之外的區域,沒法爲線程分配一塊內存區域了,這個要麼是內存自己就不夠,要麼heap的空間設置得太大了,致使了剩餘的內存已經很少了,而因爲線程自己要佔用內存,因此就不夠用了,說明了緣由,如何去修改,不用我多說,你懂的。
溢出關鍵字
java.lang.OutOfMemoryError: request {} byte for {}out of swap
這類錯誤通常是因爲地址空間不夠而致使。
六大類常見溢出已經說明JVM中99%的溢出狀況,要逃出這些溢出狀況很是困難,除非一些很怪異的故障問題會發生,好比因爲物理內存的硬件問題,致使了code cache的錯誤(在由byte code轉換爲native code的過程當中出現,可是機率極低),這種狀況內存 會被直接crash掉,相似還有swap的頻繁交互在部分系統中會致使系統直接被crash掉,OS地址空間不夠的話,系統根本沒法啓動,呵呵;JNI的濫用也會致使一些本地內存沒法釋放的問題,因此儘可能避開JNI;socket鏈接數據打開過多的socket也會報相似:IOException: Too many open files等錯誤信息。
是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;好比申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
是指程序在申請內存後,沒法釋放已申請的內存空間,一次內存泄露危害能夠忽略,但內存泄露堆積後果很嚴重,不管多少內存,早晚會被佔光。
memory leak會最終會致使out of memory!
6 0 000 0110
-6 1 000 0110 1 111 1001 1 111 1010
權值分別爲從19,21,2,3,6,7,10,32的結點,構造一棵哈夫曼樹,該樹的帶權路徑長度是?
參數名 | 意義 |
---|---|
corePoolSize | 核心池的大小 |
maximumPoolSize | 池中容許的最大線程數,這個參數表示了線程池中最多能建立的線程數量 |
keepAliveTime | 當線程空閒時間達到keepAliveTime,該線程會退出,直到線程數量等於corePoolSize。若是allowCoreThreadTimeout設置爲true,則全部線程均會退出直到線程數量爲0。 |
allowCoreThreadTimeout | 是否容許核心線程空閒退出,默認值爲false。 |
ThreadFactory threadFactory | 執行程序建立新線程時使用的工廠 |
RejectedExecutionHandler handler | 因爲超出線程範圍和隊列容量而使執行被阻塞時所使用的處理程序;ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,可是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,而後從新嘗試執行任務(重複此過程) ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 |
allowCoreThreadTimeout | 是否容許核心線程空閒退出,默認值爲false。 |
SynchronousQueue | SynchronousQueue沒有容量,是無緩衝等待隊列,是一個不存儲元素的阻塞隊列,會直接將任務交給消費者,必須等隊列中的添加元素被消費後才能繼續添加新的元素。SynchronousQueue阻塞隊列通常要求maximumPoolSizes爲無界,避免線程拒絕執行操做 |
LinkedBlockingQueue | LinkedBlockingQueue是一個無界緩存等待隊列。當前執行的線程數量達到corePoolSize的數量時,剩餘的元素會在阻塞隊列裏等待。(因此在使用此阻塞隊列時maximumPoolSizes就至關於無效了),每一個線程徹底獨立於其餘線程。生產者和消費者使用獨立的鎖來控制數據的同步,即在高併發的狀況下能夠並行操做隊列中的數據。 |
ArrayBlockingQueue | ArrayBlockingQueue是一個有界緩存等待隊列,能夠指定緩存隊列的大小,當正在執行的線程數等於corePoolSize時,多餘的元素緩存在ArrayBlockingQueue隊列中等待有空閒的線程時繼續執行,當ArrayBlockingQueue已滿時,加入ArrayBlockingQueue失敗,會開啓新的線程去執行,當線程數已經達到最大的maximumPoolSizes時,再有新的元素嘗試加入ArrayBlockingQueue時會報錯。 |
PriorityBlockingQueue | 和ArrayBlockingQueue同樣內部使用數組實現,插入和獲取數據使用同一把鎖。不一樣的是,無論有無指定長度,都會實現能夠實現自動擴容;在構造函數需傳入comparator,用於插入元素時繼續排序,若沒有傳入comparator,則插入的元素必須實現Comparatable接口。 |
unit | 參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性TimeUnit.DAYS;TimeUnit.HOURS;TimeUnit.MINUTES;TimeUnit.SECONDS; TimeUnit.MILLISECONDS;TimeUnit.MICROSECONDS;TimeUnit.NANOSECONDS; |
largestPoolSize | 用來記錄線程池中曾經出現過的最大線程數 |
completedTaskCount | 用來記錄已經執行完畢的任務個數 |
一、池中線程數小於corePoolSize,新任務都不排隊而是直接添加新線程
二、池中線程數大於等於corePoolSize,workQueue未滿,首選將新任務加入workQueue而不是添加新線程
三、池中線程數大於等於corePoolSize,workQueue已滿,可是線程數小於maximumPoolSize,添加新的線程來處理被添加的任務
四、池中線程數大於大於corePoolSize,workQueue已滿,而且線程數大於等於maximumPoolSize,新任務被拒絕,使用handler處理被拒絕的任務
volatile int runState; static final int RUNNING=0; static final int SHUTDOWN=1; static final int STOP=2; static final int TERMINATED3;
runState表示當前線程池的狀態,它是一個volatile變量用來保證線程之間的可見性;
下面的幾個static final變量表示runState可能的幾個取值。
當建立線程池後,初始時,線程池處於RUNNING狀態;
若是調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不可以接受新的任務,它會等待全部任務執行完畢;
若是調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,而且會去嘗試終止正在執行的任務;
當線程池處於SHUTDOWN或STOP狀態,而且全部工做線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態。
定義: 即一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。
原子性是拒絕多線程操做的,不管是多核仍是單核,具備原子性的量,同一時刻只能有一個線程來對它進行操做。簡而言之,在整個操做過程當中不會被線程調度器中斷的操做,均可認爲是原子性。例如 a=1是原子性操做,可是a++和a +=1就不是原子性操做。Java中的原子性操做包括:
a. 基本類型的讀取和賦值操做,且賦值必須是數字賦值給變量,變量之間的相互賦值不是原子性操做。
b.全部引用reference的賦值操做
c.java.concurrent.Atomic.* 包中全部類的一切操做
定義:指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。
在多線程環境下,一個線程對共享變量的操做對其餘線程是不可見的。Java提供了volatile來保證可見性,當一個變量被volatile修飾後,表示着線程本地內存無效,當一個線程修改共享變量後他會當即被更新到主內存中,其餘線程讀取共享變量時,會直接從主內存中讀取。固然,synchronize和Lock均可以保證可見性。synchronized和Lock能保證同一時刻只有一個線程獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存當中。所以能夠保證可見性。
定義:即程序執行的順序按照代碼的前後順序執行。
Java內存模型中的有序性能夠總結爲:若是在本線程內觀察,全部操做都是有序的;若是在一個線程中觀察另外一個線程,全部操做都是無序的。前半句是指「線程內表現爲串行語義」,後半句是指「指令重排序」現象和「工做內存主主內存同步延遲」現象。
在Java內存模型中,爲了效率是容許編譯器和處理器對指令進行重排序,固然重排序不會影響單線程的運行結果,可是對多線程會有影響。Java提供volatile來保證必定的有序性。最著名的例子就是單例模式裏面的DCL(雙重檢查鎖)。另外,能夠經過synchronized和Lock來保證有序性,synchronized和Lock保證每一個時刻是有一個線程執行同步代碼,至關因而讓線程順序執行同步代碼,天然就保證了有序性。
二者區別:
1.首先synchronized是java內置關鍵字,在jvm層面,Lock是個接口;
2.synchronized沒法判斷是否獲取鎖的狀態,Lock能夠判斷是否獲取到鎖;
3.synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程當中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),不然容易形成線程死鎖;
4.用synchronized關鍵字的兩個線程1和線程2,若是當前線程1得到鎖,線程2線程等待。若是線程1阻塞,線程2則會一直等待下去,而Lock鎖就不必定會等待下去,若是嘗試獲取不到鎖,線程能夠不用一直等待就結束了;
5.synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(二者皆可)
6.Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少許的同步問題。
https://www.cnblogs.com/iyyy/p/7993788.html
1.volatile關鍵字解決的是變量在多個線程之間的可見性;而sychronized關鍵字解決的是多個線程之間訪問共享資源的同步性。
2.volatile只能用於修飾變量,而synchronized能夠修飾方法,以及代碼塊。(volatile是線程同步的輕量級實現,因此volatile性能比synchronized要好,而且隨着JDK新版本的發佈,sychronized關鍵字在執行上獲得很大的提高,在開發中使用synchronized關鍵字的比率仍是比較大)
3.多線程訪問volatile不會發生阻塞,而sychronized會出現阻塞。
4.volatile能保證變量在多個線程之間的可見性,但不能保證原子性;而sychronized能夠保證原子性,也能夠間接保證可見性,由於它會將私有內存和公有內存中的數據作同步。
5.線程安全包含原子性和可見性兩個方面。
對於用volatile修飾的變量,JVM虛擬機只是保證從主內存加載到線程工做內存的值是最新的。
一句話說明volatile的做用:實現變量在多個線程之間的可見性。
1)Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
2)synchronized在發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生;而Lock在發生異常時,若是沒有主動經過unLock()去釋放鎖,則極可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖;
3)Lock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷;
4)經過Lock能夠知道有沒有成功獲取鎖,而synchronized卻沒法辦到。
5)Lock能夠提升多個線程進行讀操做的效率(讀寫鎖)。
在性能上來講,若是競爭資源不激烈,二者的性能是差很少的,而當競爭資源很是激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優於synchronized。因此說,在具體使用時要根據適當狀況選擇。
多線程以及多進程改善了系統資源的利用率並提升了系統 的處理能力。然而,併發執行也帶來了新的問題——死鎖。
死鎖是指兩個或兩個以上的進程(線程)在運行過程當中因爭奪資源而形成的一種僵局(Deadly-Embrace) ) ,若無外力做用,這些進程(線程)都將沒法向前推動。
關於死鎖的一些結論:
參與死鎖的進程數至少爲兩個
參與死鎖的全部進程均等待資源
參與死鎖的進程至少有兩個已經佔有資源
死鎖進程是系統中當前進程集合的一個子集
死鎖會浪費大量系統資源,甚至致使系統崩潰。
飢餓(Starvation)指一個進程一直得不到資源。
死鎖和飢餓都是因爲進程競爭資源而引發的。飢餓通常不佔有資源,死鎖進程必定佔有資源。
競爭不可搶佔資源引發死鎖
競爭可消耗資源引發死鎖
進程推動順序不當引發死鎖
互斥條件:
進程要求對所分配的資源(如打印機)進行排他性控制,即在一段時間內某資源僅爲一個進程所佔有。此時如有其餘進程請求該資源,則請求進程只能等待。
不可剝奪條件:
進程所得到的資源在未使用完畢以前,不能被其餘進程強行奪走,即只能由得到該資源的進程本身來釋放(只能是主動釋放)。
請求與保持條件:
進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其餘進程佔有,此時請求進程被阻塞,但對本身已得到的資源保持不放。
循環等待條件:
存在一種進程資源的循環等待鏈,鏈中每個進程已得到的資源同時被 鏈中下一個進程所請求。即存在一個處於等待狀態的進程集合{Pl, P2, …, pn},其中Pi等 待的資源被P(i+1)佔有(i=0, 1, …, n-1),Pn等待的資源被P0佔有
預防死鎖:經過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或幾個條件,來防止死鎖的發生。
避免死鎖:在資源的動態分配過程當中,用某種方法去防止系統進入不安全狀態,從而避免死鎖的發生。
檢測死鎖:容許系統在運行過程當中發生死鎖,但可設置檢測機構及時檢測死鎖的發生,並採起適當措施加以清除。
解除死鎖:當檢測出死鎖後,便採起適當措施將進程從死鎖狀態中解脫出來。
1.可重入鎖
若是鎖具有可重入性,則稱做爲可重入鎖。像synchronized和ReentrantLock都是可重入鎖,可重入性在我看來實際上代表了鎖的分配機制:基於線程的分配,而不是基於方法調用的分配。舉個簡單的例子,當一個線程執行到某個synchronized方法時,好比說method1,而在method1中會調用另一個synchronized方法method2,此時線程沒必要從新去申請鎖,而是能夠直接執行方法method2。
class MyClass { public synchronized void method1() { method2(); } public synchronized void method2() { } }
上述代碼中的兩個方法method1和method2都用synchronized修飾了,假如某一時刻,線程A執行到了method1,此時線程A獲取了這個對象的鎖,而因爲method2也是synchronized方法,假如synchronized不具有可重入性,此時線程A須要從新申請鎖。可是這就會形成一個問題,由於線程A已經持有了該對象的鎖,而又在申請獲取該對象的鎖,這樣就會線程A一直等待永遠不會獲取到的鎖。
而因爲synchronized和Lock都具有可重入性,因此不會發生上述現象。
2.可中斷鎖
可中斷鎖:顧名思義,就是能夠相應中斷的鎖。在Java中,synchronized就不是可中斷鎖,而Lock是可中斷鎖。
若是某一線程A正在執行鎖中的代碼,另外一線程B正在等待獲取該鎖,可能因爲等待時間過長,線程B不想等待了,想先處理其餘事情,咱們可讓它中斷本身或者在別的線程中中斷它,這種就是可中斷鎖。lockInterruptibly()方法。
3.公平鎖
公平鎖即儘可能以請求鎖的順序來獲取鎖。好比同是有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最早請求的線程)會得到該所,這種就是公平鎖。
非公平鎖即沒法保證鎖的獲取是按照請求鎖的順序進行的。這樣就可能致使某個或者一些線程永遠獲取不到鎖。
在Java中,synchronized就是非公平鎖,它沒法保證等待的線程獲取鎖的順序。
而對於ReentrantLock和ReentrantReadWriteLock,它默認狀況下是非公平鎖,可是能夠設置爲公平鎖。
咱們能夠在建立ReentrantLock對象時,經過如下方式來設置鎖的公平性:
ReentrantLock lock = new ReentrantLock(true);
若是參數爲true表示爲公平鎖,爲fasle爲非公平鎖。默認狀況下,若是使用無參構造器,則是非公平鎖。
另外在ReentrantLock類中定義了不少方法,好比:
1 isFair() //判斷鎖是不是公平鎖 2 isLocked() //判斷鎖是否被任何線程獲取了 3 isHeldByCurrentThread() //判斷鎖是否被當前線程獲取了 4 hasQueuedThreads() //判斷是否有線程在等待該鎖
讀操做不會發生衝突。
ReadWriteLock就是讀寫鎖,它是一個接口,ReentrantReadWriteLock實現了這個接口。能夠經過readLock()獲取讀鎖,經過writeLock()獲取寫鎖。
CAS 適合簡單對象的操做,好比布爾值、整型值等;
CAS 適合衝突較少的狀況,若是太多線程在同時自旋,那麼長時間循環會致使 CPU 開銷很大;
// 仍挑選以庫存數做爲樂觀鎖 //step1: 查詢出商品信息 select (inventory) from items where id=100; //step2: 根據商品信息生成訂單 insert into orders(id,item_id) values(null,100); //step3: 修改商品的庫存 update items set inventory=inventory-1 where id=100 and inventory-1>0;
fail-fast 機制,即快速失敗機制,是java集合(Collection)中的一種錯誤檢測機制。當在迭代集合的過程當中該集合在結構上發生改變的時候,就有可能會發生fail-fast,即拋出ConcurrentModificationException異常。fail-fast機制並不保證在不一樣步的修改下必定會拋出異常,它只是盡最大努力去拋出,因此這種機制通常僅用於檢測bug。在調用 next() 和 remove()時,都會執行 checkForComodification()。若 modCount 不等於 expectedModCount,
則拋出ConcurrentModificationException異常,產生fail-fast事件。 因此,若是想在循環語句中刪除集合中的某個元素,就要用迭代器iterator的remove()方法,由於它的remove()方法不只會刪除元素,還會維護一個標誌,用來記錄目前是否是可刪除狀態,例如,你不能連續兩次調用它的remove()方法,調用以前至少有一次next()方法的調用。 採用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先複製原有集合內容,在拷貝的集合上進行遍歷。java.util包下的集合類都是快速失敗的,不能在多線程下發生併發修改(迭代過程當中被修改),迭代器的快速失敗行爲應該僅用於檢測程序錯誤。java.util.concurrent包下的容器都是安全失敗,能夠在多線程下併發使用,併發修改。CopyOnWriteArrayList中寫操做須要大面積複製數組,因此性能確定不好,可是讀操做由於操做的對象和寫操做不是同一個對象,讀之間也不須要加鎖,讀和寫之間的同步處理只是在寫完後經過一個簡單的「=」將引用指向新的數組對象上來,這個幾乎不須要時間,這樣讀操做就很快很安全,適合在多線程裏使用,絕對不會發生ConcurrentModificationException,因此最後得出結論:CopyOnWriteArrayList適合使用在讀操做遠遠大於寫操做的場景裏,好比緩存。
併發:不一樣的代碼塊交替執行
並行:不一樣的代碼塊同時執行
併發就是指代碼邏輯上能夠並行,有並行的潛力,可是不必定當前是真的以物理並行的方式運行。併發指的是代碼的性質,並行指的是物理運行狀態。
新建(NEW):新建立了一個線程對象。
可運行(RUNNABLE):線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權 。
運行(RUNNING):可運行狀態(runnable)的線程得到了cpu 時間片(timeslice) ,執行程序代碼。
阻塞(BLOCKED):阻塞狀態是指線程由於某種緣由放棄了cpu 使用權,也即讓出了cpu timeslice,暫時中止運行。直到線程進入可運行(runnable)狀態,纔有機會再次得到cpu timeslice 轉到運行(running)狀態。阻塞的狀況分三種:
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。
(三). 其餘阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入可運行(runnable)狀態。
死亡(DEAD):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。
//繼承Thread class MyThread extends Thread{ //重寫run方法 @Override public void run() { //任務內容.... System.out.println("當前線程是:"+Thread.currentThread().getName()); } } Thread thread = new MyThread(); //線程啓動 thread.start();
好處:避免單繼承
//實現Runnable接口 class MyTask implements Runnable{ //重寫run方法 public void run() { //任務內容.... System.out.println("當前線程是:"+Thread.currentThread().getName()); } }
好處:減小處理器單元的閒置時間,增長吞吐量。
request 對象是 javax.servlet.httpServletRequest類型的對象。 該對象表明了客戶端的請求信息,主要用於接受經過HTTP協議傳送到服務器的數據。(包括頭信息、系統信息、請求方式以及請求參數等)。request對象的做用域爲一次請求。
response 表明的是對客戶端的響應,主要是將JSP容器處理過的對象傳回到客戶端。response對象也具備做用域,它只在JSP頁面內有效。
session 對象是由服務器自動建立的與用戶請求相關的對象。服務器爲每一個用戶都生成一個session對象,用於保存該用戶的信息,跟蹤用戶的操做狀態。session對象內部使用Map類來保存數據,所以保存數據的格式爲 「Key/value」。 session對象的value能夠使複雜的對象類型,而不只僅侷限於字符串類型。
application 對象可將信息保存在服務器中,直到服務器關閉,不然application對象中保存的信息會在整個應用中都有效。與session對象相比,application對象生命週期更長,相似於系統的「全局變量」。
out 對象用於在Web瀏覽器內輸出信息,而且管理應用服務器上的輸出緩衝區。在使用 out 對象輸出數據時,能夠對數據緩衝區進行操做,及時清除緩衝區中的殘餘數據,爲其餘的輸出讓出緩衝空間。待數據輸出完畢後,要及時關閉輸出流。
pageContext 對象的做用是取得任何範圍的參數,經過它能夠獲取 JSP頁面的out、request、reponse、session、application 等對象。pageContext對象的建立和初始化都是由容器來完成的,在JSP頁面中能夠直接使用 pageContext對象。
config 對象的主要做用是取得服務器的配置信息。經過 pageConext對象的 getServletConfig() 方法能夠獲取一個config對象。當一個Servlet 初始化時,容器把某些信息經過 config對象傳遞給這個 Servlet。 開發者能夠在web.xml 文件中爲應用程序環境中的Servlet程序和JSP頁面提供初始化參數。
page 對象表明JSP自己,只有在JSP頁面內纔是合法的。 page隱含對象本質上包含當前 Servlet接口引用的變量,相似於Java編程中的 this 指針。
exception 對象的做用是顯示異常信息,只有在包含 isErrorPage="true" 的頁面中才能夠被使用,在通常的JSP頁面中使用該對象將沒法編譯JSP文件。excepation對象和Java的全部對象同樣,都具備系統提供的繼承結構。exception 對象幾乎定義了全部異常狀況。在Java程序中,能夠使用try/catch關鍵字來處理異常狀況; 若是在JSP頁面中出現沒有捕獲到的異常,就會生成 exception 對象,並把 exception 對象傳送到在page指令中設定的錯誤頁面中,而後在錯誤頁面中處理相應的 exception 對象。
# Get和post
GET在瀏覽器回退時是無害的,而POST會再次提交請求。
GET產生的URL地址能夠被Bookmark,而POST不能夠。
GET請求會被瀏覽器主動cache,而POST不會,除非手動設置。
GET請求只能進行url編碼,而POST支持多種編碼方式。
GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留。
GET請求在URL中傳送的參數是有長度限制的,而POST麼有。
對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。
GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息。
GET參數經過傳遞,POST放在Request body中。
世間萬物均可以當作一個對象。每一個物體包括動態的行爲和靜態的屬性,這些就構成了 一個對象。
類是對象的抽象,對象是類的具體,類是對象的模板,對象是類的實例
顯示轉換就是類型強轉,把一個大類型的數據強制賦值給小類型的數據;隱式轉換就是大範圍的變量可以接受小範圍的數據;隱式轉換和顯式轉換其實就是自動類型轉換和強制類型轉換。
Java.lang
Java.io
Java.sql
Java.util
Java.awt
Java.net
Java.math
Equals
Hashcode
toString
wait
notify
clone
getClass
理論上說,java都是引用傳遞,對於基本數據類型,傳遞是值的副本,而不是值自己。對於對象類型,傳遞是對象的引用,當在一個方法操做操做參數的時候,其實操做的是引用所指向的對象。
Pow():冪運算
Sqrt():平方根
Round():四捨五入
Abs():求絕對值
Random():生成一個0-1的隨機數,包括0不包括1
接口中聲明全是public static final修飾的常量
接口中全部方法都是抽象方法
接口是沒有構造方法的
接口也不能直接實例化
接口能夠多繼承
抽象類有構造方法,接口沒有構造方法
抽象類只能單繼承,接口能夠多繼承
抽象類能夠有普通方法,接口中的全部方法都是抽象方法
接口的屬性都是public static final修飾的,而抽象的不是
繼承一個異常類,一般是RumtimeException或者Exception
Error和Exception都是java錯誤處理機制的一部分,都繼承了Throwable類。
Exception表示的異常,異常能夠經過程序來捕捉,或者優化程序來避免。
Error表示的是系統錯誤,不能經過程序來進行錯誤處理。
性質不一樣
final爲關鍵字;
finalize()爲方法;
finally爲區塊標誌,用於try語句中;
做用
final爲用於標識常量的關鍵字,final標識的關鍵字存儲在常量池中(在這裏final常量的具體用法將在下面進行介紹);
finalize()方法在Object中進行了定義,用於在對象「消失」時,由JVM進行調用用於對對象進行垃圾回收,相似於C++中的析構函數;用戶自定義時,用於釋放對象佔用的資源(好比進行I/0操做);
finally{}用於標識代碼塊,與try{}進行配合,不論try中的代碼執行完或沒有執行完(這裏指有異常),該代碼塊之中的程序一定會進行;
抽象類:
抽象方法,只有行爲的概念,沒有具體的行爲實現。使用abstract關鍵字修飾,沒有方法體。子類必須重寫這些抽象方法。
包含抽象方法的類,必定是抽象類。
抽象類只能被繼承,一個類只能繼承一個抽象類。
接口:
所有的方法都是抽象方法,屬型都是常量
不能實例化,能夠定義變量。
接口變量能夠引用具體實現類的實例
接口只能被實現,一個具體類實現接口,必須實現所有的抽象方法
接口之間能夠多實現
一個具體類能夠實現多個接口,實現多繼承現象
&是位運算符。&&是布爾邏輯運算符,在進行邏輯判斷時用&處理的前面爲false後面的內容仍需處理,用&&處理的前面爲false再也不處理後面的內容。
· JDK:Java Development Kit 的簡稱,Java 開發工具包,提供了 Java 的開發環境和運行環境。
· JRE:Java Runtime Environment 的簡稱,Java 運行環境,爲 Java 的運行提供了所需環境。
具體來講 JDK 其實包含了 JRE,同時還包含了編譯 Java 源碼的編譯器 Javac,還包含了不少 Java 程序調試和分析的工具。簡單來講:若是你須要運行 Java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 Java 程序,須要安裝 JDK。
== 解讀
對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:
· 基本類型:比較的是值是否相同;
· 引用類型:比較的是引用是否相同;
代碼示例:
1. String x = "string"; 2. String y = "string"; 3. String z = new String("string"); 4. System.out.println(x==y); // true 5. System.out.println(x==z); // false 6. System.out.println(x.equals(y)); // true 7. System.out.println(x.equals(z)); // true
代碼解讀:由於 x 和 y 指向的是同一個引用,因此 == 也是 true,而 new String()方法則重寫開闢了內存空間,因此 == 結果爲 false,而 equals 比較的一直是值,因此結果都爲 true。
equals 解讀
equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。
首先來看默認狀況下 equals 比較一個有相同值的對象,代碼以下:
1. class Cat { 2. public Cat(String name) { 3. this.name = name; 4. } 5. 6. private String name; 7. 8. public String getName() { 9. return name; 10. } 11. 12. public void setName(String name) { 13. this.name = name; 14. } 15.} 16. 17.Cat c1 = new Cat("王磊"); 18.Cat c2 = new Cat("王磊"); 19.System.out.println(c1.equals(c2)); // false
輸出結果出乎咱們的意料,居然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼以下:
1. public boolean equals(Object obj) { 2. return (this == obj); 3. }
原來 equals 本質上就是 ==。
那問題來了,兩個相同值的 String 對象,爲何返回的是 true?代碼以下:
1. String s1 = new String("老王"); 2. String s2 = new String("老王"); 3. System.out.println(s1.equals(s2)); // true
一樣的,當咱們進入 String 的 equals 方法,找到了答案,代碼以下:
1. public boolean equals(Object anObject) { 2. if (this == anObject) { 3. return true; 4. } 5. if (anObject instanceof String) { 6. String anotherString = (String)anObject; 7. int n = value.length; 8. if (n == anotherString.value.length) { 9. char v1[] = value; 10. char v2[] = anotherString.value; 11. int i = 0; 12. while (n-- != 0) { 13. if (v1[i] != v2[i]) 14. return false; 15. i++; 16. } 17. return true; 18. } 19. } 20. return false; 21.}
原來是 String 重寫了 Object 的 equals 方法,把引用比較改爲了值比較。
總結 :== 對於基本類型來講是值比較,對於引用類型來講是比較的是引用;而 equals 默認狀況下是引用比較,只是不少類從新了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。
不對,兩個對象的 hashCode() 相同,equals() 不必定 true。
代碼示例:
1. String str1 = "通話"; 2. String str2 = "重地"; 3. System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode())); 4. System. out. println(str1. equals(str2));
執行的結果:
1. str1:1179395 | str2:1179395 2. 3. false
代碼解讀:很顯然「通話」和「重地」的 hashCode() 相同,然而 equals() 則爲 false,由於在散列表中,hashCode() 相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等。
· final 修飾的類叫最終類,該類不能被繼承。
· final 修飾的方法不能被重寫。
· final 修飾的變量叫常量,常量必須初始化,初始化以後值就不能被修改。
等於 -1,由於在數軸上取值時,中間值(0.5)向右取整,因此正 0.5 是往上取整,負 0.5 是直接捨棄。
String 不屬於基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於對象。
操做字符串的類有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的區別在於 String 聲明的是不可變的對象,每次操做都會生成新的 String 對象,而後將指針指向新的 String 對象,而 StringBuffer、StringBuilder 能夠在原有對象的基礎上進行操做,因此在常常改變字符串內容的狀況下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的區別在於,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,因此在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。
\8. String str="i"與 String str=new String("i")同樣嗎?
不同,由於內存的分配方式不同。String str="i"的方式,Java 虛擬機會將其分配到常量池中;而 String str=new String("i") 則會被分到堆內存中。
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代碼:
1. // StringBuffer reverse 2. StringBuffer stringBuffer = new StringBuffer(); 3. stringBuffer. append("abcdefg"); 4. System. out. println(stringBuffer. reverse()); // gfedcba 5. // StringBuilder reverse 6. StringBuilder stringBuilder = new StringBuilder(); 7. stringBuilder. append("abcdefg"); 8. System. out. println(stringBuilder. reverse()); // gfedcba
· indexOf():返回指定字符的索引。
· charAt():返回指定索引處的字符。
· replace():字符串替換。
· trim():去除字符串兩端空白。
· split():分割字符串,返回一個分割後的字符串數組。
· getBytes():返回字符串的 byte 類型數組。
· length():返回字符串長度。
· toLowerCase():將字符串轉成小寫字母。
· toUpperCase():將字符串轉成大寫字符。
· substring():截取字符串。
· equals():字符串比較。
不須要,抽象類不必定非要有抽象方法。
示例代碼:
1. abstract class Cat { 2. public static void sayHi() { 3. System. out. println("hi~"); 4. } 5. }
上面代碼,抽象類並無抽象方法但徹底能夠正常運行。
· 普通類不能包含抽象方法,抽象類能夠包含抽象方法。
· 抽象類不能直接實例化,普通類能夠直接實例化。
不能,定義抽象類就是讓其餘類繼承的,若是定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,因此 final 不能修飾抽象類,以下圖所示,編輯器也會提示錯誤信息:
· 實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。
· 構造函數:抽象類能夠有構造函數;接口不能有。
· 實現數量:類能夠實現不少個接口;可是隻能繼承一個抽象類。
· 訪問修飾符:接口中的方法默認使用 public 修飾;抽象類中的方法能夠是任意訪問修飾符。
按功能來分:輸入流(input)、輸出流(output)。
按類型來分:字節流和字符流。
字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。
· BIO:Block IO 同步阻塞式 IO,就是咱們日常使用的傳統 IO,它的特色是模式簡單使用方便,併發處理能力低。
· NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端經過 Channel(通道)通信,實現了多路複用。
· AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操做基於事件和回調機制。
· Files. exists():檢測文件路徑是否存在。
· Files. createFile():建立文件。
· Files. createDirectory():建立文件夾。
· Files. delete():刪除一個文件或目錄。
· Files. copy():複製文件。
· Files. move():移動文件。
· Files. size():查看文件個數。
· Files. read():讀取文件。
· Files. write():寫入文件。
· Collection 是一個集合接口,它提供了對集合對象進行基本操做的通用接口方法,全部集合都是它的子類,好比 List、Set 等。
· Collections 是一個包裝類,包含了不少靜態方法,不能被實例化,就像一個工具類,好比提供的排序方法: Collections. sort(list)。
List、Set、Map 的區別主要體如今兩個方面:元素是否有序、是否容許元素重複。
· 存儲:HashMap 容許 key 和 value 爲 null,而 Hashtable 不容許。
· 線程安全:Hashtable 是線程安全的,而 HashMap 是非線程安全的。
· 推薦使用:在 Hashtable 的類註釋能夠看到,Hashtable 是保留類不建議使用,推薦在單線程環境下使用 HashMap 替代,若是須要多線程使用則用 ConcurrentHashMap 替代。
對於在 Map 中插入、刪除、定位一個元素這類操做,HashMap 是最好的選擇,由於相對而言 HashMap 的插入會更快,但若是你要對一個 key 集合進行有序的遍歷,那 TreeMap 是更好的選擇。
HashMap 基於 Hash 算法實現的,咱們經過 put(key,value)存儲,get(key)來獲取。當傳入 key 時,HashMap 會根據 key. hashCode() 計算出 hash 值,根據 hash 值將 value 保存在 bucket 裏。當計算出的 hash 值相同時,咱們稱之爲 hash 衝突,HashMap 的作法是用鏈表和紅黑樹存儲相同 hash 值的 value。當 hash 衝突的個數比較少時,使用鏈表不然使用紅黑樹。
HashSet 是基於 HashMap 實現的,HashSet 底層使用 HashMap 來保存全部元素,所以 HashSet 的實現比較簡單,相關 HashSet 的操做,基本上都是直接調用底層 HashMap 的相關方法來完成,HashSet 不容許重複的值。
· 數據結構實現:ArrayList 是動態數組的數據結構實現,而 LinkedList 是雙向鏈表的數據結構實現。
· 隨機訪問效率:ArrayList 比 LinkedList 在隨機訪問的時候效率要高,由於 LinkedList 是線性的數據存儲方式,因此須要移動指針從前日後依次查找。
· 增長和刪除效率:在非首尾的增長和刪除操做,LinkedList 要比 ArrayList 效率要高,由於 ArrayList 增刪操做要影響數組內的其餘數據的下標。
綜合來講,在須要頻繁讀取集合中的元素時,更推薦使用 ArrayList,而在插入和刪除操做較多時,更推薦使用 LinkedList。
· 數組轉 List:使用 Arrays. asList(array) 進行轉換。
· List 轉數組:使用 List 自帶的 toArray() 方法。
代碼示例:
1. // list to array 2. List<String> list = new ArrayList<String>(); 3. list. add("王磊"); 4. list. add("的博客"); 5. list. toArray(); 6. // array to list 7. String[] array = new String[]{"王磊","的博客"}; 8. Arrays. asList(array);
· 線程安全:Vector 使用了 Synchronized 來實現線程同步,是線程安全的,而 ArrayList 是非線程安全的。
· 性能:ArrayList 在性能方面要優於 Vector。
· 擴容:ArrayList 和 Vector 都會根據實際的須要動態的調整容量,只不過在 Vector 擴容每次會增長 1 倍,而 ArrayList 只會增長 50%。
· Array 能夠存儲基本數據類型和對象,ArrayList 只能存儲對象。
· Array 是指定固定大小的,而 ArrayList 大小是自動擴展的。
· Array 內置方法沒有 ArrayList 多,好比 addAll、removeAll、iteration 等方法只有 ArrayList 有。
· 相同點:都是返回第一個元素,並在隊列中刪除返回的對象。
· 不一樣點:若是沒有元素 poll()會返回 null,而 remove()會直接拋出 NoSuchElementException 異常。
代碼示例:
1. Queue<String> queue = new LinkedList<String>(); 2. queue. offer("string"); // add 3. System. out. println(queue. poll()); 4. System. out. println(queue. remove()); 5. System. out. println(queue. size());
Vector、Hashtable、Stack 都是線程安全的,而像 HashMap 則是非線程安全的,不過在 JDK 1.5 以後隨着 Java. util. concurrent 併發包的出現,它們也有了本身對應的線程安全類,好比 HashMap 對應的線程安全類就是 ConcurrentHashMap。
能夠使用 Collections. unmodifiableCollection(Collection c) 方法來建立一個只讀集合,這樣改變集合的任何操做都會拋出 Java. lang. UnsupportedOperationException 異常。
示例代碼以下:
1. List<String> list = new ArrayList<>(); 2. list. add("x"); 3. Collection<String> clist = Collections. unmodifiableCollection(list); 4. clist. add("y"); // 運行時此行報錯 5. System. out. println(list. size());
守護線程是運行在後臺的一種特殊進程。它獨立於控制終端而且週期性地執行某種任務或等待處理某些發生的事件。在 Java 中垃圾回收線程就是特殊的守護線程。
runnable 沒有返回值,callable 能夠拿到有返回值,callable 能夠看做是 runnable 的補充。
線程的狀態:
· NEW 還沒有啓動
· RUNNABLE 正在執行中
· BLOCKED 阻塞的(被同步鎖或者IO鎖阻塞)
· WAITING 永久等待狀態
· TIMED_WAITING 等待指定的時間從新被喚醒的狀態
· TERMINATED 執行完成
· 類的不一樣:sleep() 來自 Thread,wait() 來自 Object。
· 釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。
· 用法不一樣:sleep() 時間到會自動恢復;wait() 能夠使用 notify()/notifyAll()直接喚醒。
notifyAll()會喚醒全部的線程,notify()以後喚醒一個線程。notifyAll() 調用後,會將所有線程由等待池移到鎖池,而後參與鎖的競爭,競爭成功則繼續執行,若是不成功則留在鎖池等待鎖被釋放後再次參與競爭。而 notify()只會喚醒一個線程,具體喚醒哪個線程由虛擬機控制。
start() 方法用於啓動線程,run() 方法用於執行線程的運行時代碼。run() 能夠重複調用,而 start() 只能調用一次。
線程池建立有七種方式,最核心的是最後一種:
· newSingleThreadExecutor():它的特色在於工做線程數目被限制爲 1,操做一個無界的工做隊列,因此它保證了全部任務的都是被順序執行,最多會有一個任務處於活動狀態,而且不容許使用者改動線程池實例,所以能夠避免其改變線程數目;
· newCachedThreadPool():它是一種用來處理大量短期工做任務的線程池,具備幾個鮮明特色:它會試圖緩存線程並重用,當無緩存線程可用時,就會建立新的工做線程;若是線程閒置的時間超過 60 秒,則被終止並移出緩存;長時間閒置時,這種線程池,不會消耗什麼資源。其內部使用 SynchronousQueue 做爲工做隊列;
· newFixedThreadPool(int nThreads):重用指定數目(nThreads)的線程,其背後使用的是無界的工做隊列,任什麼時候候最多有 nThreads 個工做線程是活動的。這意味着,若是任務數量超過了活動隊列數目,將在工做隊列中等待空閒線程出現;若是有工做線程退出,將會有新的工做線程被建立,以補足指定的數目 nThreads;
· newSingleThreadScheduledExecutor():建立單線程池,返回 ScheduledExecutorService,能夠進行定時或週期性的工做調度;
· newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()相似,建立的是個 ScheduledExecutorService,能夠進行定時或週期性的工做調度,區別在於單一工做線程仍是多個工做線程;
· newWorkStealingPool(int parallelism):這是一個常常被人忽略的線程池,Java 8 才加入這個建立方法,其內部會構建ForkJoinPool,利用Work-Stealing算法,並行地處理任務,不保證處理順序;
· ThreadPoolExecutor():是最原始的線程池建立,上面1-3建立方式都是對ThreadPoolExecutor的封裝。
· RUNNING:這是最正常的狀態,接受新的任務,處理等待隊列中的任務。
· SHUTDOWN:不接受新的任務提交,可是會繼續處理等待隊列中的任務。
· STOP:不接受新的任務提交,再也不處理等待隊列中的任務,中斷正在執行任務的線程。
· TIDYING:全部的任務都銷燬了,workCount 爲 0,線程池的狀態在轉換爲 TIDYING 狀態時,會執行鉤子方法 terminated()。
· TERMINATED:terminated()方法結束後,線程池的狀態就會變成這個。
· execute():只能執行 Runnable 類型的任務。
· submit():能夠執行 Runnable 和 Callable 類型的任務。
Callable 類型的任務能夠獲取執行的返回值,而 Runnable 執行無返回值。
· 方法一:使用安全類,好比 Java. util. concurrent 下的類。
· 方法二:使用自動鎖 synchronized。
· 方法三:使用手動鎖 Lock。
synchronized 鎖升級原理:在鎖對象的對象頭裏面有一個 threadid 字段,在第一次訪問的時候 threadid 爲空,jvm 讓其持有偏向鎖,並將 threadid 設置爲其線程 id,再次進入的時候會先判斷 threadid 是否與其線程 id 一致,若是一致則能夠直接使用此對象,若是不一致,則升級偏向鎖爲輕量級鎖,經過自旋循環必定次數來獲取鎖,執行必定次數以後,若是尚未正常獲取到要使用的對象,此時就會把鎖從輕量級升級爲重量級鎖,此過程就構成了 synchronized 鎖的升級。
鎖的升級的目的:鎖升級是爲了減低了鎖帶來的性能消耗。在 Java 6 以後優化 synchronized 的實現方式,使用了偏向鎖升級爲輕量級鎖再升級到重量級鎖的方式,從而減低了鎖帶來的性能消耗。
當線程 A 持有獨佔鎖a,並嘗試去獲取獨佔鎖 b 的同時,線程 B 持有獨佔鎖 b,並嘗試獲取獨佔鎖 a 的狀況下,就會發生 AB 兩個線程因爲互相持有對方須要的鎖,而發生的阻塞現象,咱們稱爲死鎖。
· 儘可能使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),設置超時時間,超時能夠退出防止死鎖。
· 儘可能使用 Java. util. concurrent 併發類代替本身手寫鎖。
· 儘可能下降鎖的使用粒度,儘可能不要幾個功能用同一把鎖。
· 儘可能減小同步的代碼塊。
synchronized 是由一對 monitorenter/monitorexit 指令實現的,monitor 對象是同步的基本實現單元。在 Java 6 以前,monitor 的實現徹底是依靠操做系統內部的互斥鎖,由於須要進行用戶態到內核態的切換,因此同步操做是一個無差異的重量級操做,性能也很低。但在 Java 6 的時候,Java 虛擬機 對此進行了大刀闊斧地改進,提供了三種不一樣的 monitor 實現,也就是常說的三種不一樣的鎖:偏向鎖(Biased Locking)、輕量級鎖和重量級鎖,大大改進了其性能。
· volatile 是變量修飾符;synchronized 是修飾類、方法、代碼段。
· volatile 僅能實現變量的修改可見性,不能保證原子性;而 synchronized 則能夠保證變量的修改可見性和原子性。
· volatile 不會形成線程的阻塞;synchronized 可能會形成線程的阻塞。
反射是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲 Java 語言的反射機制。
Java 序列化是爲了保存各類對象在內存中的狀態,而且能夠把保存的對象狀態再讀出來。
如下狀況須要使用 Java 序列化:
· 想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
· 想用套接字在網絡上傳送對象的時候;
· 想經過RMI(遠程方法調用)傳輸對象的時候。
動態代理是運行時動態生成代理類。
動態代理的應用有 spring aop、hibernate 數據查詢、測試框架的後端 mock、rpc,Java註解對象獲取等。
JDK 原生動態代理和 cglib 動態代理。JDK 原生動態代理是基於接口實現的,而 cglib 是基於繼承當前類的子類實現的。
克隆的對象可能包含一些已經修改過的屬性,而 new 出來的對象的屬性都仍是初始化時候的值,因此當須要一個新的對象來保存當前對象的「狀態」就靠克隆方法了。
· 實現 Cloneable 接口並重寫 Object 類中的 clone() 方法。
· 實現 Serializable 接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆。
· 淺克隆:當對象被複制時只複製它自己和其中包含的值類型的成員變量,而引用類型的成員對象並無複製。
· 深克隆:除了對象自己被複制外,對象所包含的全部成員變量也將複製。
JSP 是 servlet 技術的擴展,本質上就是 servlet 的簡易方式。servlet 和 JSP 最主要的不一樣點在於,servlet 的應用邏輯是在 Java 文件中,而且徹底從表示層中的 html 裏分離開來,而 JSP 的狀況是 Java 和 html 能夠組合成一個擴展名爲 JSP 的文件。JSP 側重於視圖,servlet 主要用於控制邏輯。
· page:表明與一個頁面相關的對象和屬性。
· request:表明與客戶端發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個 Web 組件;須要在頁面顯示的臨時數據能夠置於此做用域。
· session:表明與某個用戶與服務器創建的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶本身的 session 中。
· application:表明與整個 Web 應用程序相關的對象和屬性,它實質上是跨越整個 Web 應用程序,包括多個頁面、請求和會話的一個全局做用域。
· 存儲位置不一樣:session 存儲在服務器端;cookie 存儲在瀏覽器端。
· 安全性不一樣:cookie 安全性通常,在瀏覽器存儲,能夠被僞造和修改。
· 容量和個數限制:cookie 有容量限制,每一個站點下的 cookie 也有個數限制。
· 存儲的多樣性:session 能夠存儲在 Redis 中、數據庫中、應用程序中;而 cookie 只能存儲在瀏覽器中。
session 的工做原理是客戶端登陸完成以後,服務器會建立對應的 session,session 建立完以後,會把 session 的 id 發送給客戶端,客戶端再存儲到瀏覽器中。這樣客戶端每次訪問服務器時,都會帶着 sessionid,服務器拿到 sessionid 以後,在內存找到與之對應的 session 這樣就能夠正常工做了。
能夠用,session 只是依賴 cookie 存儲 sessionid,若是 cookie 被禁用了,能夠使用 url 中添加 sessionid 的方式保證 session 能正常使用。
· 使用預處理 PreparedStatement。
· 使用正則表達式過濾掉字符中的特殊字符。
XSS 攻擊:即跨站腳本攻擊,它是 Web 程序中常見的漏洞。原理是攻擊者往 Web 頁面裏插入惡意的腳本代碼(css 代碼、Javascript 代碼等),當用戶瀏覽該頁面時,嵌入其中的腳本代碼會被執行,從而達到惡意攻擊用戶的目的,如盜取用戶 cookie、破壞頁面結構、重定向到其餘網站等。
預防 XSS 的核心是必須對輸入的數據作過濾處理。
CSRF:Cross-Site Request Forgery(中文:跨站請求僞造),能夠理解爲攻擊者盜用了你的身份,以你的名義發送惡意請求,好比:以你名義發送郵件、發消息、購買商品,虛擬貨幣轉帳等。
防護手段:
· 驗證請求來源地址;
· 關鍵操做添加驗證碼;
· 在請求地址添加 token 並驗證。
· throw:是真實拋出一個異常。
· throws:是聲明可能會拋出一個異常。
· final:是修飾符,若是修飾類,此類不能被繼承;若是修飾方法和變量,則表示此方法和此變量不能在被改變,只能使用。
· finally:是 try{} catch{} finally{} 最後一部分,表示不論發生任何狀況都會執行,finally 部分能夠省略,但若是 finally 部分存在,則必定會執行 finally 裏面的代碼。
· finalize: 是 Object 類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法。
try-catch-finally 其中 catch 和 finally 均可以被省略,可是不能同時省略,也就是說有 try 的時候,必須後面跟一個 catch 或者 finally。
· NullPointerException 空指針異常
· ClassNotFoundException 指定類不存在
· NumberFormatException 字符串轉換爲數字異常
· IndexOutOfBoundsException 數組下標越界異常
· ClassCastException 數據類型轉換異常
· FileNotFoundException 文件未找到異常
· NoSuchMethodException 方法不存在異常
· IOException IO 異常
· SocketException Socket 異常