1、Java基礎部分
1. JAVA的基本數據類型有哪些 ? String 是否是基本數據類型 ?
2. 一個".java"源文件中是否能夠包括多個類(不是內部類)?有什麼限制?
3. Java有沒有goto?
4. 說說&和&&的區別.
5. 在JAVA中如何跳出當前的多重嵌套循環?
6. switch語句可否做用在byte上,可否做用在long上,可否做用在String上?
7. short s1 = 1; s1 = s1 + 1;有什麼錯? short s1 = 1; s1 += 1;有什麼錯?
8. char型變量中能不能存貯一箇中文漢字?爲何?
9. 用最有效率的方法算出2乘以8等於幾?
10. 請設計一個一百億的計算器
11. 使用final關鍵字修飾一個變量時,是引用不能變,仍是引用的對象不能變?
12. "=="和equals方法究竟有什麼區別?
13. 靜態變量和實例變量的區別?
14. 是否可從一個static方法內發出對非static方法的調用?
15. Integer與int的區別
16. Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
17. 下面的代碼有什麼不妥之處?
18. 請說出做用域public,private,protected,以及不寫時的區別
19. Overload和Override的區別。Overloaded的方法是否能夠改變返回值的類型?
20. 同窗貢獻的一些題?
21. 線程如何同步和通信?
22. ClassLoader如何加載class ?
23. Servlet的生命週期?
24. 抽象類的做用?
25. ArrayList如何實現插入的數據按自定義的方式有序存放?
26. 分層設計的好處?
27. 序列化接口的id有什麼用?
28. StringBuffer與StringBuilder的區別?
29. hashCode方法的做用?
30. webservice問得不少
31. 設計出計算任意正整數的階層?
32. 在oracle數據庫中須要查詢出前8條記錄的sql語句怎麼寫?
33. 什麼是SOA,談談你的SOA的理解。service orientied architecture?
34. 如何實現線程間的通信?
35. 編程題:
36. ****Spring的DI是什麼(注:除了IOC,AOP這些概念,還不太清楚DI的概念)
37. *任意數字序列「123456」之類,輸出它們全部的排列組合
38. *****什麼是AOP(注:會用,但感受說不清楚)
39. 構造器Constructor是否可被override?
40. 接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承具體類(concrete class)? 抽象類中是否能夠有靜態的main方法?
41. 寫clone()方法時,一般都有一行代碼,是什麼?
42. 面向對象的特徵有哪些方面?
43. java中實現多態的機制是什麼?
44. abstract class和interface有什麼區別?
45. abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
46. 什麼是內部類?Static Nested Class 和 Inner Class的不一樣?
47. 內部類能夠引用它的包含類的成員嗎?有沒有什麼限制?
48. Anonymous Inner Class (匿名內部類) 是否可繼承其它類,是否能夠implements接口?
49. class.forName的做用?
50. super.getClass()方法調用?
51. String是最基本的數據類型嗎?
52. String s = "Hello";s = s + " world!";這兩行代碼執行後,原始的String對象中的內容到底變了沒有?
53. 是否能夠繼承String類?
54. String s = new String("xyz");建立了幾個String Object? 兩者之間有什麼區別?
55. String 和StringBuffer的區別?
56. 如何把一段逗號分割的字符串轉換成一個數組?
57. 數組有沒有length()這個方法? String有沒有length()這個方法?
58. 下面這條語句一共建立了多少個對象:String s="a"+"b"+"c"+"d"?
59. try {}裏有一return語句,那緊跟在這個try後的finally {}裏的code是否被執行,何時被執行,在return前仍是後?
60. 下面的程序代碼輸出的結果是多少?
61. final, finally, finalize的區別。
62. 運行時異常與通常異常有何異同?
63. error和exception有什麼區別?
64. Java中的異常處理機制的簡單原理和應用。
65. 請寫出你最多見到的5個runtime exception。
66. java如何進行異常處理,throws,throw,try,catch,finally表明什麼意義?在try塊中能夠拋出異常嗎?
67. 在java中如何進行socket編程。
68. java有幾種方法可實現一個線程?用什麼關鍵字修飾同步方法? stop()和suspend()方法爲什麼不推薦使用?
69. sleep() 和 wait() 有什麼區別?
70. 同步和異步有何異同,在什麼狀況下分別使用他們?舉例說明。
71. 下面兩個方法同步嗎?(本身發明)
72. 多線程有幾種實現方法?同步有幾種實現方法?
73. 啓動一個線程是用run()仍是start()? .
74. 當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象其它方法?
75. 線程的基本概念、線程的基本狀態以及狀態之間的關係
76. 簡述synchronized和java.util.concurrent.locks.Lock的異同 ?
77. 設計4個線程,其中兩線程每次對j增長1,另外兩線程對j每次減小1。
78. 子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次,請寫出程序。
79. 介紹Collection框架的結構
80. Collection框架中實現比較要實現什麼接口
81. ArrayList和Vector的區別
82. HashMap和Hashtable的區別。
83. List 和 Map 區別?
84. List, Set, Map是否繼承自Collection接口?
85. List、Map、Set三個接口,存取元素時,各有什麼特色?
86. 說出ArrayList,Vector, LinkedList的存儲性能和特性
87. 去掉一個Vector集合中重複的元素
88. if(!newVector.contains(obj);
89. Collection 和 Collections的區別。
90. Set裏的元素是不能重複的,那麼用什麼方法來區分重複與否呢? 是用==仍是equals()? 它們有何區別?
91. 鏈表和數組的區別?
92. 你所知道的集合類都有哪些?主要方法?
93. 兩個對象值相同(x.equals(y) == true),但卻可有不一樣的hash code,這句話對不對?
94. TreeSet裏面放對象,如同時放入父類和子類實例對象,比較時使用的是父類的compareTo方法,仍是子類的compareTo方法,仍是拋異常!
95. 說出一些經常使用的類,包,接口,請各舉5個
96. java中有幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承,請說出他們分別是哪些類?
97. 字節流與字符流的區別
98. 什麼是java序列化,如何實現java序列化?或者請解釋Serializable接口的做用。
99. 描述一下JVM加載class文件的原理機制?
100. heap和stack有什麼區別。
101. GC是什麼? 爲何要有GC?
102. 垃圾回收的優勢和原理。並考慮2種回收機制。
103. 垃圾回收器的基本原理是?垃圾回收器能夠立刻回收內存嗎?如何主動通知虛擬機進行垃圾回收?
104. 何時用assert。
105. java中會存在內存泄漏嗎,請簡單描述。
106. 能不能本身寫個類,也叫java.lang.String?html
1、 Java基礎部分java
Java 有8種基本數據類型: byte int short long double float Boolean char c++
byte int short long 都屬於整數類型.git
Double float 屬於浮點類型.程序員
Boolean 爲布爾類型web
Char 爲字符型面試
String 不是基本數據類型.它定義的爲對象正則表達式
能夠有多個類,但只能有一個public的類,而且public的類名必須與文件名相一致。算法
java中的保留字,如今沒有在java中使用。spring
&和&&均可以用做邏輯與的運算符,表示邏輯與(and),當運算符兩邊的表達式的結果都爲true時,整個運算結果才爲true,不然,只要有一方爲false,則結果爲false。
&&還具備短路的功能,即若是第一個表達式爲false,則再也不計算第二個表達式,例如,對於if(str != null && !str.equals(「」))表達式,當str爲null時,後面的表達式不會執行,因此不會出現NullPointerException若是將&&改成&,則會拋出NullPointerException異常。If(x==33 & ++y>0) y會增加,If(x==33 && ++y>0)不會增加
&還能夠用做位運算符,當&操做符兩邊的表達式不是boolean類型時,&表示按位與操做,咱們一般使用0x0f來與一個整數進行&運算,來獲取該整數的最低4個bit位,例如,0x31 & 0x0f的結果爲0x01。
備註:這道題先說二者的共同點,再說出&&和&的特殊之處,並列舉一些經典的例子來代表本身理解透徹深刻、實際經驗豐富。
在Java中,要想跳出多重循環,能夠在外面的循環語句前定義一個標號,而後在裏層循環體的代碼中使用帶有標號的break 語句,便可跳出外層循環。例如,
ok:
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
System.out.println(「i=」 + i + 「,j=」 + j);
if(j == 5) break ok;
}
}
另外,我我的一般並不使用標號這種方式,而是讓外層的循環條件表達式的結果能夠受到裏層循環體代碼的控制,例如,要在二維數組中查找到某個數字。
int arr[][] = {{1,2,3},{4,5,6,7},{9}};
boolean found = false;
for(int i=0;i<arr.length && !found;i++){
for(int j=0;j<arr[i].length;j++){
System.out.println(「i=」 + i + 「,j=」 + j);
if(arr[i][j] == 5) {
found = true;
break;
}
}
}
在switch(expr1)中,expr1只能是一個整數表達式或者枚舉常量(更大字體),整數表達式能夠是int基本類型或Integer包裝類型,因爲,byte,short,char均可以隱含轉換爲int,因此,這些類型以及這些類型的包裝類型也是能夠的。顯然,long和String類型都不符合switch的語法規定,而且不能被隱式轉換成int類型,因此,它們不能做用於swtich語句中。
對於short s1 = 1; s1 = s1 + 1; 因爲s1+1運算時會自動提高表達式的類型,因此結果是int型,再賦值給short類型s1時,編譯器將報告須要強制轉換類型的錯誤。
對於short s1 = 1; s1 += 1;因爲 += 是java語言規定的運算符,java編譯器會對它進行特殊處理,所以能夠正確編譯。
char型變量是用來存儲Unicode編碼的字符的,unicode編碼字符集中包含了漢字,因此,char型變量中固然能夠存儲漢字啦。不過,若是某個特殊的漢字沒有被包含在unicode編碼字符集中,那麼,這個char型變量中就不能存儲這個特殊漢字。補充說明:unicode編碼佔用兩個字節,因此,char類型的變量也是佔用兩個字節。
2 << 3,
由於將一個數左移n位,就至關於乘以了2的n次方,那麼,一個數乘以8只要將其左移3位便可,而位運算cpu直接支持的,效率最高,因此,2乘以8等於幾的最效率的方法是2 << 3。
首先要明白這道題目的考查點是什麼,一是你們首先要對計算機原理的底層細節要清楚、要知道加減法的位運算原理和知道計算機中的算術運算會發生越界的狀況,二是要具有必定的面向對象的設計思想。
首先,計算機中用固定數量的幾個字節來存儲的數值,因此計算機中可以表示的數值是有必定的範圍的,爲了便於講解和理解,咱們先以byte 類型的整數爲例,它用1個字節進行存儲,表示的最大數值範圍爲-128到+127。-1在內存中對應的二進制數據爲11111111,若是兩個-1相加,不考慮Java運算時的類型提高,運算後會產生進位,二進制結果爲1,11111110,因爲進位後超過了byte類型的存儲空間,因此進位部分被捨棄,即最終的結果爲11111110,也就是-2,這正好利用溢位的方式實現了負數的運算。-128在內存中對應的二進制數據爲10000000,若是兩個-128相加,不考慮Java運算時的類型提高,運算後會產生進位,二進制結果爲1,00000000,因爲進位後超過了byte類型的存儲空間,因此進位部分被捨棄,即最終的結果爲00000000,也就是0,這樣的結果顯然不是咱們指望的,這說明計算機中的算術運算是會發生越界狀況的,兩個數值的運算結果不能超過計算機中的該類型的數值範圍。因爲Java中涉及表達式運算時的類型自動提高,咱們沒法用byte類型來作演示這種問題和現象的實驗,你們能夠用下面一個使用整數作實驗的例子程序體驗一下:
int a = Integer.MAX_VALUE;
int b = Integer.MAX_VALUE;
int sum = a + b;
System.out.println(「a=」+a+」,b=」+b+」,sum=」+sum);
先不考慮long類型,因爲int的正數範圍爲2的31次方,表示的最大數值約等於2*1000*1000*1000,也就是20億的大小,因此,要實現一個一百億的計算器,咱們得本身設計一個類能夠用於表示很大的整數,而且提供了與另一個整數進行加減乘除的功能,大概功能以下:
(1)這個類內部有兩個成員變量,一個表示符號,另外一個用字節數組表示數值的二進制數
(2)有一個構造方法,把一個包含有多位數值的字符串轉換到內部的符號和字節數組中
(3)提供加減乘除的功能
public class BigInteger{
int sign;
byte[] val;
public Biginteger(String val){
sign = ;
val = ;
}
public BigInteger add(BigInteger other){
}
public BigInteger subtract(BigInteger other){
}
public BigInteger multiply(BigInteger other){
}
public BigInteger divide(BigInteger other){
}
}
備註:要想寫出這個類的完整代碼,是很是複雜的,若是有興趣的話,能夠參看jdk中自帶的java.math.BigInteger類的源碼。面試的人也知道誰都不可能在短期內寫出這個類的完整代碼的,他要的是你是否有這方面的概念和意識,他最重要的仍是考查你的能力,因此,你不要由於本身沒法寫出完整的最終結果就放棄答這道題,你要作的就是你比別人寫得多,證實你比別人強,你有這方面的思想意識就能夠了,畢竟別人可能連題目的意思都看不懂,什麼都沒寫,你要勇於答這道題,即便只答了一部分,那也與那些什麼都不懂的人區別出來,拉開了距離,算是矮子中的高個,機會固然就屬於你了。另外,答案中的框架代碼也很重要,體現了一些面向對象設計的功底,特別是其中的方法命名很專業,用的英文單詞很精準,這也是能力、經驗、專業性、英語水平等多個方面的體現,會給人留下很好的印象,在編程能力和其餘方面條件差很少的狀況下,英語好除了能夠使你得到更多機會外,薪水能夠高出一千元。
使用final關鍵字修飾一個變量時,是指引用變量不能變,引用變量所指向的對象中的內容仍是能夠改變的。例如,對於以下語句:
final StringBuffer a=new StringBuffer("immutable");
執行以下語句將報告編譯期錯誤:
a=new StringBuffer("");
可是,執行以下語句則能夠經過編譯:
a.append(" broken!");
有人在定義方法的參數時,可能想採用以下形式來阻止方法內部修改傳進來的參數對象:
public void method(final StringBuffer param){
}
實際上,這是辦不到的,在該方法內部仍然能夠增長以下代碼來修改參數對象:
param.append("a");
==操做符專門用來比較兩個變量的值是否相等,也就是用於比較變量所對應的內存中所存儲的數值是否相同,要比較兩個基本類型的數據或兩個引用變量是否相等,只能用==操做符。
若是一個變量指向的數據是對象類型的,那麼,這時候涉及了兩塊內存,對象自己佔用一塊內存(堆內存),變量也佔用一塊內存,例如Objet obj = new Object();變量obj是一個內存,new Object()是另外一個內存,此時,變量obj所對應的內存中存儲的數值就是對象佔用的那塊內存的首地址。對於指向對象類型的變量,若是要比較兩個變量是否指向同一個對象,即要看這兩個變量所對應的內存中的數值是否相等,這時候就須要用==操做符進行比較。
equals方法是用於比較兩個獨立對象的內容是否相同,就比如去比較兩我的的長相是否相同,它比較的兩個對象是獨立的。例如,對於下面的代碼:
String a=new String("foo");
String b=new String("foo");
兩條new語句建立了兩個對象,而後用a,b這兩個變量分別指向了其中一個對象,這是兩個不一樣的對象,它們的首地址是不一樣的,即a和b中存儲的數值是不相同的,因此,表達式a==b將返回false,而這兩個對象中的內容是相同的,因此,表達式a.equals(b)將返回true。
在實際開發中,咱們常常要比較傳遞進行來的字符串內容是否等,例如,String input = …;input.equals(「quit」),許多人稍不注意就使用==進行比較了,這是錯誤的,隨便從網上找幾個項目實戰的教學視頻看看,裏面就有大量這樣的錯誤。記住,字符串的比較基本上都是使用equals方法。
若是一個類沒有本身定義equals方法,那麼它將繼承Object類的equals方法,Object類的equals方法的實現代碼以下:
boolean equals(Object o){
return this==o;
}
這說明,若是一個類沒有本身定義equals方法,它默認的equals方法(從Object 類繼承的)就是使用==操做符,也是在比較兩個變量指向的對象是不是同一對象,這時候使用equals和使用==會獲得一樣的結果,若是比較的是兩個獨立的對象則總返回false。若是你編寫的類但願可以比較該類建立的兩個實例對象的內容是否相同,那麼你必須覆蓋equals方法,由你本身寫代碼來決定在什麼狀況便可認爲兩個對象的內容是相同的。
語法定義上的區別:靜態變量前要加static關鍵字,而實例變量前則不加。
程序運行時的區別:實例變量屬於某個對象的屬性,必須建立了實例對象,其中的實例變量纔會被分配空間,才能使用這個實例變量。靜態變量不屬於某個實例對象,而是屬於類,因此也稱爲類變量,只要程序加載了類的字節碼,不用建立任何實例對象,靜態變量就會被分配空間,靜態變量就能夠被使用了。總之,實例變量必須建立對象後才能夠經過這個對象來使用,靜態變量則能夠直接使用類名來引用。
例如,對於下面的程序,不管建立多少個實例對象,永遠都只分配了一個staticVar變量,而且每建立一個實例對象,這個staticVar就會加1;可是,每建立一個實例對象,就會分配一個instanceVar,便可能分配多個instanceVar,而且每一個instanceVar的值都只自加了1次。
public class VariantTest{
public static int staticVar = 0;
public int instanceVar = 0;
public VariantTest(){
staticVar++;
instanceVar++;
System.out.println(「staticVar=」 + staticVar + 」,instanceVar=」 + instanceVar);
}
}
備註:這個解答除了說清楚二者的區別外,最後還用一個具體的應用例子來講明二者的差別,體現了本身有很好的解說問題和設計案例的能力,思惟敏捷,超過通常程序員,有寫做能力!
不能夠。由於非static方法是要與對象關聯在一塊兒的,必須建立一個對象後,才能夠在該對象上進行方法調用,而static方法調用時不須要建立對象,能夠直接調用。也就是說,當一個static方法被調用時,可能尚未建立任何實例對象,若是從一個static方法中發出對非static方法的調用,那個非static方法是關聯到哪一個對象上的呢?這個邏輯沒法成立,因此,一個static方法內部發出對非static方法的調用。
int是java提供的8種原始數據類型之一。Java爲每一個原始類型提供了封裝類,Integer是java爲int提供的封裝類。int的默認值爲0,而Integer的默認值爲null,即Integer能夠區分出未賦值和值爲0的區別,int則沒法表達出未賦值的狀況,例如,要想表達出沒有參加考試和考試成績爲0的區別,則只能使用Integer。在JSP開發中,Integer的默認爲null,因此用el表達式在文本框中顯示時,值爲空白字符串,而int默認的默認值爲0,因此用el表達式在文本框中顯示時,結果爲0,因此,int不適合做爲web層的表單數據的類型。
在Hibernate中,若是將OID定義爲Integer類型,那麼Hibernate就能夠根據其值是否爲null而判斷一個對象是不是臨時的,若是將OID定義爲了int類型,還須要在hbm映射文件中設置其unsaved-value屬性爲0。
另外,Integer提供了多個與整數相關的操做方法,例如,將一個字符串轉換成整數,Integer中還定義了表示整數的最大值和最小值的常量。
Math類中提供了三個與取整有關的方法:ceil、floor、round,這些方法的做用與它們的英文名稱的含義相對應,例如,ceil的英文意義是天花板,該方法就表示向上取整,Math.ceil(11.3)的結果爲12,Math.ceil(-11.3)的結果是-11;floor的英文意義是地板,該方法就表示向下取整,Math.ceil(11.6)的結果爲11,Math.ceil(-11.6)的結果是-12;最難掌握的是round方法,它表示「四捨五入」,算法爲Math.floor(x+0.5),即將原來的數字加上0.5後再向下取整,因此,Math.round(11.5)的結果爲12,Math.round(-11.5)的結果爲-11。
1. if(username.equals(「zxx」){}
2.int x = 1;
return x==1?true:false;
說明:若是在修飾的元素上面沒有寫任何訪問修飾符,則表示friendly。
做用域 當前類 同一package子孫類 其餘package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
備註:只要記住了有4種訪問權限,4個訪問範圍,而後將全選和範圍在水平和垂直方向上分別按排從小到大或從大到小的順序排列,就很容易畫出上面的圖了。
Overload是重載的意思,Override是覆蓋的意思,也就是重寫。
重載Overload表示同一個類中能夠有多個名稱相同的方法,但這些方法的參數列表各不相同(即參數個數或類型不一樣)。
重寫Override表示子類中的方法能夠與父類中的某個方法的名稱和參數徹底相同,經過子類建立的實例對象調用這個方法時,將調用子類中的定義方法,這至關於把父類中定義的那個徹底相同的方法給覆蓋了,這也是面向對象編程的多態性的一種表現。子類覆蓋父類的方法時,只能比父類拋出更少的異常,或者是拋出父類拋出的異常的子異常,由於子類能夠解決父類的一些問題,不能比父類有更多的問題。子類方法的訪問權限只能比父類的更大,不能更小。若是父類的方法是private類型,那麼,子類則不存在覆蓋的限制,至關於子類中增長了一個全新的方法。
至於Overloaded的方法是否能夠改變返回值的類型這個問題,要看你倒底想問什麼呢?這個題目很模糊。若是幾個Overloaded的方法的參數列表不同,它們的返回者類型固然也能夠不同。但我估計你想問的問題是:若是兩個方法的參數列表徹底同樣,是否可讓它們的返回值不一樣來實現重載Overload。這是不行的,咱們能夠用反證法來講明這個問題,由於咱們有時候調用一個方法時也能夠不定義返回結果變量,即不要關心其返回結果,例如,咱們調用map.remove(key)方法時,雖然remove方法有返回值,可是咱們一般都不會定義接收返回結果的變量,這時候假設該類中有兩個名稱和參數列表徹底相同的方法,僅僅是返回類型不一樣,java就沒法肯定編程者倒底是想調用哪一個方法了,由於它沒法經過返回結果類型來判斷。
override能夠翻譯爲覆蓋,從字面就能夠知道,它是覆蓋了一個方法而且對其重寫,以求達到不一樣的做用。對咱們來講最熟悉的覆蓋就是對接口方法的實現,在接口中通常只是對方法進行了聲明,而咱們在實現時,就須要實現接口聲明的全部方法。除了這個典型的用法之外,咱們在繼承中也可能會在子類覆蓋父類中的方法。在覆蓋要注意如下的幾點:
一、覆蓋的方法的標誌必需要和被覆蓋的方法的標誌徹底匹配,才能達到覆蓋的效果;
二、覆蓋的方法的返回值必須和被覆蓋的方法的返回一致;
三、覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致,或者是其子類;
四、被覆蓋的方法不能爲private,不然在其子類中只是新定義了一個方法,並無對其進行覆蓋。
overload對咱們來講可能比較熟悉,能夠翻譯爲重載,它是指咱們能夠定義一些名稱相同的方法,經過定義不一樣的輸入參數來區分這些方法,而後再調用時,VM就會根據不一樣的參數樣式,來選擇合適的方法執行。在使用重載要注意如下的幾點:
一、在使用重載時只能經過不一樣的參數樣式。例如,不一樣的參數類型,不一樣的參數個數,不一樣的參數順序(固然,同一方法內的幾個參數類型必須不同,例如能夠是fun(int,float),可是不能爲fun(int,int));
二、不能經過訪問權限、返回類型、拋出的異常進行重載;
三、方法的異常類型和數目不會對重載形成影響;
四、對於繼承來講,若是某一方法在父類中是訪問權限是priavte,那麼就不能在子類對其進行重載,若是定義的話,也只是定義了一個新方法,而不會達到重載的效果。
1.搞了多個重載方法,參數分別是int ,char,和double,而後將double x = 2,傳遞進去,會選擇哪一個方法?
2.說說對javaee中的session的理解,你是怎麼用session的?cvs/svn下載
3.jdk中哪些類是不能繼承的:System,String,StringBuffer等。
4.在eclipse中調試時,怎樣查看一個變量的值。
5.判斷身份證:要麼是15位,要麼是18位,最後一位能夠爲字母,並寫程序提出其中的年月日。
6.一個房子裏有椅子,椅子有腿和背,房子與椅子是什麼關係,椅子與腿和背是什麼關係?
7.若是房子有多個椅子,就是聚合關係,不然是一種關聯關係,固然,聚合是一種特殊的關聯。椅子與腿和背時組合關係。
8.說說has a與is a的區別。
9.工廠模式的類圖
同窗回答說synchronized方法或代碼塊!面試官彷佛不太滿意!
只有多個synchronized代碼塊使用的是同一個監視器對象,這些synchronized代碼塊之間才具備線程互斥的效果,假如a代碼塊用obj1做爲監視器對象,假如b代碼塊用obj2做爲監視器對象,那麼,兩個併發的線程能夠同時分別進入這兩個代碼塊中。 …這裏還能夠分析一下同步的原理。
對於同步方法的分析,所用的同步監視器對象是this
接着對於靜態同步方法的分析,所用的同步監視器對象是該類的Class對象
接着對如何實現代碼塊與方法的同步進行分析。
jvm裏有多個類加載,每一個類加載能夠負責加載特定位置的類,例如,bootstrap類加載負責加載jre/lib/rt.jar中的類, 咱們平時用的jdk中的類都位於rt.jar中。extclassloader負責加載jar/lib/ext/*.jar中的類,appclassloader負責classpath指定的目錄或jar中的類。除了bootstrap以外,其餘的類加載器自己也都是java類,它們的父類是ClassLoader。
Servlet被服務器實例化後,容器運行其init方法,請求到達時運行其service方法,service方法自動派遣運行與請求對應的doXXX方法(doGet,doPost)等,當服務器決定將實例銷燬的時候調用其destroy方法。
與cgi的區別在於servlet處於服務器進程中,它經過多線程方式運行其service方法,一個實例能夠服務於多個請求,而且其實例通常不會銷燬,而CGI對每一個請求都產生新的進程,服務完成後就銷燬,因此效率上低於servlet
class MyBean implements Comparable{
public int compareTo(Object obj){
if(! obj instanceof MyBean)
throw new ClassCastException() //具體異常的名稱,我要查jdk文檔。
MyBean other = (MyBean) obj;
return age > other.age?1:age== other.age?0:-1;
}
}
class MyTreeSet {
private ArrayList datas = new ArrayList();
public void add(Object obj){
for(int i=0;i<datas.size();i++){
if(obj.compareTo(datas.get(i) != 1){
datas.add(i,obj);
}
}
}
}
把各個功能按調用流程進行了模塊化,模塊化帶來的好處就是能夠隨意組合,舉例說明:若是要註冊一個用戶,流程爲顯示界面並經過界面接收用戶的輸入,接着進行業務邏輯處理,在處理業務邏輯又訪問數據庫,若是咱們將這些步驟所有按流水賬的方式放在一個方法中編寫,這也是能夠的,但這其中的壞處就是,當界面要修改時,因爲代碼全在一個方法內,可能會碰壞業務邏輯和數據庫訪問的碼,一樣,當修改業務邏輯或數據庫訪問的代碼時,也會碰壞其餘部分的代碼。分層就是要把界面部分、業務邏輯部分、數據庫訪問部分的代碼放在各自獨立的方法或類中編寫,這樣就不會出現牽一髮而動全身的問題了。這樣分層後,還能夠方便切換各層,譬如原來的界面是Swing,如今要改爲BS界面,若是最初是按分層設計的,這時候不須要涉及業務和數據訪問的代碼,只需編寫一條web界面就能夠了。
下面的僅供參考,不建議照搬照套,必定要改爲本身的語言,發現心裏的感覺:
分層的好處:1.實現了軟件之間的解耦;2.便於進行分工;3.便於維護;4.提升軟件組件的重用;5.便於替換某種產品,好比持久層用的是hibernate,須要更換產品用toplink,就不用其餘業務代碼,直接把配置一改;6.便於產品功能的擴展;7.便於適用用戶需求的不斷變化
對象常常要經過IO進行傳送,讓你寫程序傳遞對象,你會怎麼作?把對象的狀態數據用某種格式寫入到硬盤,Person->「zxx,male,28,30000」àPerson,既然你們都要這麼幹,而且沒有個統一的幹法,因而,sun公司就提出一種統一的解決方案,它會把對象變成某個格式進行輸入和輸出,這種格式對程序員來講是透明(transparent)的,可是,咱們的某個類要想能被sun的這種方案處理,必須實現Serializable接口。
ObjectOutputStream.writeObject(obj);
Object obj = ObjectInputStream.readObject();
假設兩年前我保存了某個類的一個對象,這兩年來,我修改該類,刪除了某個屬性和增長了另一個屬性,兩年後,我又去讀取那個保存的對象,或有什麼結果?未知!sun的jdk就會蒙了。爲此,一個解決辦法就是在類中增長版本後,每一次類的屬性修改,都應該把版本號升級一下,這樣,在讀取時,比較存儲對象時的版本號與當前類的版本號,若是不一致,則直接報版本號不一樣的錯!
由於StringBuilder sbuilder = ;是線程不安全的,運行效率高,若是一個字符串變量是在方法裏面定義,這種狀況只可能有一個線程訪問它,不存在不安全的因素了,則用StringBuilder。若是要在類裏面定義成員變量,而且這個類的實例對象會在多線程環境下使用,那麼最好用StringBuffer。
首先,想要明白hashCode的做用,你必需要先知道Java中的集合。
總的來講,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。你知道它們的區別嗎?前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複。那麼這裏就有一個比較嚴重的問題了:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?這就是Object.equals方法了。可是,若是每增長一個元素就檢查一次,那麼當元素不少時,後添加到集合中的元素比較的次數就很是多了。也就是說,若是集合中如今已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大下降效率。因而,Java採用了哈希表的原理。能夠簡單理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並非)。這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。因此這裏存在一個衝突解決的問題。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。
因此,Java對於eqauls方法和hashCode方法是這樣規定的:
一、若是兩個對象相同,那麼它們的hashCode值必定要相同;
二、若是兩個對象的hashCode相同,它們並不必定相同
上面說的對象相同指的是用eqauls方法比較。
你固然能夠不按要求去作了,但你會發現,相同的對象能夠出如今Set集合中。同時,增長新元素的效率會大大降低。
SOA是指爲了解決在Internet環境下業務集成的須要,經過鏈接能完成特定任務的獨立功能實體實現的一種軟件系統架構。
1) 軟件系統架構:SOA不是一種語言,也不是一種具體的技術而是一種軟件系統架構,它嘗試給出在特定環境下推薦採用的一種架構,從這個角度上來講,它更像一種模式(Pattern)。所以它與不少已有的軟件技術好比面向對象技術,是互補的而非互斥的。它們分別面向不一樣的應用場景,用來知足不一樣的特定需求。
2) SOA的使用範圍:需求決定同時也限制功能。SOA並非包治百病的萬靈丹,它最主要的應用場合在於解決在Internet環境下的不一樣商業應用之間的業務集成問題。
在Java 語言中,提供了各類各樣的輸入輸出流(stream),使咱們可以很方便的對數據進行操做,其中,管道(pipe)流是一種特殊的流,用於在不一樣線程(threads)間直接傳送數據。一個線程發送數據到輸出管道,另外一個線程從輸入管道中讀數據。經過使用管道,實現不一樣線程間的通信。無需求助於相似臨時文件之類的東西。本文在簡要介紹管道的基本概念後,將以一個具體的實例pipeapp加以詳細說明。
1.管道的建立與使用
Java提供了兩個特殊的專門的類專門用於處理管道,它們就是pipedinputstream類和pipeoutputstream類。
Pipedinputstream表明了數據在管道中的輸出端,也就是線程向管道讀數據的一端;pipeoutputstream表明了數據在管道中的輸入端,也就是線程向管道寫數據的一端,這兩個類一塊兒使用能夠提供數據的管道流。
爲了建立一個管道流,咱們必須首先建立一個pipeoutstream對象,而後,建立pipeinputstream對象,實例以下:
pipeout= new pipedyoutstream();
pipein= new pipedputsteam(pipepout);
一旦建立了一個管道後,就能夠象操做文件同樣對管道進行數據的讀寫。
2.演示程序: pipeapp
應用程序由三個程序組成:主線程(pipeapp.Java)及由主線程啓動的兩個二級線程(ythread.Java和zthread.Java),它們使用管道來處理數據。程序從一個內容爲一行一行"x"字母的"input.txt"文件中讀取數據,使用管道傳輸數據,第一次是利用線程ythread將數據"x"轉換爲"y",最後利用線程zthread將"y"轉換爲"z",以後,程序在屏幕上顯示修改後的數據。
主線程 (pipeapp.Java)
在main()方法中,程序首先建立一個應用對象:pipeapp pipeapp=new pipeapp();
因爲程序中流操做都須要使用IOException異常處理,因此設置了一個try塊。在try中,爲了從源文件中讀取數據,程序爲"input.txt"文件建立了一個輸入流Xfileln,:
fileinputstream xfileln= new fileinputstream("input.txt");
新的輸入流傳遞給changetoy()方法,讓線程ythread能讀取該文件:
inputstream ylnpipe =pipeapp.changetoy(xfileln);
changetoy()方法建立將輸入數據"x"改變到"y"的線程ythread,並返回該線程的輸入管道:
inputstream zlnpipe = pipeapp.changetoz(ylnpipe);
changetoz()方法啓動將數據從"y"改變到"z"的線程zehread,主程序將使用從changetoz()返回的輸入管道。獲得以修改的數據。
而後,程序將管道輸入流定位到datainputstream對象,使程序可以使用readline()方法讀取數據:
datainputstream inputstream = new datainputstream(zlnpiepe);
建立了輸入流之後,程序就能夠以行一行的讀取數據病顯示在屏幕上。
String str= inputstream.readline();
While(str!=null){
system.out.println(str);
str=inputstream.readline();
}
顯示完成以後,程序關閉輸入流:
inputstream.close();
changetoy()方法
changetoy()方法首先經過傳遞一個參數inputstream給datainputstream對象來定位資源的輸入流,使程序能使用readline()方法從流中讀取數據:
datainputstream xfileln =new datainutstream(inputstream);
而後,changetoy()建立輸出管道和輸入管道:
pipeoutstream pipeout = new pipeoutputstream();
pipeinputstream pipeln = new pipedinputsteam(pipeout);
爲了可以使用println()方法輸出修改的後的文本行到管道,程序將輸出管道定位到printstream對象:
printstream printstream = new printstream(pipeout);
如今,程序能夠建立將數據從x改變到y的線程,該線程是ythread類的一個對象,他傳遞兩個參數:輸入文件(xfileln)和輸出管道(調用printstream)
ythread ythread =new thread(xfileln,printstream);
以後,程序啓動線程:
changetoz()方法
changetoz()方法與changetoy()方法很類似,他從changetoy()返回的輸入流開始:
datainputstream yfileln= new datainputstream(inputstream);
程序建立一個新的管道:
pipedoutstream pipeout2 = new pipedoutputstream();
pipedinputstream pipeln2 = new pipedinputsream(pipeout2);
該線程經過這個新的管道發出修改後的數據(輸入流pipeln2)給主程序。
源程序以下:
//
//pipeapp.Java-pipeapp的主應用程序
//
import Java.io.*
class pipeapp{
public static void main(string[] args){
pipeapp pipeapp=new pipeapp();
try{
fileinputstream xfile =new fileinputstream("input.txt");
inputstream ylnpipe = pipeapp.changetoy(xfileln);
inputstream zlnpipe=pipeapp.changetoz(ylnpipe);
system.out.println();
system.out.println("here are the results");
system.out.pringln();
datainputstream inputstream = nes datainputstream(zlnpipe);
string str = inputstream.readline();
while (str!=null){
system.out.println(str);
str=inputstream.readline();
}
inputstream.close();
}
catch(exception e){
system.out.println(e.tostring());
}
}
public inputstream changetoy(inputstream inputstream){
try{
datainputstream pipeout = new datainputsteam(inputstream);
pipedoutstream pipeout = new pipedoutputstream();
pipedlnsteam pipeln = new pipedlnputstream(pipeout);
printstream printstream = new printstream(pipeout);
ythread ythread = new ythread(xfileln,printstream);
ythread.start();
return pipeln;
}
catch(exeption e){
system.out.println(x.tostring());
}
return null;
}
public inputstream changetoz(inputstream inputsteam){
try{
datainputstream yfileln = new datainputstream(inputstream);
pipeoutputstream pipeln2 = new pipedinputstream(pipeout2);
printrstream printstream2 = new printsteam(pipeout2);
zthread zthread = new zthread(yfileln,printstream2);
zthread.start();
return pipeln2;
}
catch(exception e){
system.out.println(e.tostring());
}
return null;
}
}
Ythread類和Zthread類
因爲ythread類與zthread類基本同樣,在此僅以ythread爲例加以說明。
Ythread的構造器接收兩個參數:輸入的文件和第一個管道的輸出端,構造器存儲這兩個參數做爲類的數據成員:
Ythread(datainputstream xfileln,pringstream printstream){
this.xfileln = xfileln;
this.printstream = printstream;
}
線程經過run()方法來處理數據。首先讀取一行數據,確保xstring不爲空的狀況下循環執行:
string xstring = xfileln.readline();
每讀一行數據,完成一次轉換
string ystring = xstring.replace('x','y');
而後將修改後的數據輸出到管道的輸出端:
prinstream.prinrln(ystring);
爲了確保全部緩衝區的數據徹底進入管道的輸出端:
pringstram.flush();
循環完成後,線程關閉管道輸出流:
pringstram.close();
ythread類的源程序以下:
//
//ythread.Java
//
import Java.io.*;
class ythread exteads thread{
datainputstream xfileln;
pringstream printstream;
ythread(datainputstream xfileln,pringstream.printstream){
this.xfileln = xfileln;
this.printstream = printstream;
}
public void run(){
try{
string xstring = xfileln.readline();
while(xstring!=null){
string ystring= xstring.replace('x','y');
printstream.pringln(ystring);
printstream.flush();
xstring= xfileln.readline();
}
printstream.close();
}
catch{ioexception e}{
system.out.println(e.tostring());
}
}
}
編程1.編寫一個函數將一個十六進制數的字符串參數轉換成整數返回。
String str = 「13abf」;
int len = str.length;
int sum = 0;
for(int i=0;i<len;i++){
char c = str.charAt(len-1-i);
int n = Character.digit(c,16);
sum += n * (1<<(4*i));
}
其實,也能夠用Integer.parseInt(str,16),但面試官極可能是想考咱們的編碼基本功。
編程2 :銀行貸款的還款方式中最經常使用的是一種叫「等額本息」,還款法,即借款人在約定還款期限內的每一期(月)歸還的金額(產生的利息+部分本金)都是相等的,現有一筆總額爲T元的N年期住房貸款,年利率爲R,要求算出每一期的還款的本金和利息總額,請寫出解決思路和任意一種編程語言實現的主要代碼。
思路:既然是按月還款,那我就要將N年按月來計算,即要還N*12個月,這樣就能夠求出每個月要還的本金。因爲每個月要還的那部分本金所欠的時間不一樣,因此,它們所產生的利息是不一樣的,該部分本金的利息爲:部分本金額*所欠月數*月利率。應該是這麼個算法,若是利息還計利息,若是月還款不按年利率來算,老百姓算不明白的。
int monthMoney = T/N/12;
float monthRate = R/12;
int totalMonth = N * 12;
float totalRate = 0;
for(int i=1;i<=totalMonth;i++){
totalRate += monthMoney * monthRate * i;
}
int result = monthMoney + totalRate/N/12;
無論是面向對象,仍是面向過程,都須要分紅許多的塊,而後由這些部件協同工做完成任務
要協同工做就會產生依賴,一個方法調用另外一個方法,一個對象包含另外一個對象
若是對象A包含對象B的話,就須要在A裏new一個B
依賴注入從具體類B裏抽象出接口IB——IB的具體實現可能有不少B,B1,B2...不少種——這樣A能夠不用再new具體的B了,而是跟IoC容器說:我要一個IB(getBean("IB"))。而後,由容器根據配置文件來作具體的new的工做。具體new的是哪一個,由配置文件從代碼外部決定,要更換成B,B1,或是B2...修改配置文件就能作到,不用再改代碼了
AOP: 面向切面編程:Aspect Oriented Programming
AOP是OOP的延續,是(Aspect Oriented Programming)的縮寫,意思是面向切面編程。
主要的功能是:日誌記錄,性能統計,安全控制,事務處理,異常處理等等。
主要的意圖是:將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。
能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP能夠說也是這種目標的一種實現。
在Spring中提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。
應用舉例:
假設有在一個應用系統中,有一個共享的數據必須被併發同時訪問,首先,將這個數據封裝在數據對象中,稱爲Data Class,同時,將有多個訪問類,專門用於在同一時刻訪問這同一個數據對象。
爲了完成上述併發訪問同一資源的功能,須要引入鎖Lock的概念,也就是說,某個時刻,當有一個訪問類訪問這個數據對象時,這個數據對象必須上鎖Locked,用完後就當即解鎖unLocked,再供其它訪問類訪問。
使用傳統的編程習慣,咱們會建立一個抽象類,全部的訪問類繼承這個抽象父類,以下:
abstract class Worker{
abstract void locked();
abstract void accessDataObject();
abstract void unlocked();
}
我注:由上面這些題,能夠看出,思想很重要,只有琢磨思想和原理的人才能很好地回答這些問題!
2題的答案:
String str = 「xafdvs」;
char[] arr1 = str.toCharArray();
char[] arr2 = Arrays.copyOf(arr1,arr1.length);
for(int i=0;i<arr1.length-1;i++){
for(int j = i+1;j<arr2.length;j++){
syso: arr1[i] + 「,」 + arr2[j];
}
}
3.題的答案:
1.概念介紹:所謂AOP,即Aspect orientied program,就是面向方面的編程,
2.解釋什麼是方面:貫穿到系統的各個模塊中的系統一個功能就是一個方面,
好比,記錄日誌,統一異常處理,事務處理,全限檢查,這些功能都是軟件系統
的一個面,而不是一點,在各個模塊中都要出現。
3.什麼是面向方面編程:把系統的一個方面的功能封裝成對象的形式來處理
4.怎麼進行面向方面編程:把功能模塊對應的對象做爲切面嵌入到原來的各個系統模塊中,
採用代理技術,代理會調用目標,同時把切面功能的代碼(對象)加入進來,因此,
用spring配置代理對象時只要要配兩個屬性,分別表示目標和切面對象(Advisor)。
構造器Constructor不能被繼承,所以不能重寫Override,但能夠被重載Overload。
接口能夠繼承接口。抽象類能夠實現(implements)接口,抽象類是否可繼承具體類。抽象類中能夠有靜態的main方法。
備註:只要明白了接口和抽象類的本質和做用,這些問題都很好回答,你想一想,若是你是java語言的設計者,你是否會提供這樣的支持,若是不提供的話,有什麼理由嗎?若是你沒有道理不提供,那答案就是確定的了。
只有記住抽象類與普通類的惟一區別就是不能建立實例對象和容許有abstract方法。
clone 有缺省行爲,super.clone();由於首先要把父類中的成員複製到位,而後纔是複製本身的成員。
面向對象是一種程序的設計方法,或者說它是一種程序設計範型,其基本思想是使用對象,類,繼承,封裝,消息等基本概念來進行程序設計。
它是從現實世界中客觀存在的事物(即對象)出發來構造軟件系統,並在系統構造中儘量運用人類的天然思惟方式,強調直接以問題域(現實世界)中的事物爲中心來思考問題,認識問題,並根據這些事物的本質特色,把它們抽象地表示爲系統中的對象,做爲系統的基本構成單位(而不是用一些與現實世界中的事物相關比較遠,而且沒有對應關係的其它概念來構造系統)。這能夠使系統直接地映射問題域,保持問題域中事物及其相互關係的原本面貌。
它能夠有不一樣層次的理解:
從世界觀的角度能夠認爲:面向對象的基本哲學是認爲世界是由各類各樣具備本身的運動規律和內部狀態的對象所組成的;不一樣對象之間的相互做用和通信構成了完整的現實世界。所以,人們應當按照現實世界這個原本面貌來理解世界,直接經過對象及其相互關係來反映世界。這樣創建起來的系統才能符合現實世界的原本面目。
從方法學的角度能夠認爲:面向對象的方法是面向對象的世界觀在開發方法中的直接運用。它強調系統的結構應該直接與現實世界的結構相對應,應該圍繞現實世界中的對象來構造系統,而不是圍繞功能來構造系統。
從程序設計的角度來看,面向對象的程序設計語言必須有描述對象及其相互之間關係的語言成分。這些程序設計語言能夠概括爲如下幾類:系統中一切皆爲對象;對象是屬性及其操做的封裝體;對象可按其性質劃分爲類,對象成爲類的實例;實例關係和繼承關係是對象之間的靜態關係;消息傳遞是對象之間動態聯繫的惟一形式,也是計算的惟一形式;方法是消息的序列。
面向對象的編程語言有封裝、繼承 、抽象、多態等4個主要的特徵。
1.封裝:
封裝是保證軟件部件具備優良的模塊性的基礎,封裝的目標就是要實現軟件部件的「高內聚、低耦合」,防止程序相互依賴性而帶來的變更影響。在面向對象的編程語言中,對象是封裝的最基本單位,面向對象的封裝比傳統語言的封裝更爲清晰、更爲有力。面向對象的封裝就是把描述一個對象的屬性和行爲的代碼封裝在一個「模塊」中,也就是一個類中,屬性用變量定義,行爲用方法進行定義,方法能夠直接訪問同一個對象中的屬性。一般狀況下,只要記住讓變量和訪問這個變量的方法放在一塊兒,將一個類中的成員變量所有定義成私有的,只有這個類本身的方法才能夠訪問到這些成員變量,這就基本上實現對象的封裝,就很容易找出要分配到這個類上的方法了,就基本上算是會面向對象的編程了。把握一個原則:把對同一事物進行操做的方法和相關的方法放在同一個類中,把方法和它操做的數據放在同一個類中。
例如,人要在黑板上畫圓,這一共涉及三個對象:人、黑板、圓,畫圓的方法要分配給哪一個對象呢?因爲畫圓須要使用到圓心和半徑,圓心和半徑顯然是圓的屬性,若是將它們在類中定義成了私有的成員變量,那麼,畫圓的方法必須分配給圓,它才能訪問到圓心和半徑這兩個屬性,人之後只是調用圓的畫圓方法、表示給圓發給消息而已,畫圓這個方法不該該分配在人這個對象上,這就是面向對象的封裝性,即將對象封裝成一個高度自治和相對封閉的個體,對象狀態(屬性)由這個對象本身的行爲(方法)來讀取和改變。一個更便於理解的例子就是,司機將火車剎住了,剎車的動做是分配給司機,仍是分配給火車,顯然,應該分配給火車,由於司機自身是不可能有那麼大的力氣將一個火車給停下來的,只有火車本身才能完成這一動做,火車須要調用內部的離合器和剎車片等多個器件協做才能完成剎車這個動做,司機剎車的過程只是給火車發了一個消息,通知火車要執行剎車動做而已。
2.抽象:
抽象就是找出一些事物的類似和共性之處,而後將這些事物歸爲一個類,這個類只考慮這些事物的類似和共性之處,而且會忽略與當前主題和目標無關的那些方面,將注意力集中在與當前目標有關的方面。例如,看到一隻螞蟻和大象,你可以想象出它們的相同之處,那就是抽象。抽象包括行爲抽象和狀態抽象兩個方面。例如,定義一個Person類,以下:
class Person{
String name;
int age;
}
人原本是很複雜的事物,有不少方面,但由於當前系統只須要了解人的姓名和年齡,因此上面定義的類中只包含姓名和年齡這兩個屬性,這就是一種抽像,使用抽象能夠避免考慮一些與目標無關的細節。我對抽象的理解就是不要用顯微鏡去看一個事物的全部方面,這樣涉及的內容就太多了,而是要善於劃分問題的邊界,當前系統須要什麼,就只考慮什麼。
3.繼承:
在定義和實現一個類的時候,能夠在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的內容做爲本身的內容,並能夠加入若干新的內容,或修改原來的方法使之更適合特殊的須要,這就是繼承。繼承是子類自動共享父類數據和方法的機制,這是類之間的一種關係,提升了軟件的可重用性和可擴展性。
4.多態:
多態是指程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編程時並不肯定,而是在程序運行期間才肯定,即一個引用變量倒底會指向哪一個類的實例對象,該引用變量發出的方法調用究竟是哪一個類中實現的方法,必須在由程序運行期間才能決定。由於在程序運行時才肯定具體的類,這樣,不用修改源程序代碼,就可讓引用變量綁定到各類不一樣的類實現上,從而致使該引用調用的具體方法隨之改變,即不修改程序代碼就能夠改變程序運行時所綁定的具體代碼,讓程序能夠選擇多個運行狀態,這就是多態性。多態性加強了軟件的靈活性和擴展性。例如,下面代碼中的UserDao是一個接口,它定義引用變量userDao指向的實例對象由daofactory.getDao()在執行的時候返回,有時候指向的是UserJdbcDao這個實現,有時候指向的是UserHibernateDao這個實現,這樣,不用修改源代碼,就能夠改變userDao指向的具體類實現,從而致使userDao.insertUser()方法調用的具體代碼也隨之改變,即有時候調用的是UserJdbcDao的insertUser方法,有時候調用的是UserHibernateDao的insertUser方法:
UserDao userDao = daofactory.getDao();
userDao.insertUser(user);
比喻:人吃飯,你看到的是左手,仍是右手?
靠的是父類或接口定義的引用變量能夠指向子類或具體實現類的實例對象,而程序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。
含有abstract修飾符的class即爲抽象類,abstract 類不能建立的實例對象。含有abstract方法的類必須定義爲abstract class,abstract class類中的方法沒必要是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,因此,不能有抽象構造方法或抽象靜態方法。若是子類沒有實現抽象父類中的全部抽象方法,那麼子類也必須定義爲abstract類型。
接口(interface)能夠說成是抽象類的一種特例,接口中的全部方法都必須是抽象的。接口中的方法定義默認爲public abstract類型,接口中的成員變量類型默認爲public static final。
下面比較一下二者的語法區別:
1.抽象類能夠有構造方法,接口中不能有構造方法。
2.抽象類中能夠有普通成員變量,接口中沒有普通成員變量
3.抽象類中能夠包含非抽象的普通方法,接口中的全部方法必須都是抽象的,不能有非抽象的普通方法。
4.抽象類中的抽象方法的訪問類型能夠是public,protected和(默認類型,雖然eclipse下不報錯,但應該也不行),但接口抽象方法只能是public類型的,而且默認即爲public abstract類型。
5.抽象類中能夠包含靜態方法,接口中不能包含靜態方法
6.抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任意,但接口中定義的變量只能是public static final類型,而且默認即爲public static final類型。
7.一個類能夠實現多個接口,但只能繼承一個抽象類。
下面接着再說說二者在應用上的區別:
接口更多的是在系統架構設計方法發揮做用,主要用於定義模塊之間的通訊契約。而抽象類在代碼實現方面發揮做用,能夠實現代碼的重用,例如,模板方法設計模式是抽象類的一個典型應用,假設某個項目的全部Servlet類都要用相同的方式進行權限判斷、記錄訪問日誌和處理異常,那麼就能夠定義一個抽象的基類,讓全部的Servlet都繼承這個抽象基類,在抽象基類的service方法中完成權限判斷、記錄訪問日誌和處理異常的代碼,在各個子類中只是完成各自的業務邏輯代碼,僞代碼以下:
public abstract class BaseServlet extends HttpServlet{
public final void service(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException{
記錄訪問日誌
進行權限判斷
if(具備權限){
try{
doService(request,response);
}
catch(Excetpion e){
記錄異常信息
}
}
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException;
//注意訪問權限定義成protected,顯得既專業,又嚴謹,由於它是專門給子類用的
}
public class MyServlet1 extends BaseServlet{
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOExcetion,ServletException{
本Servlet只處理的具體業務邏輯代碼
}
}
父類方法中間的某段代碼不肯定,留給子類幹,就用模板方法設計模式。
備註:這道題的思路是先從整體解釋抽象類和接口的基本概念,而後再比較二者的語法細節,最後再說二者的應用區別。比較二者語法細節區別的條理是:先從一個類中的構造方法、普通成員變量和方法(包括抽象方法),靜態變量和方法,繼承性等6個方面逐一去比較回答,接着從第三者繼承的角度的回答,特別是最後用了一個典型的例子來展示本身深厚的技術功底。
abstract的method 不能夠是static的,由於抽象的方法是要被子類實現的,而static與子類扯不上關係!
native方法表示該方法要用另一種依賴平臺的編程語言實現的,不存在着被子類實現的問題,因此,它也不能是抽象的,不能與abstract混用。例如,FileOutputSteam類要和硬件打交道,底層的實現用的是操做系統相關的api實現,例如,在windows用c語言實現的,因此,查看jdk 的源代碼,能夠發現FileOutputStream的open方法的定義以下:
private native void open(String name) throws FileNotFoundException;
若是咱們要用java調用別人寫的c語言函數,咱們是沒法直接調用的,咱們須要按照java的要求寫一個c語言的函數,又咱們的這個c語言函數去調用別人的c語言函數。因爲咱們的c語言函數是按java的要求來寫的,咱們這個c語言函數就能夠與java對接上,java那邊的對接方式就是定義出與咱們這個c函數相對應的方法,java中對應的方法不須要寫具體的代碼,但須要在前面聲明native。
關於synchronized與abstract合用的問題,我以爲也不行,由於在我幾年的學習和開發中,歷來沒見到過這種狀況,而且我以爲synchronized應該是做用在一個具體的方法上纔有意義。並且,方法上的synchronized同步所使用的同步鎖對象是this,而抽象方法上沒法肯定this是什麼。
使用static聲明的內部類就是外部類,能夠經過外部類.內部類直接訪問。
普通內部類是不可以直接被外部所訪問的,須要經過外部類實例在找到內部類實例。
內部類就是在一個類的內部定義的類,內部類中不能定義靜態成員(靜態成員不是對象的特性,只是爲了找一個容身之處,因此須要放到一個類中而已,這麼一點小事,你還要把它放到類內部的一個類中,過度了啊!提供內部類,不是爲讓你幹這種事情,無聊,不讓你幹。我想多是既然靜態成員相似c語言的全局變量,而內部類一般是用於建立內部對象用的,因此,把「全局變量」放在內部類中就是毫無心義的事情,既然是毫無心義的事情,就應該被禁止),內部類能夠直接訪問外部類中的成員變量,內部類能夠定義在外部類的方法外面,也能夠定義在外部類的方法體中,以下所示:
public class Outer{
int out_x = 0;
public void method(){
Inner1 inner1 = new Inner1();
class Inner2 {
public void method(){
out_x = 3;
}
}
Inner2 inner2 = new Inner2();
}
public class Inner1 {
}
}
在方法體外面定義的內部類的訪問類型能夠是public,protecte,默認的,private等4種類型,這就好像類中定義的成員變量有4種訪問類型同樣,它們決定這個內部類的定義對其餘類是否可見;對於這種狀況,咱們也能夠在外面建立內部類的實例對象,建立內部類的實例對象時,必定要先建立外部類的實例對象,而後用這個外部類的實例對象去建立內部類的實例對象,代碼以下:
Outer outer = new Outer();
Outer.Inner1 inner1 = outer.new Innner1();
在方法內部定義的內部類前面不能有訪問類型修飾符,就好像方法中定義的局部變量同樣,但這種內部類的前面能夠使用final或abstract修飾符。這種內部類對其餘類是不可見的其餘類沒法引用這種內部類,可是這種內部類建立的實例對象能夠傳遞給其餘類訪問。這種內部類必須是先定義,後使用,即內部類的定義代碼必須出如今使用該類以前,這與方法中的局部變量必須先定義後使用的道理也是同樣的。這種內部類能夠訪問方法體中的局部變量,可是,該局部變量前必須加final修飾符。
對於這些細節,只要在eclipse寫代碼試試,根據開發工具提示的各種錯誤信息就能夠立刻了解到。
在方法體內部還能夠採用以下語法來建立一種匿名內部類,即定義某一接口或類的子類的同時,還建立了該子類的實例對象,無需爲該子類定義名稱:
public class Outer {
public void start() {
new Thread(new Runnable() {
public void run() {
};
}).start();
}
}
最後,在方法外部定義的內部類前面能夠加上static關鍵字,從而成爲Static Nested Class,它再也不具備內部類的特性,全部,從狹義上講,它不是內部類。Static Nested Class與普通類在運行時的行爲和功能上沒有什麼區別,只是在編程引用時的語法上有一些差異,它能夠定義成public、protected、默認的、private等多種類型,而普通類只能定義成public和默認的這兩種類型。在外面引用Static Nested Class類的名稱爲「外部類名.內部類名」。在外面不須要建立外部類的實例對象,就能夠直接建立Static Nested Class,例如,假設Inner是定義在Outer類中的Static Nested Class,那麼能夠使用以下語句建立Inner類:
Outer.Inner inner = new Outer.Inner();
因爲static Nested Class不依賴於外部類的實例對象,因此,static Nested Class能訪問外部類的非static成員變量。當在外部訪問Static Nested Class時,能夠直接使用Static Nested Class的名字,而不須要加上外部類的名字了,在Static Nested Class中也能夠直接引用外部類的static的成員變量,不須要加上外部類的名字。
在靜態方法中定義的內部類也是Static Nested Class,這時候不能在類前面加static關鍵字,靜態方法中的Static Nested Class與普通方法中的內部類的應用方式很類似,它除了能夠直接訪問外部類中的static的成員變量,還能夠訪問靜態方法中的局部變量,可是,該局部變量前必須加final修飾符。
備註:首先根據你印象說出你對內部類的整體方面的特色:例如,在兩個地方能夠定義,能夠訪問外部類的成員變量,不能定義靜態成員,這是大的特色。而後再說一些細節方面的知識,例如,幾種定義方式的語法區別,靜態內部類,以及匿名內部類。
徹底能夠。若是不是靜態內部類,那沒有什麼限制!
若是你把靜態嵌套類看成內部類的一種特例,那在這種狀況下不能夠訪問外部類的普通成員變量,而只能訪問外部類中的靜態成員,例如,下面的代碼:
class Outer {
static int x;
static class Inner {
void test() {
syso(x);
}
}
}
答題時,也要能察言觀色,揣摩提問者的心思,顯然人家但願你說的是靜態內部類不能訪問外部類的成員,但你一上來就頂牛,這很差,要先順着人家,讓人家滿意,而後再說特殊狀況,讓人家吃驚。
能夠繼承其餘類或實現其餘接口。不只是能夠,而是必須!由於匿名內部類就是在抽象類和接口的基礎上發展起來的。
調用該訪問 返回一個以字符串指定類名的類的對象。
返回字節碼,返回字節碼的方式有幾種:
①:這份字節碼曾經被加載過已經存在java虛擬機中了直接返回。
②:java虛擬機中尚未這份字節碼 用類加載器去加載 把加載進來的字節碼緩存在虛擬機中,之後再獲得這個字節碼就不用再加載。
獲得字節碼對應的實例對象: 類名.class ; 對象.getClass(),例如 new Date; 類名.class,例如,System.class
對象.getClass(),例如,new Date().getClass() Class.forName(「類名」),例如,Class.forName(「java.util.Date」);
③:靜態方法去查詢或者加載這個字符串所對應哪一個類的字節碼
由於在寫源程序的時候還不知道類的名字,在我運行的時候人家傳遞我一個字符串,這個字符串裏面包含了一個類的名字,再寫程序的時候把java.util.Date換成一個字符串變量, 等程序運行起來的時候這個變量的值從一個配置文件裏面裝再進來,這個類的名字在寫原程序的時候不用知道而是等運行的時候給我臨時送進來。
》》》》》》》》》》》》》》》》》》》》》》》》》》》《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
按參數中指定的字符串形式的類名去搜索並加載相應的類,若是該類字節碼已經被加載過,則返回表明該字節碼的Class實例對象,不然,按類加載器的委託機制去搜索和加載該類,若是全部的類加載器都沒法加載到該類,則拋出ClassNotFoundException。加載完這個Class字節碼後,接着就能夠使用Class字節碼的newInstance方法去建立該類的實例對象了。
有時候,咱們程序中全部使用的具體類名在設計時(即開發時)沒法肯定,只有程序運行時才能肯定,這時候就須要使用Class.forName去動態加載該類,這個類名一般是在配置文件中配置的,例如,spring的ioc中每次依賴注入的具體類就是這樣配置的,jdbc的驅動類名一般也是經過配置文件來配置的,以便在產品交付使用後不用修改源程序就能夠更換驅動類名
下面程序的輸出結果是多少?
import java.util.Date;
public class Test extends Date{
public static void main(String[] args) {
new Test().test();
}
public void test(){
System.out.println(super.getClass().getName());
}
}
很奇怪,結果是Test
這屬於腦筋急轉彎的題目,在一個qq羣有個網友正好問過這個問題,我以爲挺有趣,就研究了一下,沒想到今天還被你面到了,哈哈。
在test方法中,直接調用getClass().getName()方法,返回的是Test類名
因爲getClass()在Object類中定義成了final,子類不能覆蓋該方法,因此,在test方法中調用getClass().getName()方法,其實就是在調用從父類繼承的getClass()方法,等效於調用super.getClass().getName()方法,因此,super.getClass().getName()方法返回的也應該是Test。
若是想獲得父類的名稱,應該用以下代碼:
getClass().getSuperClass().getName();
基本數據類型包括byte、int、char、long、float、double、boolean和short。
java.lang.String類是final類型,所以不可繼承這個類、不能修改這個類。爲提升效率節省空間,咱們應用StringBuffer類。
沒有。由於String被設計成不可變(immutable)類,因此它的全部對象都是不可變對象。這段代碼中,s原先指向一個String對象,內容是 "Hello",而後咱們對s進行了+操做,那麼s所指向那個對象是否發生了改變呢?答案是沒有。這時,s不指向原來那個對象,而指向了另外一個 String對象,內容爲"Hello world!",原來那個對象還存在於內存中,只是s這個引用變量再也不指向它了。
經過上面的說明,咱們很容易導出另外一個結論,若是常常對字符串進行各類各樣的修改,或者說,不可預見的修改,那麼使用String來表明字符串的話會引發很大的內存開銷。由於 String對象創建以後不能再改變,因此對於每個不一樣的字符串,都須要一個String對象來表示。這時,應該考慮使用StringBuffer類,它容許修改,而不是每一個不一樣的字符串都要生成一個新的對象。而且,這兩種類的對象轉換十分容易。
同時,咱們還能夠知道,若是要使用內容相同的字符串,沒必要每次都new一個String。例如咱們要在構造器中對一個名叫s的String引用變量進行初始化,把它設置爲初始值,應當這樣作:
public class Demo {
private String s;
public Demo() {
s = "Initial Value";
}
}
而非s = new String("Initial Value");
後者每次都會調用構造器,生成新對象,性能低下且內存開銷大,而且沒有意義,由於String對象不可改變,因此對於內容相同的字符串,只要一個String對象來表示就能夠了。也就說,屢次調用上面的構造器建立多個對象,他們的String類型屬性s都指向同一個對象。
上面的結論還基於這樣一個事實:對於字符串常量,若是內容相同,Java認爲它們表明同一個String對象。而用關鍵字new調用構造器,老是會建立一個新的對象,不管內容是否相同。
至於爲何要把String類設計成不可變類,是它的用途決定的。其實不僅String,不少Java標準類庫中的類都是不可變的。在開發一個系統的時候,咱們有時候也須要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優勢,好比由於它的對象是隻讀的,因此多線程併發訪問也不會有任何問題。固然也有一些缺點,好比每一個不一樣的狀態都要一個對象來表明,可能會形成性能上的問題。因此Java標準類庫還提供了一個可變版本,即 StringBuffer。
String類是final類故不能夠繼承.
兩個或一個,」xyz」對應一個對象,這個對象放在字符串常量緩衝區,常量」xyz」無論出現多少遍,都是緩衝區中的那一個。New String每寫一遍,就建立一個新的對象,它一句那個常量」xyz」對象的內容來建立出一個新String對象。若是之前就用過’xyz’,這句表明就不會建立」xyz」本身了,直接從緩衝區拿。
JAVA平臺提供了兩個類:String和StringBuffer,它們能夠儲存和操做字符串,即包含多個字符的字符數據。這個String類提供了數值不可改變的字符串。而這個StringBuffer類提供的字符串進行修改。當你知道字符數據要改變的時候你就能夠使用StringBuffer。典型地,你能夠使用StringBuffers來動態構造字符數據。另外,String實現了equals方法,new String(「abc」).equals(new String(「abc」)的結果爲true,而StringBuffer沒有實現equals方法,因此,new StringBuffer(「abc」).equals(new StringBuffer(「abc」)的結果爲false。接着要舉一個具體的例子來講明,咱們要把1到100的全部數字拼起來,組成一個串。
StringBuffer sbf = new StringBuffer();
for(int i=0;i<100;i++){
sbf.append(i);
}
上面的代碼效率很高,由於只建立了一個StringBuffer對象,而下面的代碼效率很低,由於建立了101個對象。
String str = new String();
for(int i=0;i<100;i++){
str = str + i;
}
在講二者區別時,應把循環的次數搞成10000,而後用endTime-beginTime來比較二者執行的時間差別,最後還要講講StringBuilder與StringBuffer的區別。
String覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法,因此,將StringBuffer對象存儲進Java集合類中時會出現問題。
若是不查jdk api,我很難寫出來!我能夠說說個人思路:用正則表達式,代碼大概爲:
String [] result = orgStr.split(「,」);
用 StingTokenizer ,代碼爲:StringTokenizer tokener = StringTokenizer(orgStr,」,」);
String [] result = new String[tokener .countTokens()];
Int i=0;
while(tokener.hasNext(){result[i++]=toker.nextToken();}
數組沒有length()這個方法,有length的屬性。String有有length()這個方法。
答:對於以下代碼:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
第一條語句打印的結果爲false,第二條語句打印的結果爲true,這說明javac編譯能夠對字符串常量直接相加的表達式進行優化,沒必要要等到運行期去進行加法運算處理,而是在編譯時去掉其中的加號,直接將其編譯成一個這些常量相連的結果。
題目中的第一行代碼被編譯器在編譯時優化後,至關於直接定義了一個」abcd」的字符串,因此,上面的代碼應該只建立了一個String對象。寫以下兩行代碼,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最終打印的結果應該爲true。
也許你的答案是在return以前,但往更細地說,個人答案是在return中間執行,請看下面程序代碼的運行結果:
public class Test {
public static void main(String[] args) {
System.out.println(new Test().test());
}
static int test(){
int x = 1;
try{
return x;
}
finally{
++x;
}
}
}
運行結果是1,爲何呢?主函數調用子函數並獲得結果的過程,比如主函數準備一個空罐子,當子函數要返回結果時,先把結果放在罐子裏,而後再將程序邏輯返回到主函數。所謂返回,就是子函數說,我不運行了,你主函數繼續運行吧,這沒什麼結果可言,結果是在說這話以前放進罐子裏的。
public class smallT{
public static void main(String args[]){
smallT t = new smallT();
int b = t.get();
System.out.println(b);
}
public int get(){
try{
return 1 ;
}
finally{
return 2 ;
}
}
}
返回的結果是2。
我能夠經過下面一個例子程序來幫助我解釋這個答案,從下面例子的運行結果中能夠發現,try中的return語句調用的函數先於finally中調用的函數執行,也就是說return語句先執行,finally語句後執行,因此,返回的結果是2。Return並非讓函數立刻返回,而是return語句執行後,將把返回結果放置進函數棧中,此時函數並非立刻返回,它要執行finally語句後才真正返回。
在講解答案時能夠用下面的程序來幫助分析:
public class Test {
public static void main(String[] args) {
System.out.println(new Test().test());;
}
int test(){
try{
return func1();
}
finally{
return func2();
}
}
int func1(){
System.out.println("func1");
return 1;
}
int func2(){
System.out.println("func2");
return 2;
}
}
-----------執行結果-----------------
func1
func2
2
結論:finally中的代碼比return 和break語句後執行
final: 用於聲明屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
內部類要訪問局部變量,局部變量必須定義成final類型,例如,一段代碼……
finally:是異常處理語句結構的一部分,是異常的統一出口,表示老是執行。
finalize:是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,能夠覆蓋此方法提供垃圾收集時的其餘資源回收,例如關閉文件等。JVM不保證此方法總被調用.
異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤。java編譯器要求方法必須聲明拋出可能發生的非運行時異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。
error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。不可能期望程序能處理這樣的狀況。 exception 表示一種設計或實現問題。也就是說,它表示若是程序運行正常,從不會發生的狀況。
Error表示有JVM進行處理的,是JVM出錯.
Exctption是能夠用程序進行處理的,使用try…catch進行處理.
異常是指java程序運行時(非編譯)所發生的非正常狀況或錯誤,與現實生活中的事件很類似,現實生活中的事件能夠包含事件發生的時間、地點、人物、情節等信息,能夠用一個對象來表示,Java使用面向對象的方式來處理異常,它把程序中發生的每一個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。
Java對異常進行了分類,不一樣類型的異常分別用不一樣的Java類表示,全部異常的根類爲java.lang.Throwable,Throwable下面又派生了兩個子類:Error和Exception,Error 表示應用程序自己沒法克服和恢復的一種嚴重問題,程序只有死的份了,例如,說內存溢出和線程死鎖等系統問題。Exception表示程序還可以克服和恢復的問題,其中又分爲系統異常和普通異常,系統異常是軟件自己缺陷所致使的問題,也就是軟件開發人員考慮不周所致使的問題,軟件使用者沒法克服和恢復這種問題,但在這種問題下還可讓軟件系統繼續運行或者讓軟件死掉,例如,數組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);普通異常是運行環境的變化或異常所致使的問題,是用戶可以克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不該該死掉。
java爲系統異常和普通異常提供了不一樣的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,因此普通異常也稱爲checked異常,而系統異常能夠處理也能夠不處理,因此,編譯器不強制用try..catch處理或用throws聲明,因此係統異常也稱爲unchecked異常。
提示答題者:就按照三個級別去思考:虛擬機必須宕機的錯誤,程序能夠死掉也能夠不死掉的錯誤,程序不該該死掉的錯誤;
這道題主要考你的代碼量到底多大,若是你長期寫代碼的,應該常常都看到過一些系統方面的異常,你不必定真要回答出5個具體的系統異常,但你要可以說出什麼是系統異常,以及幾個系統異常就能夠了,固然,這些異常徹底用其英文名稱來寫是最好的,若是實在寫不出,那就用中文吧,有總比沒有強!
所謂系統異常,就是…..,它們都是RuntimeException的子類,在jdk doc中查RuntimeException類,就能夠看到其全部的子類列表,也就是看到了全部的系統異常。我比較有印象的系統異常有:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。
Java經過面向對象的方法進行異常處理,把各類不一樣的異常進行分類,並提供了良好的接口。在Java中,每一個異常都是一個對象,它是Throwable類或其它子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法能夠捕獲到這個異常並進行處理。Java的異常處理是經過5個關鍵詞來實現的:try、catch、throw、throws和finally。通常狀況下是用try來執行一段程序,若是出現異常,系統會拋出(throws)一個異常,這時候你能夠經過它的類型來捕捉(catch)它,或最後(finally)由缺省處理器來處理;
try用來指定一塊預防全部「異常」的程序;
catch子句緊跟在try塊後面,用來指定你想要捕捉的「異常」的類型;
throw語句用來明確地拋出一個「異常」;
throws用來標明一個成員函數可能拋出的各類「異常」;
finally爲確保一段代碼無論發生什麼「異常」都被執行一段代碼;
能夠在一個成員函數調用的外面寫一個try語句,在這個成員函數內部寫另外一個try語句保護其餘代碼。每當遇到一個try語句,「異常」的框架就放到堆棧上面,直到全部的try語句都完成。若是下一級的try語句沒有對某種「異常」進行處理,堆棧就會展開,直到遇到有處理這種「異常」的try語句。
答:Sockets有兩種主要的操做方式:面向鏈接的和無鏈接的。
無鏈接的操做使用數據報協議.這個模式下的socket不須要鏈接一個目的的socket,它只是簡單地投出數據報.無鏈接的操做是快速的和高效的,可是數據安全性不佳.面向鏈接的操做使用TCP協議.一個這個模式下的socket必須在發送數據以前與目的地的socket取得一個鏈接.一旦鏈接創建了,sockets就能夠使用一個流接口:打開-讀-寫-關閉.全部的發送的信息都會在另外一端以一樣的順序被接收.面向鏈接的操做比無鏈接的操做效率更低,可是數據的安全性更高.
在服務器,使用ServerSocket監聽指定的端口,端口能夠隨意指定(因爲1024如下的端口一般屬於保留端口,在一些操做系統中不能夠隨意使用,因此建議使用大於1024的端口),等待客戶鏈接請求,客戶鏈接後,會話產生;在完成會話後,關閉鏈接。在客戶端,使用Socket對網絡上某一個服務器的某一個端口發出鏈接請求,一旦鏈接成功,打開會話;會話完成後,關閉Socket。客戶端不須要指定打開的端口,一般臨時的、動態的分配一個1024以上的端口。
java5之前,有以下兩種:
第一種:
new Thread(){}.start();這表示調用Thread子類對象的run方法,new Thread(){}表示一個Thread的匿名子類的實例對象,子類加上run方法後的代碼以下:
new Thread(){
public void run(){
}
}.start();
第二種:
new Thread(new Runnable(){}).start();這表示調用Thread對象接受的Runnable對象的run方法,new Runnable(){}表示一個Runnable的匿名子類的實例對象,runnable的子類加上run方法後的代碼以下:
new Thread(new Runnable(){
public void run(){
}
}
).start();
從java5開始,還有以下一些線程池建立多線程的方式:
ExecutorService pool = Executors.newFixedThreadPool(3)
for(int i=0;i<10;i++){
pool.execute(new Runable(){public void run(){}});
}
Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});
Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});
有兩種實現方法,分別使用new Thread()和new Thread(runnable)形式,第一種直接調用thread的run方法,因此,咱們每每使用Thread子類,即new SubThread()。第二種調用runnable的run方法。
有兩種實現方法,分別是繼承Thread類與實現Runnable接口
用synchronized關鍵字修飾同步方法
反對使用stop(),是由於它不安全。它會解除由線程獲取的全部鎖定,並且若是對象處於一種不連貫狀態,那麼其餘線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這以前得到的鎖定。此時,其餘任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復運行。對任何線程來講,若是它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會形成死鎖。因此不該該使用suspend(),而應在本身的Thread類中置入一個標誌,指出線程應該活動仍是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()從新啓動線程。
sleep:Thread類中定義的方法,表示線程休眠,會自動喚醒;
wait:Object中定義的方法,須要手工調用notify()或者notifyAll()方法。
(網上的答案:sleep是線程類(Thread)的方法,致使此線程暫停執行指定時間,給執行機會給其餘線程,可是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。 wait是Object類的方法,對此對象調用wait方法致使本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池準備得到對象鎖進入運行狀態。)
sleep就是正在執行的線程主動讓出cpu,cpu去執行其餘線程,在sleep指定的時間事後,cpu纔會回到這個線程上繼續往下執行,若是當前線程進入了同步鎖,sleep方法並不會釋放鎖,即便當前線程使用sleep方法讓出了cpu,但其餘被同步鎖擋住了的線程也沒法獲得執行。wait是指在一個已經進入了同步鎖的線程內,讓本身暫時讓出同步鎖,以便其餘正在等待此鎖的線程能夠獲得同步鎖並運行,只有其餘線程調用了notify方法(notify並不釋放鎖,只是告訴調用過wait方法的線程能夠去參與得到鎖的競爭了,但不是立刻獲得鎖,由於鎖還在別人手裏,別人還沒釋放。若是notify方法後面的代碼還有不少,須要這些代碼執行完後纔會釋放鎖,能夠在notfiy方法後增長一個等待和一些代碼,看看效果),調用wait方法的線程就會解除wait狀態和程序能夠再次獲得鎖後繼續向下運行。對於wait的講解必定要配合例子代碼來講明,才顯得本身真明白。
package com.huawei.interview;
public class MultiThread {
public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
public void run() {
//因爲這裏的Thread1和下面的Thread2內部run方法要用同一對象做爲監視器,咱們這裏不能用this,由於在Thread2裏面的this和這個Thread1的this不是同一個對象。咱們用MultiThread.class這個字節碼對象,當前虛擬機裏引用這個變量時,指向的都是同一個對象。
synchronized (MultiThread.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting");
try {
//釋放鎖有兩種方式,第一種方式是程序天然離開監視器的範圍,也就是離開了synchronized關鍵字管轄的代碼範圍,另外一種方式就是在synchronized關鍵字管轄的代碼內部調用監視器對象的wait方法。這裏,使用wait方法釋放鎖。
MultiThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 is going on...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable{
public void run() {
synchronized (MultiThread.class) {
System.out.println("enter thread2...");
System.out.println("thread2 notify other thread can release wait status..");
//因爲notify方法並不釋放鎖, 即便thread2調用下面的sleep方法休息了10毫秒,但thread1仍然不會執行,由於thread2沒有釋放鎖,因此Thread1沒法得不到鎖。
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread2 is going on...");
System.out.println("thread2 is being over!");
}
}
}
}
若是數據將在線程間共享。例如正在寫的數據之後可能被另外一個線程讀到,或者正在讀的數據可能已經被另外一個線程寫過了,那麼這些數據就是共享數據,必須進行同步存取。
當應用程序在對象上調用了一個須要花費很長時間來執行的方法,而且不但願讓程序等待方法的返回時,就應該使用異步編程,在不少狀況下采用異步途徑每每更有效率。
class Test{
synchronized static void sayHello3() {
}
synchronized void getX(){}
}
多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口
同步的實現方面有兩種,分別是synchronized,wait與notify
wait():使一個線程處於等待狀態,而且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程,並且不是按優先級。
Allnotity():喚醒全部處入等待狀態的線程,注意並非給全部喚醒線程一個對象的鎖,而是讓它們競爭。
啓動一個線程是調用start()方法,使線程就緒狀態,之後能夠被調度爲運行狀態,一個線程必須關聯一些具體的執行代碼,run()方法是該線程所關聯的執行代碼。
分幾種狀況:
1.其餘方法前是否加了synchronized關鍵字,若是沒加,則能。
2.若是這個方法內部調用了wait,則能夠進入其餘synchronized方法。
3.若是其餘個方法都加了synchronized關鍵字,而且內部沒有調用wait,則不能。
4.若是其餘方法是static,它用的同步鎖是當前類的字節碼,與非靜態的方法不能同步,由於非靜態的方法用的是this。
一個程序中能夠有多條執行線索同時執行,一個線程就是程序中的一條執行線索,每一個線程上都關聯有要執行的代碼,便可以有多段程序代碼同時運行,每一個程序至少都有一個線程,即main方法執行的那個線程。若是隻是一個cpu,它怎麼可以同時執行多段程序呢?這是從宏觀上來看的,cpu一會執行a線索,一會執行b線索,切換時間很快,給人的感受是a,b在同時執行,比如你們在同一個辦公室上網,只有一條連接到外部網線,其實,這條網線一會爲a傳數據,一會爲b傳數據,因爲切換時間很短暫,因此,你們感受都在同時上網。
狀態:就緒,運行,synchronize阻塞,wait和sleep掛起,結束。wait必須在synchronized內部調用。
調用線程的start方法後線程進入就緒狀態,線程調度系統將就緒狀態的線程轉爲運行狀態,遇到synchronized語句時,由運行狀態轉爲阻塞,當synchronized得到鎖後,由阻塞轉爲運行,在這種狀況能夠調用wait方法轉爲掛起狀態,當線程關聯的代碼執行完後,線程變爲結束狀態。
主要相同點:Lock能完成synchronized所實現的全部功能
主要不一樣點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock必定要求程序員手工釋放,而且必須在finally從句中釋放。Lock還有更強大的功能,例如,它的tryLock方法能夠非阻塞方式去拿鎖。
舉例說明(對下面的題用lock進行了改寫):
package com.huawei.interview;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {
private int j;
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
ThreadTest tt = new ThreadTest();
for(int i=0;i<2;i++){
new Thread(tt.new Adder()).start();
new Thread(tt.new Subtractor()).start();
}
}
private class Subtractor implements Runnable{
public void run() {
while(true){
/*synchronized (ThreadTest.this) {
System.out.println("j--=" + j--);
//這裏拋異常了,鎖能釋放嗎?
}*/
lock.lock();
try{
System.out.println("j--=" + j--);
}finally{
lock.unlock();
}
}
}
}
private class Adder implements Runnable{
public void run() {
while(true){
/*synchronized (ThreadTest.this) {
System.out.println("j++=" + j++);
}*/
lock.lock();
try{
System.out.println("j++=" + j++);
}finally{
lock.unlock();
}
}
}
}
}
如下程序使用內部類實現線程,對j增減的時候沒有考慮順序問題。
public class ThreadTest1 {
private int j;
public static void main(String args[]) {
ThreadTest1 tt = new ThreadTest1();
Inc inc = tt.new Inc();
Dec dec = tt.new Dec();
for (int i = 0; i < 2; i++) {
Thread t = new Thread(inc);
t.start();
t = new Thread(dec);
t.start();
}
}
private synchronized void inc() {
j++;
System.out.println(Thread.currentThread().getName() + "-inc:" + j);
}
private synchronized void dec() {
j--;
System.out.println(Thread.currentThread().getName() + "-dec:" + j);
}
class Inc implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
inc();
}
}
}
class Dec implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
dec();
}
}
}
}
---------隨手再寫的一個-------------
class A{
JManger j =new JManager();
main(){
new A().call();
}
void call{
for(int i=0;i<2;i++){
new Thread(
new Runnable(){ public void run(){while(true){j.accumulate()}}}
).start();
new Thread(new Runnable(){ public void run(){while(true){j.sub()}}}).start();
}
}
}
class JManager{
private j = 0;
public synchronized void subtract(){
j--
}
public synchronized void accumulate(){
j++;
}
}
最終的程序代碼以下:
public class ThreadTest {
public static void main(String[] args) {
new ThreadTest().init();
}
public void init(){
final Business business = new Business();
new Thread(
new Runnable(){
public void run() {
for(int i=0;i<50;i++){
business.SubThread(i);
}
}
}
).start();
for(int i=0;i<50;i++){
business.MainThread(i);
}
}
private class Business{
boolean bShouldSub = true;//這裏至關於定義了控制該誰執行的一個信號燈
public synchronized void MainThread(int i){
if(bShouldSub)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int j=0;j<5;j++){
System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
}
bShouldSub = true;
this.notify();
}
public synchronized void SubThread(int i){
if(!bShouldSub)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
}
bShouldSub = false;
this.notify();
}
}
}
備註:不可能一上來就寫出上面的完整代碼,最初寫出來的代碼以下,問題在於兩個線程的代碼要參照同一個變量,即這兩個線程的代碼要共享數據,因此,把這兩個線程的執行代碼搬到同一個類中去:
package com.huawei.interview.lym;
public class ThreadTest {
private static boolean bShouldMain = false;
public static void main(String[] args) {
/*new Thread(){
public void run(){
for(int i=0;i<50;i++){
for(int j=0;j<10;j++){
System.out.println("i=" + i + ",j=" + j);
}
}
}
}.start();*/
//final String str = new String("");
new Thread(
new Runnable(){
public void run(){
for(int i=0;i<50;i++){
synchronized (ThreadTest.class) {
if(bShouldMain){
try {
ThreadTest.class.wait();}
catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName() + "i=" + i + ",j=" + j);
}
bShouldMain = true;
ThreadTest.class.notify();
}
}
}
}
).start();
for(int i=0;i<50;i++){
synchronized (ThreadTest.class) {
if(!bShouldMain){
try {
ThreadTest.class.wait();}
catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int j=0;j<5;j++){
System.out.println(
Thread.currentThread().getName() +
"i=" + i + ",j=" + j);
}
bShouldMain = false;
ThreadTest.class.notify();
}
}
}
}
下面使用jdk5中的併發庫來實現的:
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class ThreadTest{
private static Lock lock = new ReentrantLock();
private static Condition subThreadCondition = lock.newCondition();
private static boolean bBhouldSubThread = false;
public static void main(String [] args){
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.execute(new Runnable(){
public void run(){
for(int i=0;i<50;i++){
lock.lock();
try{
if(!bBhouldSubThread)
subThreadCondition.await();
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName() + ",j=" + j);
}
bBhouldSubThread = false;
subThreadCondition.signal();
}catch(Exception e){
}
finally{
lock.unlock();
}
}
}
});
threadPool.shutdown();
for(int i=0;i<50;i++){
lock.lock();
try{
if(bBhouldSubThread)
subThreadCondition.await();
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName() +",j=" + j);
}
bBhouldSubThread = true;
subThreadCondition.signal();
}catch(Exception e){
}
finally{
lock.unlock();
}
}
}
}
答:隨意發揮題,天南海北誰便談,只要讓別以爲你知識淵博,理解透徹便可。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一個Collection表明一組Object,即Collection的元素(Elements)
Map提供key到value的映射
comparable/comparator
這兩個類都實現了List接口(List接口繼承了Collection接口),他們都是有序集合,即存儲在這兩個集合中的元素的位置都是有順序的,至關於一種動態的數組,咱們之後能夠按位置索引號取出某個元素,而且其中的數據是容許重複的,這是HashSet之類的集合的最大不一樣處,HashSet之類的集合不能夠按索引號去檢索其中的元素,也不容許有重複的元素(原本題目問的與hashset沒有任何關係,但爲了說清楚ArrayList與Vector的功能,咱們使用對比方式,更有利於說明問題)。
接着才說ArrayList與Vector的區別,這主要包括兩個方面:.
(1)同步性:
Vector是線程安全的,也就是說是它的方法之間是線程同步的,而ArrayList是線程序不安全的,它的方法之間是線程不一樣步的。若是隻有一個線程會訪問到集合,那最好是使用ArrayList,由於它不考慮線程安全,效率會高些;若是有多個線程會訪問到集合,那最好是使用Vector,由於不須要咱們本身再去考慮和編寫線程安全的代碼。
(備註:對於Vector&ArrayList、Hashtable&HashMap,要記住線程安全問題,記住Vector與Hashtable是舊的,是java一誕生就提供的,它們是線程安全的,ArrayList與HashMap是java2時才提供的,它們是線程不安全的。因此,咱們講課時先講老的。)
(2)數據增加:
ArrayList與Vector都有一個初始的容量大小,當存儲進它們裏面的元素的個數超過了容量時,就須要增長ArrayList與Vector的存儲空間,每次要增長存儲空間時,不是隻增長一個存儲單元,而是增長多個存儲單元,每次增長的存儲單元的個數在內存空間利用與程序效率之間要取得必定的平衡。Vector默認增加爲原來兩倍,而ArrayList的增加策略在文檔中沒有明確規定(從源代碼看到的是增加爲原來的1.5倍)。ArrayList與Vector均可以設置初始的空間大小,Vector還能夠設置增加的空間大小,而ArrayList沒有提供設置增加空間的方法。
總結:即Vector增加原來的一倍,ArrayList增長原來的0.5倍。
HashMap:JDK1.2以後推出,是新的類。採用異步處理方式,性能較高,可是屬於非線程安全。容許設置null。
Hashtable:JDK1.0時推出,是舊的類。採用同步處理方式,性能較低,可是屬於非線程安全。容許設置null。
(條理上還須要整理,也是先說相同點,再說不一樣點)
HashMap是Hashtable的輕量級實現(非線程安全的實現),他們都完成了Map接口,主要區別在於HashMap容許空(null)鍵值(key),因爲非線程安全,在只有一個線程訪問的狀況下,效率要高於Hashtable。
HashMap容許將null做爲一個entry的key或者value,而Hashtable不容許。
HashMap把Hashtable的contains方法去掉了,改爲containsvalue和containsKey。由於contains方法容易讓人引發誤解。
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。
最大的不一樣是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不須要本身爲它的方法實現同步,而HashMap 就必須爲之提供外同步。
Hashtable和HashMap採用的hash/rehash算法都大概同樣,因此性能不會有很大的差別。
就HashMap與HashTable主要從三方面來講。
一.歷史緣由:Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現
二.同步性:Hashtable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的
三.值:只有HashMap可讓你將空值做爲一個表的條目的key或value 。
一個是存儲單列數據的集合,另外一個是存儲鍵和值這樣的雙列數據的集合,List中存儲的數據是有順序,而且容許重複;Map中存儲的數據是沒有順序的,其鍵是不能重複的,它的值是能夠有重複的。
List,Set是,Map不是
這樣的題屬於隨意發揮題:這樣的題比較考水平,兩個方面的水平:一是要真正明白這些內容,二是要有較強的總結和表述能力。若是你明白,但表述不清楚,在別人那裏則等同於不明白。
首先,List與Set具備類似性,它們都是單列元素的集合,因此,它們有一個功共同的父接口,叫Collection。Set裏面不容許有重複的元素,所謂重複,即不能有兩個相等(注意,不是僅僅是相同)的對象 ,即假設Set集合中有了一個A對象,如今我要向Set集合再存入一個B對象,但B對象與A對象equals相等,則B對象存儲不進去,因此,Set集合的add方法有一個boolean的返回值,當集合中沒有某個元素,此時add方法可成功加入該元素時,則返回true,當集合含有與某個元素equals相等的元素時,此時add方法沒法加入該元素,返回結果爲false。Set取元素時,無法說取第幾個,只能以Iterator接口取得全部的元素,再逐一遍歷各個元素。
List表示有前後順序的集合, 注意,不是那種按年齡、按大小、按價格之類的排序。當咱們屢次調用add(Obj e)方法時,每次加入的對象就像火車站買票有排隊順序同樣,按先來後到的順序排序。有時候,也能夠插隊,即調用add(int index,Obj e)方法,就能夠指定當前對象在集合中的存放位置。一個對象能夠被反覆存儲進List中,每調用一次add方法,這個對象就被插入進集合中一次,其實,並非把這個對象自己存儲進了集合中,而是在集合中用一個索引變量指向這個對象,當這個對象被add屢次時,即至關於集合中有多個索引指向了這個對象,如圖x所示。List除了能夠以Iterator接口取得全部的元素,再逐一遍歷各個元素以外,還能夠調用get(index i)來明確說明取第幾個。
Map與List和Set不一樣,它是雙列的集合,其中有put方法,定義以下:put(obj key,obj value),每次存儲時,要存儲一對key/value,不能存儲重複的key,這個重複的規則也是按equals比較相等。取則能夠根據key得到相應的value,即get(Object key)返回值爲key 所對應的value。另外,也能夠得到全部的key的結合,還能夠得到全部的value的結合,還能夠得到key和value組合成的Map.Entry對象的集合。
List 以特定次序來持有元素,可有重複元素。Set 沒法擁有重複元素,內部排序。Map 保存key-value值,value可多值。
HashSet按照hashcode值的某種運算方式進行存儲,而不是直接按hashCode值的大小進行存儲。例如,"abc" ---> 78,"def" ---> 62,"xyz" ---> 65在hashSet中的存儲順序不是62,65,78,LinkedHashSet按插入的順序存儲,那被存儲對象的hashcode方法還有什麼做用呢?咱們想一想!hashset集合比較兩個對象是否相等,首先看hashcode方法是否相等,而後看equals方法是否相等。new 兩個Student插入到HashSet中,看HashSet的size,實現hashcode和equals方法後再看size。
同一個對象能夠在Vector中加入屢次。往集合裏面加元素,至關於集合裏用一根繩子鏈接到了目標對象。往HashSet中卻加不了屢次的。
ArrayList和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增長和插入元素,它們都容許直接按序號索引元素,可是插入元素要涉及數組元素移動等內存操做,因此索引數據快而插入數據慢,Vector因爲使用了synchronized方法(線程安全),一般性能上較ArrayList差,而LinkedList使用雙向鏈表實現存儲,按序號索引數據須要進行前向或後向遍歷,可是插入數據時只須要記錄本項的先後項便可,因此插入速度較快。
LinkedList也是線程不安全的,LinkedList提供了一些方法,使得LinkedList能夠被看成堆棧和隊列來使用。
Vector newVector = new Vector();
For (int i=0;i<vector.size();i++){
Object obj = vector.get(i);
newVector.add(obj);
}
還有一種簡單的方式,HashSet set = new HashSet(vector);
Collection: 是集合類的上級接口,繼承與他的接口主要有Set 和List.
Collections:是針對集合類的一個幫助類,他提供一系列靜態方法實現對各類集合的搜索、排序、線程安全化等操做。
Set裏的元素是不能重複的,元素重複與否是使用equals()方法進行判斷的。
equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,爲的是當兩個分離的對象的內容和類型相配的話,返回真值。
建立數組時,必須明確說明數組的長度,(即數組中元素的個數),以便在內存中留出一塊空間存放全部的數組元素,數組中各數據元素在內存中是順序存放的。
建立鏈表時,不須要給出鏈表中元素(稱爲節點)的個數,能夠先只建立一個鏈表頭,其餘元素在須要時動態地建立並加入到鏈表,鏈表的數據無素在內存中不是連續存放的。
最經常使用的集合類是 List 和 Map。 List 的具體實現包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構建、存儲和操做任何類型對象的元素列表。 List 適用於按數值索引訪問元素的情形。
Map 提供了一個更通用的元素存儲方法。 Map 集合類用於存儲元素對(稱做"鍵"和"值"),其中每一個鍵映射到一個值。
ArrayList/VectoràList
àCollection
HashSet/TreeSetàSet
PropetiesàHashTable
àMap
Treemap/HashMap
我記的不是方法名,而是思想,我知道它們都有增刪改查的方法,但這些方法的具體名稱,我記得不是很清楚,對於set,大概的方法是add,remove, contains;對於map,大概的方法就是put,remove,contains等,由於,我只要在eclispe下按點操做符,很天然的這些方法就出來了。我記住的一些思想就是List類會有get(int index)這樣的方法,由於它能夠按順序取元素,而set類中沒有get(int index)這樣的方法。List和set均可以迭代出全部元素,迭代時先要獲得一個iterator對象,因此,set和list類都有一個iterator方法,用於返回那個iterator對象。map能夠返回三個集合,一個是返回全部的key的集合,另一個返回的是全部value的集合,再一個返回的key和value組合成的EntrySet對象的集合,map也有get方法,參數是key,返回值是key對應的value。
對。若是對象要保存在HashSet或HashMap中,它們的equals相等,那麼,它們的hashcode值就必須相等。
若是不是要保存在HashSet或HashMap,則與hashcode沒有什麼關係了,這時候hashcode不等是能夠的,例如arrayList存儲的對象就不用實現hashcode,固然,咱們沒有理由不實現,一般都會去實現的。
(應該是沒有針對問題的確切的答案,當前的add方法放入的是哪一個對象,就調用哪一個對象的compareTo方法,至於這個compareTo方法怎麼作,就看當前這個對象的類中是如何編寫這個方法的)
實驗代碼:
public class Parent implements Comparable {
private int age = 0;
public Parent(int age){
this.age = age;
}
public int compareTo(Object o) {
System.out.println("method of parent");
Parent o1 = (Parent)o;
return age>o1.age?1:age<o1.age?-1:0;
}
}
public class Child extends Parent {
public Child(){
super(3);
}
public int compareTo(Object o) {
System.out.println("method of child");
Child o1 = (Child)o;
return 1;
}
}
public class TreeSetTest {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(new Parent(3));
set.add(new Child());
set.add(new Parent(4));
System.out.println(set.size());
}
}
要讓人家感受你對java ee開發很熟,因此,不能僅僅只列core java中的那些東西,要多列你在作ssh項目中涉及的那些東西。就寫你最近寫的那些程序中涉及的那些類。
經常使用的類:BufferedReader BufferedWriter FileReader FileWirter String Integer
java.util.Date,System,Class,List,HashMap
經常使用的包:java.lang java.io java.util java.sql ,javax.servlet,org.apache.strtuts.action,org.hibernate
經常使用的接口:Remote List Map Document NodeList ,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate)、Session(Hibernate),HttpSession
字節流,字符流。字節流繼承於InputStream OutputStream,字符流繼承於InputStreamReader OutputStreamWriter。在java.io包中還有許多其餘的流,主要是爲了提升性能和使用方便。
要把一片二進制數據數據逐一輸出到某個設備中,或者從某個設備中逐一讀取一片二進制數據,無論輸入輸出設備是什麼,咱們要用統一的方式來完成這些操做,用一種抽象的方式進行描述,這個抽象描述方式起名爲IO流,對應的抽象類爲OutputStream和InputStream ,不一樣的實現類就表明不一樣的輸入和輸出設備,它們都是針對字節進行操做的。
在實際應用中,常常須要把徹底是字符的一段文本輸出去或讀進來,用字節流能夠嗎?計算機中的一切最終都是二進制的字節形式存在。對於「中國」這些字符,首先要獲得其對應的字節,而後將字節寫入到輸出流。讀取時,首先讀到的是字節,但是咱們要把它顯示爲字符,咱們須要將字節轉換成字符。因爲這樣的需求很普遍,人家專門提供了字符流的包裝類。
底層設備永遠只接受字節數據,有時候要寫字符串到底層設備,須要將字符串轉成字節再進行寫入。字符流是字節流的包裝,字符流則是直接接受字符串,它內部將串轉成字節,再寫入底層設備,這爲咱們向IO設別寫入或讀取字符串提供了一點點方便。
字符向字節轉換時,要注意編碼的問題,由於字符串轉成字節數組,實際上是轉成該字符的某種編碼的字節形式,讀取也是反之的道理。
講解字節流與字符流關係的代碼案例:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class IOTest {
public static void main(String[] args) throws Exception {
String str = "中國人";
/*FileOutputStream fos = new FileOutputStream("1.txt");
fos.write(str.getBytes("UTF-8"));
fos.close();*/
/*FileWriter fw = new FileWriter("1.txt");
fw.write(str);
fw.close();*/
PrintWriter pw = new PrintWriter("1.txt","utf-8");
pw.write(str);
pw.close();
/*FileReader fr = new FileReader("1.txt");
char[] buf = new char[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len);
System.out.println(myStr);*/
/*FileInputStream fr = new FileInputStream("1.txt");
byte[] buf = new byte[1024];
int len = fr.read(buf);
String myStr = new String(buf,0,len,"UTF-8");
System.out.println(myStr);*/
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("1.txt"),"UTF-8"
)
);
String myStr = br.readLine();
br.close();
System.out.println(myStr);
}
}
咱們有時候將一個java對象變成字節流的形式傳出去或者從一個字節流中恢復成一個java對象,例如,要將java對象存儲到硬盤或者傳送給網絡上的其餘計算機,這個過程咱們能夠本身寫代碼去把一個java對象變成某個格式的字節流再傳輸,可是,jre自己就提供了這種支持,咱們能夠調用OutputStream的writeObject方法來作,若是要讓java 幫咱們作,要被傳輸的對象必須實現serializable接口,這樣,javac編譯時就會進行特殊處理,編譯的類才能夠被writeObject方法操做,這就是所謂的序列化。須要被序列化的類必須實現Serializable接口,該接口是一個mini接口,其中沒有須要實現的方法,implements Serializable只是爲了標註該對象是可被序列化的。
例如,在web開發中,若是對象被保存在了Session中,tomcat在重啓時要把Session對象序列化到硬盤,這個對象就必須實現Serializable接口。若是對象要通過分佈式系統進行網絡傳輸或經過rmi等遠程調用,這就須要在網絡上傳輸對象,被傳輸的對象就必須實現Serializable接口。
JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader 是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。
java的內存分爲兩類,一類是棧內存,一類是堆內存。棧內存是指程序進入一個方法時,會爲這個方法單獨分配一塊私屬存儲空間,用於存儲這個方法內部的局部變量,當這個方法結束時,分配給這個方法的棧會釋放,這個棧中的變量也將隨之釋放。
堆是與棧做用不一樣的內存,通常用於存放不放在當前方法棧中的那些數據,例如,使用new建立的對象都放在堆裏,因此,它不會隨方法的結束而消失。方法中的局部變量使用final修飾後,放在堆中,而不是棧中。
GC:垃圾回收,使用GC能夠進行垃圾空間釋放操做。
GC是垃圾收集的意思(Gabage Collection),內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會致使程序或系統的不穩定甚至崩潰,Java提供的GC功能能夠自動監測對象是否超過做用域從而達到自動回收內存的目的,Java語言沒有提供釋放已分配內存的顯示操做方法。
Java語言中一個顯著的特色就是引入了垃圾回收機制,使c++程序員最頭疼的內存管理的問題迎刃而解,它使得Java程序員在編寫程序的時候再也不須要考慮內存管理。因爲有個垃圾回收機制,Java中的對象再也不有"做用域"的概念,只有對象的引用纔有"做用域"。垃圾回收能夠有效的防止內存泄露,有效的使用能夠使用的內存。垃圾回收器一般是做爲一個單獨的低級別的線程運行,不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清楚和回收,程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。回收機制有分代複製垃圾回收和標記垃圾回收,增量垃圾回收。
簡單答法:將無用的空間對象進行釋放.
倆中回收機制:自動回收和手工調用System.gc()方法,實際上調用System.gc就至關於調用了Runtime.getRuntime().gc()方法.
對於GC來講,當程序員建立對象時,GC就開始監控這個對象的地址、大小以及使用狀況。一般,GC採用有向圖的方式記錄和管理堆(heap)中的全部對象。經過這種方式肯定哪些對象是"可達的",哪些對象是"不可達的"。當GC肯定一些對象爲"不可達"時,GC就有責任回收這些內存空間。能夠。程序員能夠手動執行System.gc(),通知GC運行,可是Java語言規範並不保證GC必定會執行。
JDK1.4以後增長的新關鍵字——assert,表示斷言,即程序執行到某個地方以後值確定是預計好的;通常開發中不多使用assert;要想使用斷言,就必須使用-ea參數。
assertion(斷言)在軟件開發中是一種經常使用的調試方式,不少開發語言中都支持這種機制。在實現中,assertion就是在程序中的一條語句,它對一個boolean表達式進行檢查,一個正確程序必須保證這個boolean表達式的值爲true;若是該值爲false,說明程序已經處於不正確的狀態下,assert將給出警告或退出。通常來講,assertion用於保證程序最基本、關鍵的正確性。assertion檢查一般在開發和測試時開啓。爲了提升性能,在軟件發佈後,assertion檢查一般是關閉的。
package com.huawei.interview;
public class AssertTest {
public static void main(String[] args) {
int i = 0;
for(i=0;i<5;i++){
System.out.println(i);
}
//假設程序不當心多了一句--i;
--i;
assert i==5;
}
}
所謂內存泄露就是指一個再也不被程序使用的對象或變量一直被佔據在內存中。java中有垃圾回收機制,它能夠保證一對象再也不被引用的時候,即對象編程了孤兒的時候,對象將自動被垃圾回收器從內存中清除掉。因爲Java 使用有向圖的方式進行垃圾回收管理,能夠消除引用循環的問題,例若有兩個對象,相互引用,只要它們和根進程不可達的,那麼GC也是能夠回收它們的,例以下面的代碼能夠看到這種狀況的內存回收:
package com.huawei.interview;
import java.io.IOException;
public class GarbageTest {
public static void main(String[] args) throws IOException {
try {
gcTest();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("has exited gcTest!");
System.in.read();
System.in.read();
System.out.println("out begin gc!");
for(int i=0;i<100;i++){
System.gc();
System.in.read();
System.in.read();
}
}
private static void gcTest() throws IOException {
System.in.read();
System.in.read();
Person p1 = new Person();
System.in.read();
System.in.read();
Person p2 = new Person();
p1.setMate(p2);
p2.setMate(p1);
System.out.println("before exit gctest!");
System.in.read();
System.in.read();
System.gc();
System.out.println("exit gctest!");
}
private static class Person{
byte[] data = new byte[20000000];
Person mate = null;
public void setMate(Person other){
mate = other;
}
}
}
java中的內存泄露的狀況:長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄露,儘管短生命週期對象已經再也不須要,可是由於長生命週期對象持有它的引用而致使不能被回收,這就是java中內存泄露的發生場景,通俗地說,就是程序員可能建立了一個對象,之後一直再也不使用這個對象,這個對象卻一直被引用,即這個對象無用可是卻沒法被垃圾回收器回收的,這就是java中可能出現內存泄露的狀況,例如,緩存系統,咱們加載了一個對象放在緩存中(例如放在一個全局map對象中),而後一直再也不使用它,這個對象一直被緩存引用,但卻再也不被使用。
檢查java中的內存泄露,必定要讓程序將各類分支狀況都完整執行到程序結束,而後看某個對象是否被使用過,若是沒有,則才能斷定這個對象屬於內存泄露。
若是一個外部類的實例對象的方法返回了一個內部類的實例對象,這個內部類對象被長期引用了,即便那個外部類實例對象再也不被使用,但因爲內部類持久外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會形成內存泄露。
下面內容來自於網上(主要特色就是清空堆棧中的某個元素,並非完全把它從數組中拿掉,而是把存儲的總數減小,本人寫得能夠比這個好,在拿掉某個元素時,順便也讓它從數組中消失,將那個元素所在的位置的值設置爲null便可):
我實在想不到比那個堆棧更經典的例子了,以至於我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,固然若是沒有在書上看到,可能過一段時間我本身也想的到,但是那時我說是我本身想到的也沒有人相信的。
import java.util.EmptyStackException;
public class Stack {
private Object[] elements = new Object[10];
private int size = 0;
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if (elements.length == size) {
Object[] oldElements = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
}
上面的原理應該很簡單,假如堆棧加了10個元素,而後所有彈出來,雖然堆棧是空的,沒有咱們要的東西,可是這是個對象是沒法回收的,這個才符合了內存泄露的兩個條件:無用,沒法回收。
可是就是存在這樣的東西也不必定會致使什麼樣的後果,若是這個堆棧用的比較少,也就浪費了幾個K內存而已,反正咱們的內存都上G了,哪裏會有什麼影響,再說這個東西很快就會被回收的,有什麼關係。下面看兩個例子。
例子1
import java.util.Stack;
public class Bad {
public static Stack s = Stack();
static {
s.push(new Object());
s.pop(); // 這裏有一個對象發生內存泄露
s.push(new Object()); // 上面的對象能夠被回收了,等因而自愈了
}
}
由於是static,就一直存在到程序退出,可是咱們也能夠看到它有自愈功能,就是說若是你的Stack最多有100個對象,那麼最多也就只有100個對象沒法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的狀況就是他們都是無用的,由於咱們一旦放新的進取,之前的引用天然消失!
內存泄露的另一種狀況:當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段了,不然,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,在這種狀況下,即便在contains方法使用該對象的當前引用做爲的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會致使沒法從HashSet集合中單獨刪除當前對象,形成內存泄露。
能夠,但在應用的時候,須要用本身的類加載器去加載,不然,系統的類加載器永遠只是去加載jre.jar包中的那個java.lang.String。因爲在tomcat的web應用程序中,都是由webapp本身的類加載器先本身加載WEB-INF/classess目錄中的類,而後才委託上級的類加載器加載,若是咱們在tomcat的web應用程序中寫一個java.lang.String,這時候Servlet程序加載的就是咱們本身寫的java.lang.String,可是這麼幹就會出不少潛在的問題,原來全部用了java.lang.String類的都將出現問題。
雖然java提供了endorsed技術,能夠覆蓋jdk中的某些類,具體作法是….。可是,可以被覆蓋的類是有限制範圍,反正不包括java.lang這樣的包中的類。
(下面的例如主要是便於你們學習理解只用,不要做爲答案的一部分,不然,人家懷疑是題目泄露了)例如,運行下面的程序:
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("string");
}
}
報告的錯誤以下:
java.lang.NoSuchMethodError: main
Exception in thread "main"
這是由於加載了jre自帶的java.lang.String,而該類中沒有main方法。