1、筆試題目:html
1. 簡述類與對象的區別,Java 虛函數的做用。java
類是對象的抽象,對象是類的具體實例。
類是抽象的,不佔用內存,而對象是具體的,佔有內存空間。設計模式
java中沒有虛函數的概念,普通函數就至關於C++中的虛函數,不過能夠在函數前加final使函數不能被重寫。虛函數的做用是容許在派生類中從新定義與基類同名的函數,是多態性的一種體現。數組
2. Database table 寫SQL語句去掉重複的記錄,保留其中ID最小的一條。安全
delete from tablename where id not in (select min(id) from tablename group by col1,col2,...)多線程
3. 分析兩個for循環的優缺點:函數
(1)優化
1 for(i=0; i<N; i++) 2 { 3 if(condition) 4 DoSomething(); 5 else 6 DoOtherthing(); 7 }
(2)this
1 if(condition) 2 { 3 for(i=0; i<N; i++) 4 DoSomething(); 5 } 6 else 7 { 8 for(i=0; i<N; i++) 9 DoOtherthing(); 10 }
程序(1)(前者):
優勢:程序簡潔
條件判斷出如今for裏面,意味着,即便我在DoSomething()或DoOtherthing()這2個函數中改變了condition的值,for循環也能正確執行個人意圖,由於它在每次循環中都會從新檢測conditon的值並針對condition的值作不一樣動做,所謂以不變應萬變,這是難能難得的.
缺點:多執行了N-1次邏輯判斷,而且打斷了循環「流水線」做業,使得編譯器不能對循環進行優化處理,下降了效率。
若是condition一直不曾改變,咱們可憐的if必須每次循環都判斷一下condition的真假.犧牲了運行時效率.spa
程序(2)(後者):
優勢:循環的效率高。只進行一次判斷,運行時效率高.適合那種condition的值不會改變的狀況.
缺點:因爲只在一開始進行一次判斷,因此失去了改變condition的值的機會,也就是說,即便我在DoSomething()中改變了condition的值爲false,這個程序也不會改變它的判斷,它依然執行着DoSomething()的循環.咱們不能隨時更換咱們須要進行的動做。這是犧牲了彈性。
N較大時,建議採用後面這種寫法,因爲前者老要進行邏輯判斷,打斷了循環「流水線」做業,使得編譯器不能對循環進行優化處理,下降了效率。
4. 1-100的天然數,放到長度爲99的數組a[]中,設計一個好的方法,可以方便地找出未被放入的數。
1 int total = 0; 2 for (int i=0; i<99; i++) { 3 total += a[i]; 4 } 5 System.out.println("未被放入的數是:"+(5050-total));
其餘方法:
1).先將數組中的99個數字進行排序,而後將1-100個數字依次在數組中折半查找,能夠借用java中已有的
Arrays.binarySearch
2).其實若是數組能夠換成集合的話,能夠直接判斷集合中是否contain這1-100個數字就能夠了
3).用HashMap實現,把數字1-100都放入HashMap中,key爲數字,value隨便,好比爲1,for循環遍歷長度爲99的數組,對HashMap移除當前數字的key,最後剩下的1個數便是所求。
5. 用遞歸實現100!。
1 import java.math.BigInteger; 2 3 public class factorial { 4 5 public static BigInteger callFactorial(int n) { 6 if (n < 1) 7 throw new IllegalArgumentException(); 8 9 if (n == 1) 10 return BigInteger.ONE; 11 12 return callFactorial(n - 1).multiply(BigInteger.valueOf(n)); 13 } 14 15 public static void main(String[] args) { 16 System.out.println(callFactorial(100)); 17 } 18 }
6. 設計模式:Singleton、Factory(代碼)
第一種(懶漢,線程不安全):
1 public class Singleton { 2 private static Singleton instance; 3 4 public static Singleton getInstance() { 5 if (instance == null) { 6 instance = new Singleton(); 7 } 8 return instance; 9 } 10 }
這種寫法lazy loading很明顯,可是致命的是在多線程不能正常工做。
第二種(懶漢,線程安全):
1 public class Singleton { 2 private static Singleton instance; 3 4 public static synchronized Singleton getInstance() { 5 if (instance == null) { 6 instance = new Singleton(); 7 } 8 return instance; 9 } 10 }
這種寫法可以在多線程中很好的工做,並且看起來它也具有很好的lazy loading,可是,遺憾的是,效率很低,99%狀況下不須要同步。
第三種(餓漢):
1 public class Singleton { 2 private static Singleton instance = new Singleton(); 3 4 public static Singleton getInstance() { 5 return instance; 6 } 7 }
這種方式基於classloder機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,雖然致使類裝載的緣由有不少種,在單例模式中大多數都是調用getInstance方法, 可是也不能肯定有其餘的方式(或者其餘的靜態方法)致使類裝載,這時候初始化instance顯然沒有達到lazy loading的效果。
第四種(餓漢,變種):
1 public class Singleton { 2 private Singleton instance = null; 3 static { 4 instance = new Singleton(); 5 } 6 7 public static Singleton getInstance() { 8 return this.instance; 9 } 10 }
表面上看起來差異挺大,其實更第三種方式差很少,都是在類初始化即實例化instance。
第五種(靜態內部類):
1 public class Singleton { 2 private static class SingletonHolder { 3 private static final Singleton INSTANCE = new Singleton(); 4 } 5 6 public static final Singleton getInstance() { 7 return SingletonHolder.INSTANCE; 8 } 9 }
這種方式一樣利用了classloder的機制來保證初始化instance時只有一個線程,它跟第三種和第四種方式不一樣的是(很細微的差異):第三種和第四種方式是隻要Singleton類被裝載了,那麼instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不必定被初始化。由於SingletonHolder類沒有被主動使用,只有顯示經過調用getInstance方法時,纔會顯示裝載SingletonHolder類,從而實例化instance。想象一下,若是實例化instance很消耗資源,我想讓他延遲加載,另一方面,我不但願在Singleton類加載時就實例化,由於我不能確保Singleton類還可能在其餘的地方被主動使用從而被加載,那麼這個時候實例化instance顯然是不合適的。這個時候,這種方式相比第三和第四種方式就顯得很合理。
第六種(枚舉):
1 public enum Singleton { 2 INSTANCE; 3 public void whateverMethod() { 4 } 5 }
這種方式是Effective Java做者Josh Bloch 提倡的方式,它不只能避免多線程同步問題,並且還能防止反序列化從新建立新的對象,可謂是很堅強的壁壘啊,不過,我的認爲因爲1.5中才加入enum特性,用這種方式寫難免讓人感受生疏,在實際工做中,我也不多看見有人這麼寫過。
第七種(雙重校驗鎖):
1 public class Singleton { 2 private volatile static Singleton singleton; 3 4 public static Singleton getSingleton() { 5 if (singleton == null) { 6 synchronized (Singleton.class) { 7 if (singleton == null) { 8 singleton = new Singleton(); 9 } 10 } 11 } 12 return singleton; 13 } 14 }
這個是第二種方式的升級版,俗稱雙重檢查鎖定,詳細介紹請查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5以後,雙重檢查鎖定纔可以正常達到單例效果。
總結
有兩個問題須要注意:
1.若是單例由不一樣的類裝載器裝入,那便有可能存在多個單例類的實例。假定不是遠端存取,例如一些servlet容器對每一個servlet使用徹底不一樣的類裝載器,這樣的話若是有兩個servlet訪問一個單例類,它們就都會有各自的實例。
2.若是Singleton實現了java.io.Serializable接口,那麼這個類的實例就可能被序列化和復原。無論怎樣,若是你序列化一個單例類的對象,接下來複原多個那個對象,那你就會有多個單例類的實例。
對第一個問題修復的辦法是:
1 private static Class getClass(String classname) 2 throws ClassNotFoundException { 3 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 4 5 if(classLoader == null) 6 classLoader = Singleton.class.getClassLoader(); 7 8 return (classLoader.loadClass(classname)); 9 } 10 }
對第二個問題修復的辦法是:
1 public class Singleton implements java.io.Serializable { 2 public static Singleton INSTANCE = new Singleton(); 3 4 protected Singleton() { 5 6 } 7 private Object readResolve() { 8 return INSTANCE; 9 } 10 }
對我來講,我比較喜歡第三種和第五種方式,簡單易懂,並且在JVM層實現了線程安全(若是不是多個類加載器環境),通常的狀況下,我會使用第三種方式,只有在要明確實現lazy loading效果時纔會使用第五種方式,另外,若是涉及到反序列化建立對象時我會試着使用枚舉的方式來實現單例,不過,我一直會保證個人程序是線程安全的,並且我永遠不會使用第一種和第二種方式,若是有其餘特殊的需求,我可能會使用第七種方式,畢竟,JDK1.5已經沒有雙重檢查鎖定的問題了。
Factory模式:待續。