從網上找了些面試題,本身手工總結了理解了一下,若有理解錯誤,還請指正。javascript
java基礎html
4. 描述一下ArrayList和LinkedList各自實現和區別 java
ArrayList是線性表 ,隨機取數快,數據刪除插入較慢,佔用內存小面試
LinkedList是鏈表 插入刪除快,查找遍歷較慢佔用內存稍大算法
5. Java中的隊列都有哪些,有什麼區別。編程
隊列是實現生產者消費者的重要工具,java中的隊列通常氛圍阻塞的和非阻塞的,或者是根據須要有其餘多重的實現方式:api
ArrayBlockingQueue——帶邊界的阻塞式隊列數組
ConcurrentLinkedDeque / ConcurrentLinkedQueue——無邊界的鏈表隊列(CAS)緩存
DelayQueue——元素帶有延遲的隊列安全
LinkedBlockingDeque / LinkedBlockingQueue——鏈表隊列(帶鎖),可設定是否帶邊界
LinkedTransferQueue——可將元素`transfer`進行w/o存儲
PriorityBlockingQueue——併發PriorityQueue
SynchronousQueue——使用Queue接口進行Exchanger
Queue在設計之處就考慮了多線程的安全性,表明性的Queue的兩種實現,表示實現線程安全的兩套方案:•BlockingQueue,•ConcurrentLinkedQueue
前者是阻塞的同步的,後者是併發的。
這裏提一下多線程安全問題的含義,以前有些搞混了。線程安全的類 ,指的是類內共享的全局變量的訪問必須保證是不受多線程形式影響的。若是因爲多線程的訪問(好比修改、遍歷、查看)而使這些變量結構被破壞或者針對這些變量操做的原子性被破壞,則這個類就不是線程安全的。實現線程安全又有兩種方式,一種是同步的,將多線程的請求排個隊,A線程進來了關上廁所門,其餘線程在外面等待。A線程執行結束,B線程進入一樣關上門。這樣的操做保證了多線程下共享變量不會被修改,可是效率比較差,若是線程太多會致使嚴重的鎖爭用。還有一種方式是併發的,多個線程能夠同時進入一段代碼,如何保證線程安全呢?就是經過各個線程之間對共享變量的修改和操做都是可見的,你改了什麼我必須第一時間知道,而後針對性地作一些操做。這樣極大地提升了處理速度,只是算法的實現會很是的複雜。另外,類自身實現線程安全,只是表明類自身的api保持原子性,可是外部使用多個api的狀況下,仍是要手動進行加鎖。concurrent包下的集合類實現,都是採用的併發方式,而Collections工具類包裝的集合都是使用同步鎖方式。其中CurrentHashMap應該是使用了分段鎖和volatile。java併發:同步容器&併發容器 - 煙雨暗千家 - 博客園
LinkedList實現了Queue接口,也能夠做爲隊列使用,可是不能夠訪問非Queue接口的方法,以避免發生錯亂。
BlocingQueue:ArrayBlockingQueue,LinkedBlockingQueue(入隊和出隊兩把鎖),DelayQueue,PriorityBlockingQueue(公平鎖),SynchronousQueue。
ReentrantLock 只鎖部分代碼塊
ConcurrentLinkedQueue:使用volatile關鍵字使共享變量可見,具體細節頗爲複雜。
【Java多線程總結之聊一聊Queue - I'm Sure - ITeye技術網站 】【java中線程隊列BlockingQueue的用法-shwenwen-ITPUB博客 】
這個坑很深,目前只能研究到這裏了……
6. 反射中,Class.forName和classloader的區別。
Class.forName()方法底層調用Class.forName(className,true,classloader);表示在加載類的時候,執行初始化步驟;
ClassLoader.loadClass()方法底層調用ClassLoader.loadClass(className,false);表示不執行初始化步驟;
初始化步驟會初始化靜態變量和靜態代碼塊,若是此處沒有初始化,那就須要在newInstance時,初始化了。根據不一樣的須要,調用相應的方法。
可能會引伸到JVM裝載類的過程:編譯解釋java文件爲二進制class,裝載class(經過類的徹底限定名和類加載器實例),連接(對二進制字節碼校驗,初始化靜態變量,找到父類接口和類,肯定調用的方法和類都是存在的且具有權限),初始化(靜態代碼塊其餘)。
7. Java七、Java8的新特性
java7的更新多數是語法糖:
try包含資源代碼塊,自動釋放;多個catch合併;更強的類型推測,new的時候再也不須要指定類型,直接<>便可;二進制字面量支持;經過[],{}初始化集合。
java8
Lambda表達式與函數接口,接口支持默認方法和靜態方法,可以使用相似於C++的方法引用 class::method,重複註解,更強的類型推測,可將參數名稱保存在字節碼中,Optional 支持對null的處理,Stream 相似於mapReduce的集合處理方式,api能夠直接連綴調用,Date/Time API 的更新,在兼容舊api分基礎上提供了許多新的api,傳聞java 9 將進一步支持貨幣api,base64直接支持,支持運行javascript以及js和java的相互調用(java8就有了?),jvm的permGen移除,類依賴分析器jdeps。
8. Java數組和鏈表兩種結構的操做效率,在哪些狀況下(從開頭開始,從結尾開始,從中間開始),哪些操做(插入,查找,刪除)的效率高。
數組 不能動態擴容,佔內存小,添加刪除操做稍慢,查詢快。
鏈表 能夠動態擴容,佔內存大,添加刪除操做快,查詢慢。
9. Java內存泄露的問題調查定位:jmap,jstack的使用等等。
沒用過很差說
10. string、stringbuilder、stringbuffer區別
String 是final的字符串,不可變。StringBuffer是線程安全的可變String,StringBuilder是簡化的可變字符串,不是線程安全的。
11. hashtable和hashmap的區別
上面有所說起:
1. HashTable不支持用null做爲key,而HashMap支持
2. HashTable是線程安全的map集合,HashMap不是線程安全的。
3. HashTable由於支持線程安全,因此沒有HashMap的快速失敗機制,可是HashTable不能保證多個api複合調用時保證安全。
4. Map想要支持多線程安全能夠經過Collections工具類獲取同步的Map,或者經過concurrent包中的concurrentHashMap返回併發的Map,並且能夠與有序的TreeMap轉化,不少時候,即使是多線程環境下也並不必定須要HashTable。
13 .異常的結構,運行時異常和非運行時異常,各舉個例子。
單獨再說
14. String 類的經常使用方法
length() , subString , indexOf , lastIndexOf , charAt , equals ,toUpperCase , equalsIgnoreCase , trim , toString , toLowerCase , replace , match(用於正則) , startsWith , compareTo. format(容許相似於C語言的輸出字符串格式控制)
15. Java 的引用類型有哪幾種
String , Object ,基本類型的包裝類型:Integer,Long , Boolean , Short , Float,Double,Byte,Character,Void。Enum枚舉類型
16. 抽象類和接口的區別
從語法上來看:
參數 | 抽象類 | 接口 |
默認的方法實現 | 它能夠有默認的方法實現 | 接口徹底是抽象的。它根本不存在方法的實現 |
實現 | 子類使用extends關鍵字來繼承抽象類。若是子類不是抽象類的話,它須要提供抽象類中全部聲明的方法的實現。 | 子類使用關鍵字implements來實現接口。它須要提供接口中全部聲明的方法的實現 |
構造器 | 抽象類能夠有構造器 | 接口不能有構造器 |
與正常Java類的區別 | 除了你不能實例化抽象類以外,它和普通Java類沒有任何區別 | 接口是徹底不一樣的類型 |
訪問修飾符 | 抽象方法能夠有public、protected和default這些修飾符 | 接口方法默認修飾符是public。你不可使用其它修飾符。 |
main方法 | 抽象方法能夠有main方法而且咱們能夠運行它 | 接口沒有main方法,所以咱們不能運行它。 |
多繼承 | 抽象方法能夠繼承一個類和實現多個接口 | 接口只能夠繼承一個或多個其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有點慢的,由於它須要時間去尋找在類中實現的方法。 |
添加新方法 | 若是你往抽象類中添加新的方法,你能夠給它提供默認的實現。所以你不須要改變你如今的代碼。 | 若是你往接口中添加方法,那麼你必須改變實現該接口的類。 |
從本質上來看:
抽象類是對子類的一個抽象,抽象類提供了子類的某些方法約束,做爲公共的你們都必須遵照的約束,有利於直接複用代碼。不能肯定的子類行爲能夠採用抽象方法定義,交給具體子類實現。抽象類更多的是在具體代碼編寫時的一種設計。
接口是定義一組約束規則,做爲完全的抽象層存在。它只要求實現者實現全部的方法,而不關心具體的實現方式,它只爲子類指定功能,可是不存在對子類實現方式的要求,也就是說沒有公共的已經實現的方法。更多的是用於軟件架構設計中,對不一樣模塊的抽象。
java8中支持了接口的默認方法,具體後面再研究。
18. java的基礎類型和字節大小
java中基本數據類型(不是引用類型)有8種
byte | Byte | 1 | -128 | 127 |
char | Character | 2 | unicode-0 | unicode 2^16-1 |
short | Short | 2 | -2^15 | 2^15-1 |
int | Integer | 4 | -2^31 | 2^31-1 |
float | Float | 4 | ||
double | Double | 8 | ||
long | Long | 8 | ||
boolean | Boolean | - | true | false |
19. Hashtable,HashMap,ConcurrentHashMap底層實現原理與線程安全問題。
HashMap沒有作多線程的控制,底層實現原理以前有提到過。HashTable是使用了重用鎖,在操做時鎖住了一段代碼塊,是同步的。ConcurrentHashMap,內部使用了volatile關鍵字,使得共享變量在多線程之間可見和同步,編程上考慮的比較多算法比較複雜。具體的上面的文章中有提到過。
20. Hash衝突怎麼辦?哪些解決散列衝突的方法?
hash衝突應該是hash算法的問題,也有多是hashCode算法的問題。這個//todo
21. HashMap衝突很厲害,最差性能,你會怎麼解決?從O(n)提高到log(n)。
首先哈希表是經過哈希函數計算元素的哈希值,而後將次哈希值做爲要存放的數組地址的下標,以便達到用數組存放元素,同時可以實現較快的查找刪除操做。
這裏哈希函數就很是重要,若是哈希函數計算的不一樣元素的哈希值容易重複,就會致使哈希衝突(不一樣元素映射到同一下標),哈希表不得不額外地解決這些衝突。
好的哈希函數會盡可能避免衝突,能均勻的分佈。以及計算簡單。
解決哈希衝突的辦法主要有下面幾種:
開放地址法:若是發現哈希值下標已經被佔用,就向下查找下一個空元素存入,問題是很是容易產生集聚現象,使得衝突加重。其中也有一些變種辦法,例如不直接向下查找下一個元素,而是生成不一樣的查找步數,這個能夠根據隨機數或者二次哈希產生。
再哈希法:衝突後再次執行另外一個哈希算法,獲得新的地址。
鏈地址法:若是哈希衝突了,就直接將衝突的元素接在原佔有的元素後面,造成一個鏈表。
而HashMap就是採用這種辦法作的。HashMap的性能,也是與其hash函數有關,當存放的key是用戶自定義的對象而不是String的時候,須要重寫這個對象的hashCode方法,這個hashCode方法的效率就會影響到HashMap的哈希函數的效率,若是HashCode不能作到計算簡單隨機分佈,hash函數就會容易衝突。
HashMap衝突很嚴重的話,還能夠將HashMap轉化爲平衡樹實現的TreeMap,後者的性能是logn.
23. rehash
看起來應該是HashMap的再哈希算法,主要用於當HashMap的容量超過負載因子(致使元素容易集聚成鏈表),HashMap就會擴容,全部的元素的哈希值就要從新計算,rehash大概就是用於此處的。rehash比較消耗性能,所以應該避免HashMap頻繁擴容。
24. hashCode() 與 equals() 生成算法、方法怎麼重寫。
hashCode
hashCode用於生成標記一個對象的最好是惟一的散列碼,它將被用在HashMap等散列表中,哈希函數會根據該散列碼計算數組下標從而存放起來。
所以若是散列碼是同樣的話,在HashMap中認爲是同一個對象,若是不同,它就會認爲這個一個新的對象。
你們說在重寫equals時重寫hashCode方法,是爲了不對象在用在哈希表的鍵時形成的問題。
設想重寫了equals方法,兩個不一樣的對象被認爲是相等的,咱們把相等的對象存到map的key中,而後再根據這個相等的對象取value的時候,就會出錯。由於HashMap根據hashCode計算出來的兩個對象的值不一樣,HashMap認爲是不同的。因此就找不到當初存放的對象。
因此hashCode須要重寫,而且保證兩個值相同的對象,它的hashCode必定是相同的。(若是固定地返回某個常量值,會致使系統會將全部的這種類型的對象都視爲相同。)
重寫hashCode方法是一項技術活,不過一些簡單的重寫方案仍是有的。下面的代碼從《effective java》中摘錄
/**此處的變量都是int類型*/ @Override public int hashCode(){ int result = 17; result = 31*reuslt + areaCode; result = 31*result + prefix; result = 31*result + lineNumber; return result; }
equals
/** * equals方法須要知足自反性,對稱性,傳遞性,一致性和非空性 * equasl方法寫完了要進行相關的測試 * 過程是: * 檢查被比較的對象是否是本身,若是是返回true * 檢查被比較的對象是否是null,若是是返回false * 檢查被比較的對象是否是和本身是同一個類型的,若是不是返回false * 將被比較對象強制轉換成本身的類型,而後逐一比較值。 * 對於float,double,數組等格式的域,須要使用特殊的比較方法,而不能用==。 * */ @Override public boolean equals(Object obj){ if(this == obj)return true; if(obj == null)return false; if(obj.getClass() != this.getClass())return false;//!(obj instanceof Te1) Te1 t = (Te1)obj; if(!t.name.equals(name))return false; if(!(t.id==id))return false; if(Double.compare(salary,t.salary)!=0)return false; if(!Arrays.equals(strs,t.strs))return false; return true; }