後臺開發的過程當中積累的關於java的雜記html
架構
SSH框架
爲何要分層?
由於分層使代碼變得清晰,容易寫也容易閱讀,更重要的是讓代碼擴展性更好,層與層之間的改動不會互相影響java
各層的分工
- dao——與數據庫交互
- service——處理業務邏輯,調用dao層方法
action——用來控制轉發,接到請求交給service處理linux
dao是用於操做數據用的,service是爲頁面功能服務的,在service中對數據進行處理計算,而後返回數據結果到ACTION,而action則再對數據進一步處理,好比把list轉成json,把兩個service數據進行合併等,併發送到jsp頁面顯示。面試
併發相關
ReentrantLock
參考Java多線程11:ReentrantLock的使用和Condition算法
- lock以後要本身unlock
- lock相比synchronized更加靈活,能夠經過trylock判斷鎖是否是被佔用了,在被佔用的狀況下能夠忙其餘事,而不是直接就阻塞了
- lock持有的是對象監視器,也就是相似於syncronized(this){} ,可是須要注意這二者持有的對象監視器是不一樣的
- lock配置Condition的signal和await能夠實現syncronized的wait和notify來實現等待/通知模型,相比之下Condition更靈活:一個lock實例能夠建立多個Condition實例,實現多路通知和有選擇性的通知,而不是像notify同樣是由jvm隨機選擇的
BlockingQueue
概念
阻塞隊列是一種支持當獲取元素時會阻塞直到隊列不爲空,當插入元素時阻塞直到隊列有空間。數據庫
方法
操做阻塞隊列有四種形式的方法,add/remove/element拋出異常,offer/poll/peek返回具體值,put/take阻塞,offer(e, time, unit)、poll(time, unit)指定等待的最長時間json
不一樣實現
- ArrayBlockingQueue 只有一個鎖,經過兩個condition來實現阻塞、通知,添加和刪除數據時只容許一個被執行
- LinkedBlockingQueue 有兩個鎖,putLock和takeLock,各自維護一個condition,添加和刪除數據能夠容許並行,固然刪除和添加最多各自有一個線程在執行。
- LinkedBlockingQueue 不只在消費數據的時候進行喚醒插入阻塞的線程,並且在插入若是容量還沒滿,也會喚醒插入阻塞的線程
jvm原理
垃圾回收
參考深刻理解java垃圾回收機制
垃圾回收就是java中的一個亮點(有利有弊),經過必定的算法自動管理對象的生命週期,防止內存泄漏(內存對象的生命週期超過了程序須要它的時長)數組
垃圾回收的算法有:緩存
- 引用計數:早期的算法,經過給堆中的每一個對象內置一個引用計數器來實現(缺點是:沒法檢測循環引用)
- 標誌、清洗算法
- 分代收集:頻繁收集新生代,比較少的收集老生代,基本不收集持久代(分代回收的GC分爲 minor gc 和 full/major gc,如下爲兩種gc的日誌格式)
- GC:

- FULL GC:

- 新的對象都在eden區上建立,當eden區的大小達到閾值就會發生GC,eden區中存活的對象會複製到survivor區,並清除eden中無效的對象,若是survivor區中的對象達到年齡限制或者大小達到閾值,就會將存活的對象複製到old區,若是這時old區空間不足就會發生full gc,full gc以後old區的空間仍然沒法承載young區要晉升的對象大小,就會發生OOM
內存分配
參考Java裏的堆(heap)棧(stack)和方法區(method)、深刻探究JVM | 探祕Metaspace安全
內存分爲 heap、stack、method
heap:
- 堆存放的都是對象,空間大可是訪問慢(時空守恆)
- 爲全部線程所共享
- java heap主要分紅三種:
- young:主要用來存放新生的對象
- old: 主要用來存放生命週期長的內存對象
- permanent:主要用來存放類和方法的元數據信息和常量池 ,類被加載後就放入這個區域。GC不會對持久層進行清理,
- metaspace:在java8中持久代已經被移除了,由於持久代的大小是固定的,因此在類加載不少的狀況下,容易出現OOM:PermGen space錯誤,類和方法的元數據被移入元數據區。(存在於本地內存中,因此大小隻受物理內存的影響)元數據區是自動增加的,經過-XX:MaxMetaSpaceSize來限制Metaspace的大小,之前的Perm參數失效,超過最大值將會在metaspace發生full gc收集dead class或者classloader
stack:
- 棧區放的都是基礎數據類型和對象的引用
- 保存函數調用的現場
method:
- 方法區也爲全部線程所共享(另外一種說法也就是permanent區)
- 方法區存放的都是在整個程序中永遠惟一的元素,包括class、static變量
- 常量池也是方法區的一部分,存放程序中的字面量如」hello「 以及常量
java 集合框架
哈希結構
哈希表的數組長度爲何老是習慣用2^n?
hash 的時候老是須要對對象的hashCode取哈希表長度的模,對於2^n 取模,能夠簡化爲 hash & (2^n - 1),提升效率
jdk1.8中的hashmap中的 hash算法是什麼?有什麼優勢?
hash算法是(h = key.hashCode()) ^ (h >>> 16) ,經過這樣hash,高位的變化反映到低位裏,這樣咱們取模的時候hash & (length - 1) 就不會只取低位相關,防止有些hashCode只和高位相關形成的衝突過多
hashtable、hashmap、concurrenthashmap 哈希家族的異同點
- 都是繼承於map接口,用於存儲鍵值對。hashtable是同步的,若是不須要線程安全,推薦使用hashmap代替hashtable,若是須要高併發線程安全的實現,使用ConcurrentHashMap代替hashtable
- 集合方法返回的iterators是「fail-fast」的,也就是說當hash結果被修改,除了經過iterator的remove方法外的改動,都會形成iterator拋出ConcurrentModificationException異常。所以,在併發修改的狀況下,iterator很快失敗並清除,而不是冒險在將來不肯定的時間作不肯定的事。hashtable的方法返回的emurations卻不是fail-fast的
- hashmap能夠接受null值,hashtable則不行
- HashMap能夠經過下面的語句進行同步:Map m = Collections.synchronizeMap(hashMap);
ConcurrentHashMap(面試必考)
concurrentHashMap是一個併發的hash表的實現,它支持徹底的併發讀取,支持大數量的併發更新操做。
高性能的緣由:
- 用分離鎖實現多個線程間的更深層次的共享訪問,再也不是隻有一個線程能同時持有容器的鎖了。
- 利用hashEntry的不變性(final hash,key,next)來下降讀操做對加鎖的需求
- 用volatile變量協調讀寫線程間的內存可見性
缺點
- 返回的迭代器是弱一致性的,fail-safe而且不會拋出ConcurrentModificationException異常
源碼分析(基於jdk1.7)

concurrentHashMap是由segment數組和hashEntry數組組成的,segment是一種可重入鎖ReentranLock,在CHM中扮演鎖的角色,HashEntry用於存儲鍵值對數據。一個CHM中包含一個segment數組,segment的結構和hashmap相似,一個segment中包含一個hashEntry數組,每一個HashEntry都是一個鏈表的結構,每一個segment守護着本身的hashEntry數組,要往這一段hashEntry數組中修改,必須先得到相應的鎖
arrayList、linklist、vector
- arrayList 內部用數組實現,隨機訪問和遍歷快,插入刪除慢
- linklist 內部用鏈表實現,適合數據的動態插入和刪除,隨機訪問和遍歷慢
- vector 跟 arraylist差很少,除了如下幾點
- vector是線程安全的,所以訪問速度也較慢
- arraylist在內存不夠時擴展50% + 1個,vector默認擴展一倍
java 代碼執行順序
JAVA類首次裝入時,會對靜態成員變量或方法進行一次初始化,但方法不被調用是不會執行的,靜態成員變量和靜態初始化塊級別相同,非靜態成員變量和非靜態初始化塊級別相同。
初始化順序:先初始化父類的靜態代碼--->初始化子類的靜態代碼-->(建立實例時,若是不建立實例,則後面的不執行)初始化父類的非靜態代碼(變量定義等)--->初始化父類構造函數--->初始化子類非靜態代碼(變量定義等)--->初始化子類構造函數
tips:
若子類沒有顯示調用父類的構造函數,則默認調用父類的無參構造函數,若是父類沒有則編譯錯誤
java 命令行參數
-classpath
java 經過指定-classpath 來指定虛擬機搜索的你要運行的類的目錄、jar文件名、zip文件名,之間用;(linux 用:)隔開。不然java查不到你的class文件就會報java.lang.NoClassDefFoundError異常,在運行時能夠經過System.getProperty(「java.class.path」)獲得jvm查找類的路徑
也能夠經過CLASSPATH環境變量來指定類搜索路徑,建議用-cp
- -DpropertyName=value
系統屬性,能夠經過System.getProperty(propertyName)獲取value的值,用來設置全局變量值,如配置文件路徑
- -Xms -Xmx 堆的最大最小值
- -Xss 線程堆的最大值
- --XX:+HeapDumpOnOutOfMemoryError
當JVM不斷地拋出OutOfMemory錯誤的時候,該命令會通知JVM拍攝一個「堆轉儲快照」, 並經過-XX:HeapDumpPath 指定該文件的保存路徑,能夠方便調試問題
- -XX:+UseParNewGC 使用多線程併發處理新生代GC
- -XX:+UseConcMarkSweepGC 使用CMS併發處理GC
-Djava.awt.headless=true 無頭模式,系統的配置模式,在該模式下,系統缺乏了顯示、鍵盤或鼠標。聽說
在Java服務器程序須要進行部分圖像處理功能時,建議將程序運行模式設置爲headless,這樣有助於服務器端有效控制程序運行狀態和內存使用(可防止在處理大圖片時發生內存溢出)
泛型
- 上界:
表示對泛型的限制,傳進來的對象必須是class 或者 class 的子類
- 通配符
- java5以後添加了通配符 和 泛型(泛型咱們是瞭解的),通配符的基本用法
GenericType<?>
GenericType<? extends upperBoundType> // 設置上界
GenericType<? super lowerBoundType> // 設置下界
List<? extends Number> nums = new ArrayList<>();
List<Integer> ints = Arrays.asList(1, 2);
List<Double> doubles = Arrays.asList(1.1, 2.2);
nums.addAll(ints);
nums.addAll(dbls); // addAll 方法使用了通配符做爲參數
}
// print all Number 或 Number 子類的list, 若是沒有通配符的話, 估計得一個一個子類都去實現如下。。。
public void print(List<? extends Number> list) {
list.forEach(x -> System.out.println(x));
}
- 通配符的限制:
- 不能用來直接建立變量對象
- 不能進行修改操做,例以下面的代碼,編譯器可能以爲,鬼知道你引用了哪一個子類呢
List<? extends Number> nums = new ArrayList<Integer>();
nums.add(1); // 編譯錯誤
java 基礎概念
- 守護線程: 只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就所有工做。當最後一個守護線程結束時,守護線程隨同JVM一塊兒結束工做。最典型的例子就是GC
- volatile:
- 用在多線程中,同步變量。通常狀況下線程爲了提升效率,會緩存主內存中的變量在本身的線程棧中,volatile聲明的變量則不能緩存,保證了線程之間的變量一致性[雖然本身測不出這個效果。。。]
不能保證線程安全。引用例子以下
假如線程1,線程2 在進行read,load 操做中,發現主內存中count的值都是5,那麼都會加載這個最新的值,在線程1堆count進行修改以後,會write到主內存中,主內存中的count變量就會變爲6;線程2因爲已經進行read,load操做,在進行運算以後,也會更新主內存count的變量值爲6;致使兩個線程及時用volatile關鍵字修改以後,仍是會存在併發的狀況。
- 建立對象:
建立對象有哪些方法?
日誌操做
log4j
log4j 使用寫日誌變得很簡單,支持多個輸出和格式化
log4j 主要由三個部分組成 logger 、appender、 layout
- logger 負責日誌的收集,主要由rootlogger 和 其餘各個類的logger組成,經過logger.info等方法來記錄消息,子logger中能夠自定義各類配置,若是沒有設置就會向上級的logger查找相應的配置,最上層爲rootlogger
- appender負責日誌的輸出,能夠輸出到文件、控制檯、數據庫、kafka等等
- layout綁定到相應的的appender來格式化它的日誌輸出格式,豐滿多姿