title: JAVA面試題之基礎篇
date: 2017-03-01
tags: 面試題
- 九種基本資料類型的大小,以及他們的封裝類。各自占多少位元組
boolean, byte, char, short, int, long, float, double, void 還有一種引用類型
Boolean, Byte, Character, Short, Integer, Long, Float, Double, Void
存在原因:節省記憶體開銷
2 2 2 4 8
- String 類能被繼承嗎,為什麼。
不能,String是一個final類
- 講講類的執行個體化順序,比如父類靜态資料,構造函數,字段,當 new 的時候,他們的執行順序
類執行個體化的過程中,先執行父類的構造器,然後執行隐式的構造代碼,再執行構造方法中的代碼
- JAVA8 的 ConcurrentHashMap 為什麼放棄了分段鎖,有什麼問題嗎,如果你來設計,你如何設計
它摒棄了Segment(鎖段)的概念,而是啟用了一種全新的方式實作,利用CAS算法,段鎖性能也不是很高,而CAS操作是CPU支援的操作,是一種原子操作
1、下面講一下concurrentHashMap的結構
ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成(一個segment裡面有一個hashEntry數組)。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裡扮演鎖的角色,HashEntry則用于存儲鍵值對資料。一個ConcurrentHashMap裡包含一個Segment數組,Segment的結構和HashMap類似,是一種數組和連結清單結構
- 有沒有有順序的 Map 實作類,如果有,他們是怎麼保證有序的。
LinkedHashMap 雙向連結清單
- 繼承和聚合的差別在哪
聚合:指的是整體與部分的關系。通常在定義一個整體類後,再去分析這個整體類的組成結構。進而找出一些組成類,該整體類群組成類之間就形成了聚合關系。例如一個航母編隊包括海空母艦、驅護艦艇、艦載飛機及核動力攻擊潛艇等。需求描述中“包含”、“組成”、“分為…部分”等詞常意味着聚合關系。
- 講講你了解的 nio
Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會擷取。而不是保持線程阻塞,是以直至資料變的可以讀取之前,該線程可以繼續做其他的事情
- 反射中,Class.forName 和 ClassLoader 差別
ClassLoader.loadClass(className)實際上調用的是ClassLoader.loadClass(name, false),第二個參數指出Class是否被初始化。差別就出來了。Class.forName(className)裝載的class已經被初始化,而ClassLoader.loadClass(className)裝載的class還沒有被link。
- 描述動态代理的幾種實作方式,分别說出相應的優缺點。
JDK代理(Proxy.newProxyInstance()方法建立動态代理)和CGLIB代理
CGLib建立的動态代理對象性能比JDK建立的動态代理對象的性能高不少,但是CGLib在建立代理對象時所花費的時間卻比JDK多得多,是以對于單例的對象,因為無需頻繁建立對象,用CGLib合适,反之,使用JDK方式要更為合适一些。同時,由于CGLib由于是采用動态建立子類的方法,對于final方法,無法進行代理
- 動态代理與cglib實作的差別
JDK動态代理隻能對實作了接口的類生成代理,而不能針對類 。
CGLIB是針對類實作代理,主要是對指定的類生成一個子類,覆寫其中的方法 。
因為是繼承,是以該類或方法最好不要聲明成final ,final可以阻止繼承和多态。
jdk中的動态代理通過反射類
和Proxy
回調接口實作,要求委托類必須實作一個接口,隻能對該類接口中定義的方法實作代理,這在實際程式設計中有一定的局限性。InvocationHandler
- 為什麼CGlib方式可以對接口實作代理
CGLIB是通過生成java 位元組碼進而動态的産生代理對象
- final的用途
final類不能被繼承,是以final類的成員方法沒有機會被覆寫,預設都是final的。在設計類時候,如果這個類不需要有子類,類的實作細節不允許改變,并且确信這個類不會載被擴充,那麼就設計為final類。
- 寫出三種單例模式實作
//餓漢模式 public class Singleton { private final static Singleton INSTANCE = new Singleton(); // Private constructor suppresses private Singleton() {} // default public constructor public static Singleton getInstance() { return INSTANCE; } }
//懶漢模式,和雙重檢查鎖模式 public class Singleton { private static volatile Singleton INSTANCE = null; // Private constructor suppresses // default public constructor private Singleton() {} //thread safe and performance promote public static Singleton getInstance() { if(INSTANCE == null){ synchronized(Singleton.class){ //when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again. if(INSTANCE == null){ INSTANCE = new Singleton(); } } } return INSTANCE; }
- 如何在父類中為子類自動完成所有的 hashcode 和 equals 實作?這麼做有何優劣
直接重寫equals方法和hashcode方法
保證hashcode唯一 (這題我不會….
-
請結合 OO 設計理念,談談通路修飾符 public、private、protected、default 在應
用設計中的作用
面對對象設計理念,自己扯,安全,通路控制域
- 數組和連結清單資料結構描述,各自的時間複雜度
數組要求是一塊連續的記憶體空間來存儲,單連結清單,每個元素除了存儲本身的值外還存儲了前驅的引用也就是存儲了前驅所在的記憶體位址資訊。這樣像鍊條一樣把各元素保證了在邏輯上的連續性。雙連結清單就是不僅存儲了前驅的引用還存儲了後繼的引用
- 請列出 5 個運作時異常
NullPointerException- 空指針引用異常
ClassCastException - 類型強制轉換異常。
IllegalArgumentException - 傳遞非法參數異常。
ArithmeticException - 算術運算異常
ArrayStoreException - 向數組中存放與聲明類型不相容對象異常
IndexOutOfBoundsException - 下标越界異常
NegativeArraySizeException - 建立一個大小為負數的數組錯誤異常
NumberFormatException - 數字格式異常
SecurityException - 安全異常
UnsupportedOperationException - 不支援的操作異常
- 在自己的代碼中,如果建立一個 java.lang.String 對象,這個對象是否可以被類加載器加載?為什麼
不能,雙親委派模型來加載
- 說一說你對 java.lang.Object 對象中 hashCode 和 equals 方法的了解。在什麼場景下需要重新實作這兩個方法
兩個方法是一起的,equals方法對比兩個對象是否值相等,如果相等他們的hashcode也要相等,如果如果你有需要要重寫equals方法,也要重寫hashCode方法(自己了解,(⊙﹏⊙))
- 在 jdk1.5 中,引入了泛型,泛型的存在是用來解決什麼問題。
解決強制轉換帶來的記憶體消耗
- 這樣的 a.hashcode() 有什麼用,與 a.equals(b)有什麼關系。
算出hashcode,比較值相等,則hashCode相等
- 有沒有可能 2 個不相等的對象有相同的 hashcode
存在
- Java 中的 HashSet 内部是如何工作的
HashSet 内部使用 HashMap 。它将元素存儲為鍵和值。(譯者注:HashSet 把存儲的值作為 key)
- 什麼是序列化,怎麼序列化,為什麼序列化,反序列化會遇到什麼問題,如何解決。
- 序列化: 将資料結構或對象轉換成二進制串的過程。
- 反序列化:将在序列化過程中所生成的二進制串轉換成資料結構或者對象的過程。
- Switch能否用string做參數?
jdk 1.7之後就加上了
- equals與==的差別。
equals比較是他們的值是否相等,==比較的是他們在記憶體中的存放位址
- Object有哪些公用方法?
toString,hashcode,equals,getClass
- Java的四種引用,強弱軟虛,用到的場景。
強引用: 如Object object = new Object() 這個Object()就是一個強引用了,如果一個對象具有強引用。垃圾回收器就不會去回收有強引用的對象
弱引用:隻要垃圾回收器在自己的記憶體空間中線程檢測到了,就會立即被回收
軟引用:如果記憶體空間足夠,那麼垃圾回收器就不會回收它
虛引用:如果一個對象隻具有虛引用,那麼它就和沒有任何引用一樣,随時會被jvm當作垃圾進行回收
//建立一個強引用Test String str = new String("Test"); //建立一個引用隊列 ReferenceQueue<string> rq = new ReferenceQueue<string>(); //實作一個軟引用,将強引用類型str和是執行個體化的rq放到軟引用實作裡面 SoftReference<string> srf = new SoftReference<string>(str,rq); //通過軟引用get方法擷取強引用中建立的記憶體空間Test值 System.out.println(srf.get()); //程式執行下gc現在jvm的記憶體空間還有很多是以gc不會回收str的對象 System.gc(); //是以這裡執行get還是會列印Test的 System.out.println(srf.get());
- Hashcode的作用。
hashCode是用來在散列存儲結構中确定對象的存儲位址的,
- hashcode是用來查找的,如果你學過資料結構就應該知道,在查找和排序這一章有
- 例如記憶體中有這樣的位置
- 0 1 2 3 4 5 6 7
- 而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用hashcode而任意存放,那麼當查找時就需要到這八個位置裡挨個去找,或者用二分法一類的算法。
- 但如果用hashcode那就會使效率提高很多。
- 我們這個類中有個字段叫ID,那麼我們就定義我們的hashcode為ID%8,然後把我們的類存放在取得得餘數那個位置。比如我們的ID為9,9除8的餘數為1,那麼我們就把該類存在1這個位置,如果ID是13,求得的餘數是5,那麼我們就把該類放在5這個位置。這樣,以後在查找該類時就可以通過ID除 8求餘數直接找到存放的位置了。
- ArrayList、LinkedList、Vector的差別。
ArrayList 和Vector是采用數組方式存儲資料,此數組元素數大于實際存儲的資料以便增加和插入元素,都允許直接序号索引元素,但是插入資料要設計到數組元素移動等記憶體操作,是以索引資料快插入資料慢,Vector由于使用了synchronized方法(線程安全)是以性能上比ArrayList要差,LinkedList使用雙向連結清單實作存儲,按序号索引資料需要進行向前或向後周遊,但是插入資料時隻需要記錄本項的前後項即可,是以插入數度較快!
- String、StringBuffer與StringBuilder的差別。
String 是不可變的對象, 是以在每次對 String 類型進行改變的時候其實都等同于生成了一個新的 String 對象,然後将指針指向新的 String 對象
StringBuffer每次結果都會對 StringBuffer 對象本身進行操作,而不是生成新的對象,再改變對象引用
該類被設計用作 StringBuffer 的一個簡易替換,用在字元串緩沖區被單個線程使用的時候,大多數實作中,它比 StringBuffer 要快。兩者的方法基本相同
- Map、Set、List、Queue、Stack的特點與用法。
Set集合類似于一個罐子,”丢進”Set集合裡的多個對象之間沒有明顯的順序。Set繼承自Collection接口,不能包含有重複元素(記住,這是整個Set類層次的共有屬性)。隻能用Lterator實作單項周遊
List集合代表一個元素有序、可重複的集合,集合中每個元素都有其對應的順序索引。List集合允許加入重複元素,因為它可以通過索引來通路指定位置的集合元素。List集合預設按元素的添加順序設定元素的索引,可以在任意位置增加删除元素。
Queue用于模拟”隊列”這種資料結構(先進先出 FIFO)。隊列的頭部儲存着隊列中存放時間最長的元素,隊列的尾部儲存着隊列中存放時間最短的元素。新元素插入(offer)到隊列的尾部,通路元素(poll)操作會傳回隊列頭部的元素,隊列不允許随機通路隊列中的元素。結合生活中常見的排隊就會很好了解這個概念
Map用于儲存具有”映射關系”的資料,是以Map集合裡儲存着兩組值,一組值用于儲存Map裡的key,另外一組值用于儲存Map裡的value。key和value都可以是任何引用類型的資料。Map的key不允許重複
Stack是Vector提供的一個子類,用于模拟”棧”這種資料結構(LIFO後進先出),它提供了通常的push和pop操作,以及取堆棧頂點的peek()方法、測試堆棧是否為空的empty方法等
- HashMap和HashTable的差別。
HashMap是非synchronized,而Hashtable是synchronized,這意味着Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正确的同步的話,多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴充性更好。
- HashMap和ConcurrentHashMap的差別,HashMap的底層源碼。
就不自己畫圖了,盜了一個圖,在這裡hashCode函數就是用于确定目前key應該放在hash桶裡面的位置,這裡hash桶可以看成是一個數組,最簡單的通過一些取餘的方法就能用來确認key應該擺放的位置,而equal函數則是為了與後面的元素之間判斷重複
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key); //擷取目前key的hash值 int i = indexFor(hash, table.length); //傳回在hash桶裡面的位置 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //周遊目前hansh桶後面的元素 Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //如果有相同的key,那麼需要替換value V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; //傳回以前的value } } modCount++; addEntry(hash, key, value, i); //放入entry return null; }
差別:
1)ConcurrentHashMap對整個桶數組進行了分段,而HashMap則沒有
2)ConcurrentHashMap在每一個分段上都用鎖進行保護,進而讓鎖的粒度更精細一些,并發性能更好,而HashMap沒有鎖機制,不是線程安全的
強烈推薦這個部落格:
[hashmap原理和concurrentHashMap]: http://blog.csdn.net/fjslovejhl/article/details/11762721
- TreeMap、HashMap、LindedHashMap的差別。
- hashmap中元素的排列順序是随機的
- TreeMap按鍵值排序,預設是按升序排序,也可以指定排序的比較器,當用Iterator 周遊TreeMap時,得到的記錄是排過序的。散列或比較函數隻能作用于鍵,與鍵關聯的值不能進行散列或 比較
- LinkedHashMap按照插入元素項的順序排序
- hashmap、hashtable性能好些,是基于hash;treemap是基于紅黑樹(一種自平衡二叉查找樹)實作的,性能較差
- HashTable,TreeMap不允許null值,key和value都不可以,HashMap允許null值,key和value都可以,但隻能由一個null值,因為hashmap如果key值相同,新的key, value将替代舊的
- Collection包結構,與Collections的差別。
Collections是提供了對集合進行操作的強大方法的工具類 ,它包含有各種有關集合操作的靜态多态方法。此類不能執行個體化
- try catch finally,try裡有return,finally還執行麼?
執行,執行時機問題。finally總會執行(除非是System.exit()),正常情況下在try後執行,抛異常時在catche後面執行
- Excption與Error包結構。OOM你遇到過哪些情況,SOF你遇到過哪些情況。
Error類和Exception類都繼承自Throwable類,Excption可以是 可被控制(checked) 或 不可控制的(unchecked) ,Error總是 不可控制的(unchecked) .
OOM(OutOfMemoryError)
- 記憶體中加載的資料量過于龐大,如一次從資料庫取出過多資料;
- 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
- 代碼中存在死循環或循環産生過多重複的對象實體;
- 使用的第三方軟體中的BUG;
- 啟動參數記憶體值設定的過小;
- 遞歸調用
- 大量循環或死循環
- 全局變量是否過多
- 數組、List、map資料過大
- Java面向對象的三個特征與含義。
封裝、繼承、多态
- 封裝把一個對象的屬性私有化,同時提供一些可以被外界通路的屬性的方法
- 繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的資料或新的功能,也可以用父類的功能,但不能選擇性地繼承父類
- 所謂多态就是指程式中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在程式設計時并不确定,而是在程式運作期間才确定
- Override和Overload的含義去差別。
- 重載 (Overload) 表示同一個類中可以有多個名稱相同的方法,但這些方法的參數清單各不相同(即參數個數、類型或順序不同)。
- 覆寫 (Override) 表示子類中的方法可以與父類中的某個方法的名稱 和參數完全相同,通過子類建立的執行個體對象調用這個方法時,将調用子類中的定義方法,這相當于把父類中定義的那 個完全相同的方法給覆寫了
- Interface與abstract類的差別。
抽象類是被用來建立繼承層級裡子類的模闆
接口是抽象方法的集合。如果一個類實作了某個接口,那麼它就繼承了這個接口的抽象方法,可以了解為接口是一種特殊的抽象類
- Static class 與non static class的差別。
靜态内部類和非靜态内部類
非靜态内部類能夠通路外部類的靜态和非靜态成員。
靜态類不能通路外部類的非靜态成員。他隻能通路外部類的靜态成員
- java多态的實作原理。
多态就是指程式中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在程式設計時并不确定,而是在程式運作期間才确定,即一個引用變量倒底會指向哪個類的執行個體對象,該引用變量發出的方法調用到底是哪個類中實作的方法,必須在由程式運作期間才能決定。因為在程式運作時才确定具體的類,這樣,不用修改源程式代碼,就可以讓引用變量綁定到各種不同的類實作上,進而導緻該引用調用的具體方法随之改變,即不修改程式代碼就可以改變程式運作時所綁定的具體代碼,讓程式可以選擇多個運作狀态,這就是多态性。
指向子類的父類引用由于向上轉型了,它隻能通路父類中擁有的方法和屬性,而對于子類中存在而父類中不存在的方法,該引用是不能使用的,盡管是重載該方法。若子類重寫了父類中的某些方法,在調用該些方法的時候,必定是使用子類中定義的這些方法(動态連接配接、動态調用)。
[多态經典解釋看這裡]: http://www.cnblogs.com/chenssy/p/3372798.html
- 實作多線程的幾種方法:
Thread與Runable
使用ExecutorService、Callable、Future實作有傳回結果的多線程。(實作Callable接口)
- 線程同步的方法
sychronized、lock、reentrantLock等。
- 鎖的等級:方法鎖、對象鎖、類鎖。
synchronized修飾方法時候是方法鎖,方法一旦執行,就獨占該鎖,直到從該方法傳回時才将鎖釋放
當一個對象中有synchronized method或synchronized block的時候調用此對象的同步方法或進入其同步區域時,就必須先獲得對象鎖。如果此對象的對象鎖已被其他調用者占用
由于一個class不論被執行個體化多少次,其中的靜态方法和靜态變量在記憶體中都隻有一份。是以,一旦一個靜态的方法被申明為synchronized。此類所有的執行個體化對象在調用此方法,共用同一把鎖,我們稱之為類鎖
- 寫出生産者消費者模式。
最簡單的是使用LinkedBlockingQueue**阻塞隊列方法**,他提供2個方法
put()方法:類似于我們上面的生産者線程,容量達到最大時,自動阻塞。
take()方法:類似于我們上面的消費者線程,容量為0時,自動阻塞。
- ThreadLocal的設計理念與作用。
ThreadLocal是Java語言提供的用于支援線程局部變量的類。所謂的線程局部變量,就是僅僅隻能被本線程通路,不能線上程之間進行共享通路的變量(每個線程一個拷貝)
- ThreadPool用法與優勢。
直接new一個,然後把你要共享的變量set進去。源碼分析:既然ThreadLocal裡面有一個Map也是用Entry來存儲對象,這裡我們看到Entry內建了WeakReference類,泛型聲明了ThreadLocal,即每一個Entry對象都保留了對 ThreadLocal執行個體的弱引用,之是以這麼幹的原因是,線程在結束之後需要将ThreadLocal執行個體從map中remove調,以便回收記憶體空間
- Concurrent包裡的其他東西
BlockingQueue、ArrayBlockingQueue(有界緩存,FIFO,支援公平通路政策)
LinkedBlockingQueue(無界)
- wait()和sleep()的差別。
Thread.sleep不會導緻鎖行為的改變,如果目前線程是擁有鎖的,那麼Thread.sleep不會讓線程釋放鎖,wait是object的一個方法,會釋放鎖
- foreach與正常for循環效率對比。
for效率最高,foreach内部是使用疊代器實作
- Java IO與NIO。
Java IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些資料被讀取,或資料完全寫入。該線程在此期間不能再幹任何事情了。 Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會擷取。而不是保持線程阻塞,是以直至資料變的可以讀取之前,該線程可以繼續做其他的事情
- 反射的作用與原理,反射建立類的3種執行個體是什麼
Java的反射機制是在編譯并不确定是哪個類被加載了,而是在程式運作的時候才加載、探知、自審
對Object進行反射,對類名(全限定名)進行反射,對class檔案進行反射
- 泛型常用特點,List能否轉為List。
可以,用泛型就可以在編譯器轉換過來,如果強制轉換會丢失了String的特性
- 解析XML的幾種方式的原理與特點:DOM、SAX、PULL。
DOM通過樹形結構存取xml文檔
SAX解析xml是基于事件流的處理方式的适合移動端
SAX解析器的工作方式是自動将事件推入注冊的事件處理器進行處理
- Java與C++對比。
Java是完全面向對象的,所有方法都必須寫在類中,java沒有指針
- Java1.7與1.8新特性。
lambda表達式,函數式程式設計
- 設計模式有哪些
單例、工廠、擴充卡、責任鍊、觀察者、代理模式等等
- JNI的使用。
NI全稱是
是在JAVA和Native層(包括但不限于C/C++)互相調用的接口規範Java Native Interface
- 寫一個Java類,在其中聲明對應要調用的native方法,用
關鍵字修飾。 比如native
private static native int native_newInstance();
- 通過
指令生成java類對應的C/C++頭檔案。javah
javah -encoding utf-8 -cp src com.young.soundtouch.SoundTouch
- 在C/C++中實作頭檔案中聲明的函數
- 編譯C/C++代碼為動态庫(Windows中的dll,linux(Android)中的so,MAC OSX中的dylib)。
- 在java代碼中加載動态庫,即可像調用Java方法一樣,調用到native函數。
- 寫一個Java類,在其中聲明對應要調用的native方法,用