本文是對一些java基礎知識的整理,把以前印象筆記裏面的所有慢慢搬到這個blog來html
爲了方便就按照《Thinking In Java》的目錄來編輯。java
這裏面的內容均爲面試題相關,可能的考點等android
這本書裏面有些翻譯不是很好,建議和英文版對照。程序員
抽象、封裝、繼承、多態web
面向對象設計方法主要特徵:繼承、封裝、多態。面試
延伸點:反射會破壞代碼的封裝性(mock等)正則表達式
return | method | 解釋 |
---|---|---|
protected Object | clone() | 建立並返回此對象的一個副本。 |
protected void | finalize() | 當垃圾回收器肯定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。 |
void | notify() | 喚醒在此對象監視器上等待的單個線程。 |
void | notifyAll() | 喚醒在此對象監視器上等待的全部線程。 |
void | wait() | 在其餘線程調用此對象的 notify()方法或 notifyAll()方法前,致使當前線程等待。 |
void | wait(long timeout) | 在其餘線程調用此對象的 notify()方法或 notifyAll()方法,或者超過指定的時間量前,致使當前線程等待。 |
void | wait(long timeout, int nanos) | 在其餘線程調用此對象的 notify()方法或 notifyAll()方法,或者其餘某個線程中斷當前線程,或者已超過某個實際時間量前,致使當前線程等待。 |
方法屬於對象的成員,靜態方法屬於類的成員算法
Q:爲何 wait, notify 和 notifyAll 這些方法不在 thread 類裏面?編程
一個很明顯的緣由是 JAVA 提供的鎖是「對象級」 的而不是「線程級」的,每一個對象都有鎖,經過線程得到。若是線程須要等待某些鎖那麼調用對象中的 wait()方法就有意義了。api
若是 wait()方法定義在 Thread 類中,線程正在等待的是哪一個鎖就不明顯了。
簡單的說,因爲 wait,notify 和 notifyAll 都是鎖級別的操做,因此把他們定義在 Object 類中由於鎖屬於對象。
拓展:
在 Java 中,每一個對象都有兩個池,鎖(monitor)池和等待池
鎖池:假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized 方法(或者 synchronized 塊),因爲這些線程在進入對象的 synchronized 方法以前必須先得到該對象的鎖的擁有權,可是該對象的鎖目前正被線程A擁有,因此這些線程就進入了該對象的鎖池中。
等待池:假設一個線程A調用了某個對象的 wait()方法,線程A就會釋放該對象的鎖(由於wait()方法必須出如今 synchronized 中,這樣天然在執行 wait()方法以前線程A就已經擁有了該對象的鎖),同時線程A就進入到了該對象的等待池中。
若是另外的一個線程調用了相同對象的 notifyAll()方法,那麼處於該對象的等待池中的線程就會所有進入該對象的鎖池中,準備爭奪鎖的擁有權。若是另外的一個線程調用了相同對象的 notify()方法,那麼僅僅有一個處於該對象的等待池中的線程(隨機)會進入該對象的鎖池.
Q:爲何 wait 和 notify 方法要在同步塊中調用?
- 避免 IllegalMonitorStateException
- 避免任何在 wait 和 notify 之間潛在的競態條件
Integer,Float,Double等都繼承自Number類
名稱 | 包裝類型 | 初始化 | 其餘 |
---|---|---|---|
boolean | Boolean | false | 1位 |
byte(1字節) | Byte | (byte)0 | ASCII碼:空格:32;數字0到9:48到57,後面爲大小寫字母; byte 類型用在大型數組中節約空間,主要代替整數,由於 佔用的空間只有 int 類型的四分之一 |
short(2字節) | Short | (short)0 | Short 數據類型也能夠像 byte 那樣節省空間。一個short變量是int型變量所佔空間的二分之一; |
char(2字節) | Character | '\u0000'(null) | 單一的 16 位 Unicode 字符; |
int(4字節) | Integer | 0 | |
float(4字節) | Float | 0.0f | 精確到小數點後6位,在儲存大型浮點數組的時候可節省內存空間; |
long(8字節) | Long | 0L | |
double(8字節) | Double | 0.0d | 精確到小數點後15位(默認的小數類型) |
BigInteger 任意精度的整數,不可變
BigDecimal 任何精度的定點數(商業計算),不可變,一般建議優先使用String構造方法
parseInt()是把String變成int的基礎數據類型;
byte,short,char-> int -> long -> float -> double 不一樣類型運算結果類型向右邊靠齊。
浮點數到整數的轉換是經過捨棄小數獲得,而不是四捨五入
裝箱過程是經過調用包裝器的valueOf方法實現的,而拆箱過程是經過調用包裝器的 xxxValue方法實現的。
大多數狀況下二者相同,java.io.file,java.util.Date,java.lang.string,包裝類(Integer,Double等)等實現了本身的equals,比較規則爲:若是兩個對象的類型一致,而且內容一致,則返回true。
valueOf()
方法的實現比較簡單,就是先判斷值是否在緩存池中,若是在的話就直接返回緩存池的內容。
在 Java 8 中,Integer
緩存池的大小默認爲 -128~127,因此除非用 new Integer()
顯式的建立對象,不然都是同一個對象(-128~127)
比較符號 | 比較對象 | 數據轉型 |
---|---|---|
== | 同一個對象 | 有表達式會觸發 |
equals | 同一個類型和內容 | 不觸發 |
通常會考你「==」和equals的區別,數據轉型,拆箱裝箱
「==」和equals記住,前者相同,後者相等。前者會觸發數據轉型。
new Integer()新建對象
valueof裝箱,xxxValue拆箱
方法 | 功能 | 返回值 |
---|---|---|
ceil() | 向上取整 | double |
floor() | 向下取整 | double |
round() | 原來的數字加上0.5後再向下取整 | int |
三種元素構成:數字+字符+$+下劃線。
不能以數字開頭+不能是關鍵字.
一個文件中能夠有多個類,若是沒有public類,則能夠與任意類名相同,若是有public類則文件名必須與此類名相同,由於一個文件中只能有一個public類。若是文件中只有一個類,則文件名必須與類名相同
名稱 | 功能 |
---|---|
Override | 標識當前的方法,是否覆蓋了它的父類中的方法 |
Deprecated | 已通過期或者即將過時的方法,不推薦咱們使用 |
SuppressWarning | 屏蔽相應的警告 |
運算符 | 結合性 | 其餘 |
---|---|---|
() [] . | 從左到右 | |
! +(正) -(負) ~ ++ -- | 從右向左 | ~n=-n-1 |
* / % | 從左向右 | %取餘操做,只適用於整型 |
+(加) -(減) | 同上 | |
<< >> >>> | 同上 | 按位左(右)移運算符, 按位右移運補零運算符。 |
< <= > >= instanceof | 同上 | |
== != | 同上 | |
& | 同上 | 相對應位都是1,則結果爲1,不然爲0 |
^ | 同上 | 相對應位值相同,則結果爲0,不然爲1 |
或 | 同上 | 相對應位都是0,則結果爲0,不然爲1 |
&& | 同上 | 邏輯與(前面表達式爲false,後面不計算) |
邏輯或 | 同上 | 邏輯或(前面表達式爲true,後面不計算) |
break 中斷並跳出當前循環。直接跳出當前的循環,從當前循環外面開始執行,忽略循環體中任何其餘語句和循環條件測試。他只能跳出一層循環,若是你的循環是嵌套循環,那麼你須要按照你嵌套的層次,逐步使用break來跳出.
return 致使當前的方法退出,並返回那個值。
選擇因子:整數類型(long除外),enum(jdk1.5後),String(jdk1.7後)
字符串的switch是經過equals()和hashCode()方法來實現的。
注意break
類的加載包括:加載,驗證,準備,解析,初始化
構造方法內能夠用this調用另外一個構造方法
同名不一樣參數爲重載,與返回值類型無關;(好比你調用這個方法的時候java怎麼根據返回值來區分你實際調用的方法)與訪問修飾符無關
子類覆寫父類的方法爲重寫
題型:怎麼算重載和重寫,重寫的修飾符範圍
final不能重寫
名稱 | 功能 |
---|---|
包訪問權限 | 不提供任何權限修飾詞狀況下;默認包:處於同一個文件目錄下 |
public | 均可用 |
protected | 修飾方法和成員變量,處理繼承的概念,保證了僅有繼承的類才能訪問這個成員。同時具備包訪問權限。 |
private | 僅在被包含的類裏面可用,類的變量一般聲明爲private |
❤class編寫順序可按照:public👉protected👉private
類:❌(private & protected),⭕️(包訪問權限 or public)
靜態變量會默認賦初值,局部變量和final聲明的變量必須手動賦初值
將對象置於新類中便可
想在新類中使用現有類的功能而非它的接口
構造函數不繼承,只能顯式(super)或者隱式的調用,導出類的構造器會插入對基類構造器的調用
方法(包括私有)均繼承,可是不能直接調用私有方法。
ps:也可使用super訪問父類被子類隱藏的變量或覆蓋的方法。
將一個成員對象置於構造的類中,同時在新類中暴露了該成員的全部方法。
例:代理類中建立原類的對象,代理類的方法中調用此對象的方法。(其實就是套個殼,間接調用原類的方法)
初始化:一、定義的時候初始化
二、構造器初始化
三、惰性初始化(在使用這些對象以前)
四、實例初始化
只有命令行調用的main方法會被調用
確保正確清理:使用finally語句保證清理/執行類的全部特定清理動做,其順序同生成順序相反/不要使用finalize()
組合和繼承(has a & is a ):
組合一般適用於在新類中使用現有類的功能而非接口,即在新類中嵌入一個現有類的private對象。
繼承一般用於,使用某個類,並開發一個它的特殊版本。是否須要重新類向基類進行向上轉型
數據(數值不變)
引用(引用不變可是對象自身能夠改變)
❌數組中的數據沒法被修飾爲不可更改
空白final:被聲明爲final可是沒有給定初始值的域,容許可是必須在域的定義處或者每一個構造器中對final賦值
final 的方法不能被修改(繼承類中不能覆蓋),final類不能繼承(裏面的方法也是final的,數值不受影響)
private final = private修飾的方法,繼承類中能覆蓋(由於方法爲private 的時候,它就不是接口的一部分,導出類中相同名稱的方法並非覆蓋而是從新生成了一個新的方法);
用來修飾成員變量和成員方法,也能夠造成靜態static代碼塊。不能在方法中定義
static變量在第一次使用的時候初始化,只會有一份成員對象。
總結:(靜態變量、靜態初始化塊)>(變量、初始化塊)>構造器
針對非靜態方法是編譯看左,運行看右,可是對於成員變量,都是看左,也就是父類
它一般只是爲了方便輸出,好比System.out.println(xx),括號裏面的「xx」若是不是String類型的話,就自動調用xx的toString()方法
Java中除了static和final方法之外,其餘方法都是後期綁定(動態綁定)實現多態
動態綁定僅僅針對類的方法
一、抽象的方法沒有body
二、含有抽象方法的類就是抽象類
三、不能建立實例
ps:抽象類的繼承類,若是沒有爲全部方法提供方法體,編譯器會強制用abstract來修飾這個類。
JDK 1.8之前,抽象類的方法默認訪問權限爲protected
JDK 1.8時,抽象類的方法默認訪問權限變爲default
只有public、static、final變量和抽象方法
能夠在此以前加public,implements關鍵字,必須實現全部方法
不能建立實例
在jdk1.8以前,interface之中能夠定義常量和方法,常量必須是public、static、final的,方法必須是public、abstract的。因爲這些修飾符都是默認的
JDK8及之後,容許咱們在接口中定義static方法和default方法,有方法體
靜態方法,只能經過接口名調用,不能夠經過實現類的類名或者實現類的對象調用。
default方法,只能經過接口實現類的對象來調用。能夠被覆寫,可是不能再加default修飾符。
JDK 1.9時,接口中的方法能夠是private的
接口的修飾符,有無body,接口和抽象類的區別等
一、不能有static變量和字段
二、尾部有分號
三、傳入的參數是final
四、不含命名構造器,實例初始化的實際效果就是構造器
五、只能實現一個接口,且不能同時繼承和實現接口
局部內部類:在方法體裏面建立
須要不止一個該內部類的時候使用。局部內部類含有構造器,匿名內部類只能用於實例初始化
一、不能有static變量和字段
二、構造器必須鏈接到指向其外圍對象的引用
三、外圍類繼承的時候,內部類並無被繼承。即便基類和導出類的內部類名稱相同也是不一樣且獨立的類。
經常使用:外部類有個方法指向內部類的引用(匿名類把內部類直接放到方法裏面)
內部類對外部類引用:外部類.this(嵌套類沒有)
建立內部類的對象:外部類對象.new 內部類名()
static的內部類
能夠做爲接口的一部分
靜態內部類能夠訪問外圍類的靜態數據,包括私有數據,但不能訪問非靜態數據;
非靜態內部類能夠直接訪問外圍類的數據,包括私有數據
方法 | 功能 |
---|---|
Collections.synchronizedXxx() | 將指定的集合包裝成線程同步的集合 |
collection1.containsAll(Collection2) | 合併兩個collection |
Collection.addAll(collection,11,12,13,14) | 接受一個collection對象,以及一個數組或者是用逗號分割的列表 |
Collections.sort(list); | 根據元素的天然順序 對指定列表按升序進行排序。 |
Collections.shuffle(List<?> list, Random rand); | 打亂 |
arrays.aslist()接受一個逗號分隔的元素列表(可變參數)或者數組,並轉換爲一個list對象
表明長度能夠改變得數組。能夠對元素進行隨機的訪問,插入與與刪除元素的速度慢。
默認大小爲10,不夠再以1.5倍擴容新數組,拷貝數據到新數組中
在實現中採用鏈表數據結構。插入和刪除速度快,隨機訪問速度慢。
method | 解釋 |
---|---|
subList(List2) | 建立片斷 |
indexOf(object) | 獲取索引編號 |
containsAll(List2) | 是否包含,無視順序 |
addFirst/add/addLast | 添加單個元素 |
remove/removeFirst/poll/移除並返回列表頭 removeLast 移除並返回列表尾 | |
getFirst/element/peek | |
peek/poll 列表空返回null | |
詳細參考:LinkedList基本用法
一個是數組,一個是鏈表,二者的讀寫屬性基本相反。
單向移動:
while(it.hasNext()) { Pet p = it.next(); print(p); }
延伸點:可能會引發ConcurrentModificationException 異常
ListIterator:雙向移動
method | 解釋 |
---|---|
nextIndex() | 返回列表中ListIterator所需位置後面元素的索引 |
previousIndex() | |
previous() | |
next() | |
listIterator(n) | 一開始就指向列表索引爲n處的ListIterator,空則指向開始處 |
remove() | 對迭代器使用hasNext()方法時,刪除ListIterator指向位置後面的元素;當對迭代器使用hasPrevious()方法時,刪除ListIterator指向位置前面的元素 |
不重複,測試歸屬性(查找是否存在set中)
和collection接口相同
Map沒有繼承於Collection接口
Map集合中的鍵對象不容許重複,也就說,任意兩個鍵對象經過equals()方法比較的結果都是false,可是能夠將任意多個鍵獨享映射到同一個值對象上。
Object put(Object key, Object value):
Object remove(Object key)
void putAll(Map t): 未來自特定映像的全部元素添加給該映像
void clear(): 從映像中刪除全部映射
Object get(Object key): 得到與關鍵字key相關的值
containsKey()和containsValue()測試Map中是否包含某個「鍵」或「值」。
名稱 | 線程安全 | 功能 |
---|---|---|
HashMap | 不安全 | 基於散列表的實現。插入和查詢「鍵值對」的開銷是固定的。能夠經過構造器設置容量capacity和負載因子load factor,以調整容器的性能。 |
LinkedHashMap | 不安全 | 相似於HashMap,可是迭代遍歷它時,取得「鍵值對」的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點。而在迭代訪問時發而更快,由於它使用鏈表維護內部次序。 |
TreeMap | 不安全 | 基於紅黑樹數據結構的實現。查看「鍵」或「鍵值對」時,它們會被排序(次序由Comparabel或Comparator決定)。TreeMap的特色在於,你獲得的結果是通過排序的。TreeMap是惟一的帶有subMap()方法的Map,它能夠返回一個子樹。 |
WeakHashMap | 不安全 | 弱鍵(weak key)Map,Map中使用的對象也被容許釋放: 這是爲解決特殊問題設計的。若是沒有map以外的引用指向某個「鍵」,則此「鍵」能夠被垃圾收集器回收。 |
IdentifyHashMap | 不安全 | 使用==代替equals()對「鍵」做比較的hash map。專爲解決特殊問題而設計。 |
ConcurrentHashMap | 安全 | 將Hash表分爲16segment,每次只須要的segment進行加鎖 |
它根據鍵的hashCode值存儲數據,大多數狀況下能夠直接定位到它的值,於是具備很快的訪問速度,但遍歷順序倒是不肯定的。
HashMap 大概具備如下特色:
除了不容許 null 而且同步,Hashtable 幾乎和他同樣。
JDK1.7 中hashmap 是經過 桶(數組)加鏈表的數據結構來實現的。當發生hash碰撞的時候,以鏈表的形式進行存儲。
JDK1.8 中hashmap 增長了在原有桶(數組) + 鏈表的基礎上增長了黑紅樹的使用。當一個hash碰撞的數量超過指定次數(TREEIFY_THRESHOLD)的時候,鏈表將轉化爲紅黑樹結構。
默認爲0.75。通常不建議修改,
桶數默認爲16,很是規設計(通常採用素數),主要是爲了在取模和擴容時作優化,同時爲了減小衝突。
關於桶數,在構造hashmap時,若是你傳入的是一個本身設定的數字,它會調用tableSizeFor幫你轉換成一個比傳入容量參數值大的最小的2的n次方(因此無論你傳入什麼數字最終仍是2的冪)。
這裏tableSizeFor若是你看過源代碼的話,會發現用的是挺晦澀的位運算(強烈吐槽),那一大堆的運算你能夠理解爲把這個數字的全部位都變爲1,再加上1,就能獲得比傳入容量參數值大的最小的2的n次方。
默認值爲16應該是
HashMap 中經過將傳入鍵的 hashCode
和此值按位右移 16 位補零後的值
進行按位異或,獲得這個鍵的哈希值。
(h = key.hashCode()) ^ (h >>> 16);
這裏是因爲,因爲哈希表的容量都是 2 的 N 次方(最大2^30),在當前,元素的 hashCode() 在不少時候下低位是相同的,這將致使衝突(碰撞),所以 1.8 之後作了個移位操做:將元素的 hashCode() 和本身右移 16 位後的結果求異或。
因爲 int 只有 32 位(4*8),無符號右移 16 位至關於把高位的一半移到低位:
使用segment來分段和管理鎖,segment繼承自ReentrantLock,所以ConcurrentHashMap使用ReentrantLock來保證線程安全。
JDK1.8以前採用分段鎖,核心就是一句話:儘可能下降同步鎖的粒度。
JDK1.8以後使用CAS思想代替冗雜的分段鎖實現。
不出意料,面試者答出CAS以後一定會被追問其思想以及應用,換作我本身的話會有以下思路做答:CAS採用樂觀鎖思想達到lock free,提一下sun.misc.Unsafe中的native方法,至於CAS的其餘應用能夠聊一聊Atomic原子類和一些無鎖併發框架(如Amino),提到ABA問題加分。
都實現了Map接口,主要的區別有:
先進先出
喂(Vector) S(Stack) H(hashtable) E(enumeration)
拋InterruptedException的表明方法有:
- java.lang.Object 類的 wait 方法
- java.lang.Thread 類的 sleep、join方法
catch匹配到第一個符合的處理程序就結束
能夠重寫構造器,傳入一個字符串
調用printStackTrace()能夠打印出異常
構造器:派生類構造器不能捕獲基類構造器拋出的異常/派生類構造器的異常說明必須包含基類構造器的異常說明。
若是基類構造器沒有異常說明,派生類能有。
方法:派生類方法遵照基類方法的異常說明,可是也能夠不拋出異常。
若是基類方法沒有異常說明,派生類也不能有。
對象:派生類的對象只被要求捕獲這個類所拋出的異常/基類的對象只被要求捕獲基類所拋出的異常
異常匹配:拋出的異常能夠被此異常的基類catch
因此使用try、catch、finally語句塊中須要注意的是
String對象是不可變的(只讀),內部使用 char 數組存儲數據,該數組被聲明爲 final 。
方法 | 功能 |
---|---|
substring | 左閉右開,只有一個數值則右邊到底 |
int indexOf(String str) | 可用於判斷子字符串是否存在 |
Java 5提出,因爲String的鏈接(+)實際上是調用了StringBuilder.append,因此,建議在for循環內使用StringBuilder來進行字符串的拼接。
和StringBuilder相似
若是程序是在單線程下運行,或者是沒必要考慮到線程同步問題,咱們應該優先使用StringBuilder類;若是要保證線程安全,天然是StringBuffer。
優先StringBuilder,爲了線程安全用StringBuffer
String正則表達式相關的method
method | 解釋 |
---|---|
matches(String regex) | |
split(String regex) | |
split(String regex, int limit) | 限制分割次數,最終數組數爲limit |
replaceFirst(),replaceAll() |
如:String str="abcaxc"; Patter p="ab*c";
貪婪匹配:正則表達式通常趨向於最大長度匹配,也就是所謂的貪婪匹配。如上面使用模式p匹配字符串str,結果就是匹配到:abcaxc(ab*c)。
非貪婪匹配:就是匹配到結果就好,就少的匹配字符。如上面使用模式p匹配字符串str,結果就是匹配到:abc(ab*c)。
Pattern p = Pattern.compile(regex); Matcher m = p.matcher(String args);
method | 解釋 |
---|---|
matches() | 是否匹配 |
lookingAt() | 該字符串的起始部分是否匹配 |
find() |
返回參與比較的先後兩個字符串的asc碼的差值,
若是兩個字符串首字母不一樣,則該方法返回首字母的asc碼的差值;
若是參與比較的兩個字符串若是首字符相同,則比較下一個字符,直到有不一樣的爲止,返回該不一樣的字符的asc碼差值,若是兩個字符串不同長,能夠參與比較的字符又徹底同樣,則返回兩個字符串的長度差值
RTTI:runtime type information
延伸點:類的加載
全部的類都是在第一次使用時,動態加載到JVM中的。
構造器也是static
Class.forName(目標類全限定名的String),返回class引用,若是沒有加載就加載它
Object.getClass。返回該對象的實際類型的Class引用
Class.getInterfaces
Class.getSuperclass
Class.newInstance:
一般狀況下,一個編譯器處理泛型有兩種方式:Code specialization
和Code sharing
。C++和C#是使用Code specialization
的處理機制,而Java使用的是Code sharing
的機制。
Code sharing方式爲每一個泛型類型建立惟一的字節碼錶示,而且將該泛型類型的實例都映射到這個惟一的字節碼錶示上。將多種泛型類形實例映射到惟一的字節碼錶示是經過類型擦除(type erasue
)實現的。
也就是說,對於Java虛擬機來講,他根本不認識Map<String, String> map
這樣的語法。須要在編譯階段經過類型擦除的方式進行解語法糖。
類型擦除的主要過程以下:
虛擬機中沒有泛型,只有普通類和普通方法,全部泛型類的類型參數在編譯時都會被擦除,泛型類並無本身獨有的Class類對象。好比並不存在List<String>.class
或是List<Integer>.class
,而只有List.class
。
生成器(generator)專門負責創造對象的類;
通常一個生成器只定義一個方法:用以產生新的對象;
基本類型沒法做爲類型參數
泛型方法所在的類能夠不是泛型類,若是static方法須要使用泛型能力,就必須使其成爲泛型方法
使用泛型類,必須在建立對象的時候指定類型參數的值,泛型方法不用
類型推斷避免重複的泛型參數列表,可是隻對賦值有效:
e.g:Map<Person,List<? extends Pet>> petPeople = New.Map()
;
反射能夠用於判斷任意對象所屬的類,得到Class對象,構造任意一個對象以及調用一個對象
詳見:java中的反射機制
//獲取類型 Class c1 = Class.forName("Employee"); Class c2 = Employee.class; Employee e = new Employee(); Class c3 = e.getClass(); //建立對象 Objecto = c1.newInstance(); //調用了Employee的無參數構造方法. //獲取屬性 Field[] fs = c.getDeclaredFields(); getDeclaredMethods()//獲取全部的方法 getReturnType() //得到方法的放回類型 getParameterTypes() //得到方法的傳入參數類型 getDeclaredMethod("方法名",參數類型.class,……)//得到特定的方法 getDeclaredConstructors()//獲取全部的構造方法 getDeclaredConstructor(參數類型.class,……) //獲取特定的構造方法 getSuperclass()//獲取某類的父類 getInterfaces()//獲取某類實現的接口
使用案例如Hibernate,BeanUtils。
在程序運行時,運用反射機制動態建立而成。
動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。
java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
代理類每每會在代理對象業務邏輯先後增長一些功能性的行爲,如使用事務或者打印日誌。
必須實現InvocationHandler接口,並在該類中定義代理行爲。
依賴於字節碼技術
cglib是針對類來實現代理的,他的原理是對指定的目標類
生成一個子類
,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾
的類進行代理。
數組命名時名稱與[]能夠隨意排列
粗糙數組:構成矩陣的每一個向量均可以具備任意的長度
不能實例化具備參數化類型的數組
建立測試數據:
Arrays.asList()返回的是一個List (List是一個接口,返回List實際是返回List接口的一個實現),這個List在底層是有數組實現的,因此size是fixed的
Arrays.fill(Arrays,parameters):用同一個parameter填充整個數組(引用)
Arrays實用功能:
System.arraycopy(源數組,偏移量,目標組,複製偏移量,複製個數)
file既能夠表明一個文件名,也能夠表明一個文件夾的名字
bio,nio區別要熟知,瞭解nio中的ByteBuffer,Selector,Channel能夠幫助面試者度過很多難關。幾乎提到nio一定會問netty,其實我分析了一下,問這個的面試官本身也不必定會,但就是有人喜歡問,因此我們適當應付一下就好:一個封裝很好擴展很好的nio框架,經常使用於RPC框架之間的傳輸層通訊。
後綴是stream的都是字節流,其餘的都是字符流
聲明爲static和transient類型的成員數據不能被串行化。由於static表明類的狀態, transient表明對象的臨時數據。
inputstream的close方法用來關閉流
skip()用來跳過一些字節
mark()用來標記流
reset()復位流
一、緩衝輸入文件
緩衝區的做用的主要目的是:避免每次和硬盤打交道,提升數據訪問的效率。
public class BufferedInputFile { public static String read(String filename) throws IOException { BufferedReader in = new BufferedReader(new FileReader(filename)); String s; StringBuilder sb = new StringBuilder(); while((s = in.readLine())!= null) sb.append(s + "\n"); in.close(); return sb.toString(); } public static void main(String[] args)throws IOException { System.out.print(read("BufferedInputFile.java")); } }
二、從內存輸入
public class MemoryInput { public static void main(String[] args)throws IOException { StringReader in = new StringReader(BufferedInputFile.read("MemoryInput.java")); int c; //read()是以int形式返回下一字節,必須轉爲char才能正確打印 while((c = in.read()) != -1) System.out.print((char)c); } }
三、格式化的內存輸入
public class FormattedMemoryInput { public static void main(String[] args) throws IOException { try { DataInputStream in = new DataInputStream(new ByteArrayInputStream( BufferedInputFile.read("FormattedMemoryInput.java").getBytes())); while(true) System.out.print((char)in.readByte()); } catch(EOFException e) { System.err.println("End of stream"); } } }
四、基本的文件輸出
public class BasicFileOutput { static String file = "BasicFileOutput.out"; public static void main(String[] args)throws IOException { BufferedReader in = new BufferedReader( new StringReader(BufferedInputFile.read("BasicFileOutput.java"))); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file))); int lineCount = 1; String s; while((s = in.readLine()) != null ) out.println(lineCount++ + ": " + s); out.close(); // Show the stored file: System.out.println(BufferedInputFile.read(file)); } }
爲了提升讀寫性能,能夠採用什麼流
Java中有幾種類型的流
JDK 爲每種類型的流提供了一些抽象類以供繼承,分別是哪些類
對文本文件操做用什麼I/O流
對各類基本數據類型和String類型的讀寫,採用什麼流
能指定字符編碼的 I/O 流類型是什麼
延伸點:裝飾者模式
BIO:同步阻塞式IO,簡單理解:一個鏈接一個線程;Apache,Tomcat。主要是併發量要求不高的場景
NIO:同步非阻塞IO,簡單理解:一個請求一個線程;Nginx,Netty。主要是高併發量要求的場景
AIO:異步非阻塞IO,簡單理解:一個有效請求一個線程;還不是特別成熟,底層也基本是多線程模擬,因此應用場景很少,Netty曾經用了,但又放棄了。
什麼是序列化?如何實現 Java 序列化及注意事項
Serializable 與 Externalizable 的區別
方法 | 功能 | 返回值 |
---|---|---|
values() | 返回enum實例的數組 | T[] |
compareTo(E o) | 順序比較,若是該枚舉對象位於指定枚舉對象以後,則返回正整數;反之返回負整數;不然返回零; | int |
name() | 返回此枚舉實例的名稱 | |
getDeclaringClass() | 返回實例所屬於的enum類 | T |
ordinal() | 返回實例聲明時候的順序 | int |
定義實例方法必須在任何方法或者屬性前
定義實例方法後面加上「;」,而後開始寫方法
當多個線程訪問某個類時,無論運行時環境採用何種調度方式或者這些線程將如何交替進行,而且在主調代碼中不須要任何額外的同步或協同,這個類都能表現出正確的行爲,那麼稱這個類是線程安全的
一般與鎖一塊兒出現:除了synchronized以外,還常常被問起的是juc中的Lock接口,其具體實現主要有兩種:可重入鎖,讀寫鎖。這些都沒問題的話,還會被詢問到分佈式下的同步鎖,通常藉助於中間件實現,如Redis,Zookeeper等,開源的Redis分佈式鎖實現有Redisson,回答注意點有兩點:一是注意鎖的可重入性(藉助於線程編號),二是鎖的粒度問題。除此以外就是一些juc的經常使用工具類如:CountdownLatch,CyclicBarrir,信號量
ExecutorService exec = Executors.newCacheThreadPool(); for(int i ; i <5 ; i++) //Liftoff實現了runnable接口 exec.execute(new Liftoff()); //防止新任務提交給這個executor exec.shutdown();
newFixedThreadPool(int number);一次性預先執行高代價的線程分配
newSingleThreadExecutor; 相似於newFixedThreadPool(1),會序列化全部提交給它的任務。
method | 解釋 |
---|---|
run() | 執行線程體中具體的內容,若是直接調用run方法,其實仍是單線程 |
start() | 啓動線程對象,使其進入就緒狀態 |
sleep() | 佔着CPU不工做。能夠拋出interruptException,可是異常拋出的時候回清理這個標誌,致使在catch裏面調用isInterrupt(),總爲false。TimeUnit.SECONDS.sleep(1):可讀性更高 |
suspend() | 使線程掛起,要經過resume()方法使其從新啓動 |
join() | 等待該線程終止;在父線程中,子線程調用join,父線程被掛起,等待子線程結束纔回復。 |
yield() | 當前線程放棄CPU,全部線程(包括當前線程)共同競爭CPU。 |
setDeamon(true) | 設置爲後臺線程,必須在線程start以前調用。 |
getName() | 有一構造函數參數爲String name;獲取線程的name。 |
isAlive() | 判斷是否被掛起 |
holdsLock() | 返回true若是當且僅當當前線程擁有某個具體對象的鎖。 |
wait:等待使用CPU
sleep:佔着CPU不工做
延伸點:注意
interrupted()
會清楚中斷狀態參考連接:如何正確地中止一個線程?
調度器傾向於讓優先級最高的線程先執行,一般在run的開頭部分設定
getPriority():讀取現有線程的優先級
setPriority():修改線程優先級。
每一個線程都有一個ThreadLocal就是每一個線程都擁有了本身獨立的一個變量,競爭條件被完全消除了。
它是爲建立代價高昂的對象獲取線程安全的好方法,好比你能夠用ThreadLocal讓SimpleDateFormat變成線程安全的,由於那個類建立代價高昂且每次調用都須要建立不一樣的實例因此不值得在局部範圍使用它,若是爲每一個線程提供一個本身獨有的變量拷貝,將大大提升效率。
首先,經過複用減小了代價高昂的對象的建立個數。其次,你在沒有使用高代價的同步或者不變性的狀況下得到了線程安全。線程局部變量的另外一個不錯的例子是ThreadLocalRandom類,它在多線程環境中減小了建立代價高昂的Random對象的個數。
悲觀鎖是獨佔鎖,阻塞鎖,寫入頻繁使用悲觀鎖
樂觀鎖是非獨佔鎖,非阻塞鎖。讀取頻繁使用樂觀鎖。
樂觀鎖(
Optimistic Locking
)實際上是一種思想。相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作。有一種樂觀鎖的實現方式就是CAS ,這種算法在JDK 1.5中引入的
java.util.concurrent
(J.U.C)中有普遍應用。可是值得注意的是這種算法會存在ABA問題(部分經過版本號來解決)
這裏就是悲觀鎖樂觀鎖的應用處理併發問題
參考資料:樂觀鎖的一種實現方式——CAS
死鎖是指兩個或兩個以上的進程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。這是一個嚴重的問題,由於死鎖會讓你的程序掛起沒法完成任務。
死鎖的發生必須知足如下四個條件:
避免死鎖最簡單的方法就是阻止循環等待條件,將系統中全部的資源設置標誌位、排序,規定全部的進程申請資源必須以必定的順序(升序或降序)作操做來避免死鎖。
Q:如何檢查死鎖?
A:經過jConsole檢查死鎖
參考資料:java 查看線程死鎖
可重入鎖,也叫作遞歸鎖,指的是同一線程 外層函數得到鎖以後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。
在JAVA環境下 ReentrantLock 和synchronized 都是 可重入鎖
加到 static 方法上是給 Class 上鎖,即整個類上鎖。
Lock對象必須被顯式地建立、鎖定和釋放。
緊接着lock的調用,後面是try-finally語句,同時finally裏面要有unclock來釋放鎖
public class MutexEvenGenerator extends IntGenerator { private int currentEvenValue = 0; private Lock lock = new ReentrantLock(); public int next() { lock.lock(); try { ++currentEvenValue; Thread.yield(); // Cause failure faster ++currentEvenValue; return currentEvenValue; } finally { lock.unlock(); } } public static void main(String[] args) { EvenChecker.test(new MutexEvenGenerator()); } } ///:~
禁止指令重排序優化。編譯器只保證程序執行結果與源代碼相同,卻不保證明際指令的順序與源代碼相同。這在單線程看起來沒什麼問題,然而一旦引入多線程,這種亂序就可能致使嚴重問題。volatile關鍵字就能夠從語義上解決這個問題。 jdk1.5之後生效。
關注點:主存和工做內存, happens-before原則(主要是前三條)
JMM規定全部變量都是存在主存中的,每一個線程又包含本身的工做內存。因此線程的操做都是以工做內存爲主,它們只能訪問本身的工做內存,且工做先後都要把值在同步回主內存。
基本類型變量,除了long和double,都能保證原子性的操做。
long和double因爲是分離的32位來操做,不能保證原子性。
當一個變量被volatile修飾時,那麼對它的修改會馬上刷新到主存,當其它線程須要讀取該變量時,會去內存中讀取新值。而普通變量則不能保證這一點。
其實經過synchronized和Lock也可以保證可見性,線程在釋放鎖以前,會把共享變量值都刷回主存,可是synchronized和Lock的開銷都更大。
JMM是容許編譯器和處理器對指令重排序的,可是規定了as-if-serial語義,即無論怎麼重排序,程序的執行結果不能改變。
ThreadB_start()
(啓動線程B) , 那麼A線程的ThreadB_start()
happens-before 於B中的任意操做ThreadB.join()
而且成功返回,那麼線程B中的任意操做happens-before於線程A從ThreadB.join()
操做成功返回。interrupt()
方法的調用先行發生於被中斷線程代碼檢測到中斷事件的發生,能夠經過Thread.interrupted()
方法檢測是否有中斷髮生finalize()
方法的開始同步器是一些使線程可以等待另外一個線程的對象,容許它們協調動做。最經常使用的同步器是CountDownLatch和Semaphore,不經常使用的是Barrier 和Exchanger
延伸點:java多線程面試彙總
要注意true,false,null, friendly,sizeof不是java的關鍵字,可是你不能把它們做爲java標識符用。
null是表明不肯定的對象,null是一個關鍵字。所以能夠將null賦給引用類型變量,但不能夠將null賦給基本類型變量。
容器類型與null
List:容許重複元素,能夠加入任意多個null。
Set:不容許重複元素,最多能夠加入一個null。
Map:Map的key最多能夠加入一個null,value字段沒有限制。
數組:基本類型數組,定義後,若是不給定初始值,則java運行時會自動給定值。引用類型數組,不給定初始值,則全部的元素值爲null。
一、判斷一個引用類型數據是否null。 用==來判斷。
二、釋放內存,讓一個非null的引用類型變量指向null。這樣這個對象就再也不被任何對象應用了。等待JVM垃圾回收機制去回收。
java.lang包定義了一些基本的類型,包括Integer,String之類的,是java程序必備的包,有解釋器自動引入,無需手動導入
工具 | 功能 |
---|---|
javac.exe | 把.java文件編譯爲.class文件(每一個類一個.class文件) |
java.exe | 執行編譯好的.class文件 |
javadoc.exe | 生成Java說明文檔,api文檔 |
jdb.exe | Java調試器 |
javaprof.exe | 剖析工具 |
Java語言使用的是Unicode字符集。
ascii:美國標準信息交換碼。使用的是1個字節的7位來表示該表中的字符。
ISO8859-1:拉丁碼錶。使用1個字節來表示。
GB2312:簡體中文碼錶。
GBK:簡體中文碼錶,比GB2312融入更多的中文文件和符號。
unicode:國際標準碼錶。都用兩個字節表示一個字符。
UTF-8:對unicode進行優化,每個字節都加入了標識頭。
名稱 | 功能 |
---|---|
單一職責原則(Single-Resposibility Principle) | 一個類,最好只作一件事,只有一個引發它的變化。單一職責原則能夠看作是低耦合、高內聚在面向對象原則上的引伸,將職責定義爲引發變化的緣由,以提升內聚性來減小引發變化的緣由。 |
開放封閉原則(Open-Closed principle) | 軟件實體應該是可擴展的,而不可修改的。也就是,對擴展開放,對修改封閉的。 |
Liskov替換原則(Liskov-Substituion Principle) | 子類必須可以替換其基類。這一思想體現爲對繼承機制的約束規範,只有子類可以替換基類時,才能保證系統在運行期內識別子類,這是保證繼承複用的基礎。 |
依賴倒置原則(Dependecy-Inversion Principle) | 依賴於抽象。具體而言就是高層模塊不依賴於底層模塊,兩者都同依賴於抽象;抽象不依賴於具體,具體依賴於抽象。 |
接口隔離原則(Interface-Segregation Principle) | 使用多個小的專門的接口,而不要使用一個大的總接口 |
session.setAttribute()和session.getAttribute()配對使用,做用域是整個會話期間,在全部的頁面都使用這些數據的時候使用。
request.getAttribute()表示從request範圍取得設置的屬性,必需要先setAttribute設置屬性,才能經過getAttribute來取得,設置與取得的爲Object對象類型。
延伸點:JDK, JRE 和JVM的區別
繼承自Hashtable
我本身實際使用應該只有在xml裏
<context:property-placeholder location="classpath*:conf/conf_a.properties"/> <bean class="com.xxx.aaa.Bean1" p:driverClassName="${modulea.jdbc.driverClassName}" p:url="${modulea.jdbc.url}" p:username="${modulea.jdbc.username}" p:password="${modulea.jdbc.password}"/>
兼容數組類參數的。
必須做爲參數列表的最後一項
能匹配定長的方法,那麼優先匹配該方法。含有不定參數的那個重載方法是最後被選中的。
參考: Java方法的可變參數個數