1、Java 基礎部分javascript
Java 有 8 種基本數據類型: byte int short long double float Boolean charhtml
byte int short long 都屬於整數類型.前端
Double float 屬於浮點類型.java
Boolean 爲布爾類型node
Char 爲字符型mysql
String 不是基本數據類型.它定義的爲對象c++
能夠有多個類,但只能有一個 public 的類,而且 public 的類名必須與文件名相一致。git
java 中的保留字,如今沒有在 java 中使用。程序員
&和&&均可以用做邏輯與的運算符,表示邏輯與(and),當運算符兩邊的表達式的結果都web
爲 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 是什麼。
47. 什麼是內部類?Static Nested Class和 Inner Class的不一樣?
使用 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 方法轉爲掛起狀態,當線程關聯的代碼執行完後,線程變爲結束狀態。
77. 簡述 synchronized和 java.util.concurrent.locks.Lock的異同 ?
主要相同點: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 Locklock= 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);
90. Collection 和 Collections 的區別。
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 Stacks=Stack();
static {
s.push(newObject());
s.pop();// 這裏有一個對象發生內存泄露
s.push(newObject());// 上面的對象能夠被回收了,等因而自愈了
}
}
由於是 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 方法。
2、Java 代碼查錯
abstract class Name {
private String name;
public abstract boolean isStupidName(String name) {}
}
大俠們,這有何錯誤?
答案: 錯。abstract method 必須以分號結尾,且不帶花括號。
2.public class Something {
void doSomething () {
private String s = "";
int l = s.length();
}
}
有錯嗎?
答案: 錯。局部變量前不能放置任何訪問修飾符 (private,public,和 protected)。final 能夠用來修飾局部變量
(final 如同 abstract 和 strictfp,都是非訪問修飾符,strictfp 只能修飾 class 和 method 而非variable)。
3.abstract class Something {
private abstract String doSomething ();
}
這好像沒什麼錯吧?
答案: 錯。abstract 的 methods 不能以 private 修飾。abstract 的 methods 就是讓子類 implement(實現)具體細節的,怎麼能夠用 private 把 abstract
method 封鎖起來呢? (同理,abstract method 前不能加 final)。
4.public class Something {
public int addOne(final int x) {
return ++x;
}
}
這個比較明顯。
答案: 錯。int x 被修飾成 final,意味着 x 不能在 addOne method 中被修改。
5.public class Something {
public static void main(String[] args) {
Other o = new Other();
new Something().addOne(o);
}
public void addOne(final Other o) {
o.i++;
}
}
class Other {
public int i;
}
和上面的很類似,都是關於 final 的問題,這有錯嗎?
答案: 正確。在 addOne method 中,參數 o 被修飾成 final。若是在 addOne method 裏咱們修改了 o 的 reference
(好比: o = new Other();),那麼如同上例這題也是錯的。但這裏修改的是 o 的 member vairable (成員變量),而 o 的 reference 並無改變。
6.class Something {
int i;
public void doSomething() {
System.out.println("i = " + i);
}
}
有什麼錯呢? 看不出來啊。
答案: 正確。輸出的是"i = 0"。int i 屬於 instant variable (實例變量,或叫成員變量)。instant
variable 有 default value。int 的 default value 是 0。
7.class Something {
final int i;
public void doSomething() {
System.out.println("i = " + i);
}
}
和上面一題只有一個地方不一樣,就是多了一個 final。這難道就錯了嗎?
答案: 錯。final int i 是個 final 的 instant variable (實例變量,或叫成員變量)。final 的 instant variable 沒有 default value,必須在 constructor (構造器)結束以前被賦予一個明確的值。能夠修改成"final int i = 0;"。
8.public class Something {
public static void main(String[] args) {
Something s = new Something();
System.out.println("s.doSomething() returns " + doSomething());
}
public String doSomething() {
return "Do something ...";
}
}
看上去很完美。
答案: 錯。看上去在 main 裏 call doSomething 沒有什麼問題,畢竟兩個 methods 都在同一個 class 裏。但仔細看,main 是 static 的。static method 不能直接 call non-static methods。可改
成 "System.out.println("s.doSomething() returns " + s.doSomething());"。同理,static method 不能訪問 non-static instant variable。
9.此處,Something 類的文件名叫 OtherThing.java class Something {
private static void main(String[] something_to_do) { System.out.println("Do something ...");
}
}
這個好像很明顯。
答案: 正確。歷來沒有人說過 Java 的 Class 名字必須和其文件名相同。但 public class 的名字必須和文件名相同。
10.interface A{
int x = 0;
}
class B{
int x =1;
}
class C extends B implements A {
public void pX(){
System.out.println(x);
}
public static void main(String[] args) {
new C().pX();
}
}
答案:錯誤。在編譯時會發生錯誤(錯誤描述不一樣的 JVM 有不一樣的信息,意思就是未明確的 x 調用,兩個 x 都匹配(就象在同時 import java.util 和 java.sql 兩個包時直接聲明 Date 同樣)。對於父類的變量,能夠用 super.x 來明確,而接口的屬性默認隱含爲 public static final.因此能夠經過 A.x 來明確。
11.interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable, Bounceable {
Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
ball = new Ball("Football");
System.out.println(ball.getName());
}
}
這個錯誤不容易發現。
答案: 錯。"interface Rollable extends Playable, Bounceable"沒有問題。interface 可繼承多個interfaces,因此這裏沒錯。問題出在 interface Rollable 裏的"Ball ball = new Ball("PingPang");"。任何在 interface 裏聲明的 interface variable (接口變量,也可稱成員變量),默認爲 public static final。也就是說"Ball ball = new Ball("PingPang");"其實是"public static final Ball ball = new Ball("PingPang");"。在 Ball 類的 Play()方法中,"ball = new Ball("Football");"改變了 ball 的reference,而這裏的 ball 來自 Rollable interface,Rollable interface 裏的 ball 是 public static final 的,final 的 object 是不能被改變 reference 的。所以編譯器將在"ball = new Ball("Football");" 這裏顯示有錯。
3、算法與編程
用插入法進行排序代碼以下
package test;
import java.util.*;
class InsertSort {
ArrayList al; //定義一個鏈表
public InsertSort(int num,int mod) //帶參數的構造函數{
al = new ArrayList(num); //實例化鏈表
Random rand = new Random(); //取一個隨機數
System.out.println("The ArrayList Sort Before:");
for (int i=0;i<num ;i++ ) {
al.add(new Integer(Math.abs(rand.nextInt()) % mod + 1)); System.out.println("al["+i+"]="+al.get(i));
}
}
public void SortIt() {
Integer tempInt;
int MaxSize=1;
for(int i=1;i<al.size();i++) {
tempInt = (Integer)al.remove(i);
if(tempInt.intValue()>=((Integer)al.get(MaxSize-1)).intValue()) {
al.add(MaxSize,tempInt);
MaxSize++;
System.out.println(al.toString());
} else{
for (int j=0;j<MaxSize ;j++ ) {
if (((Integer)al.get(j)).intValue()>=tempInt.intValue()) { al.add(j,tempInt);
MaxSize++;
System.out.println(al.toString());
break;
}
}
}
}
System.out.println("The ArrayList Sort After:");
for(int i=0;i<al.size();i++) {
System.out.println("al["+i+"]="+al.get(i));
}
}
public static void main(String[] args) {
InsertSort is = new InsertSort(10,100); //定義一個類的對象
is.SortIt(); //調用類中的方法, 執行排序
}
}
JAVA 類實現序例化的方法是實現 java.io.Serializable 接口
Collection 框架中實現比較要實現 Comparable 接口和 Comparator 接口
import java.lang.reflect.Array;
public class IntArrayDemo {
public static void main(String[] args) {
Class c = int.class;
Object objArr = Array.newInstance(c, 10);
for(int i = 0; i < 10; i++) {
Array.set(objArr, i, i);
}
int[] arr=(int[])objArr;
for(int j = 0; j < arr.length; j++) {
System.out.println(arr[j]);
}
}
}
答:代碼以下:
package test;
public class CountGame {
private static boolean same(int[] p, int l, int n) {
for (int i = 0; i < l; i++) {
if (p[i] == n) {
return true;
}
}
return false;
}
public static void play(int playerNum, int step) {
int[] p = new int[playerNum];
int counter = 1;
while (true) {
if (counter > playerNum * step) {
break;
}
for (int i = 1; i < playerNum + 1; i++) {
while (true) {
if (same(p, playerNum, i) == false)
break;
else
i = i + 1;
}
if (i > playerNum)
break;
if (counter % step == 0) {
System.out.print(i + " ");
p[counter / step - 1] = i;
}
counter += 1;
}
}
System.out.println();
}
public static void main(String[] args) {
play(10, 7);
}
}
package cn.itcast;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
public class MainClass {
public static void main(String[] args) throws Exception {FileManager a = new FileManager("a.txt", new char[] { '\n' }); FileManager b = new FileManager("b.txt", new char[] { '\n', ' ' }); FileWriter c = new FileWriter("c.txt");
String aWord = null;
String bWord = null;
while ((aWord = a.nextWord()) != null) {
c.write(aWord + "\n");
bWord = b.nextWord();
if (bWord != null)
c.write(bWord + "\n");
}
while ((bWord = b.nextWord()) != null) {
c.write(bWord + "\n");
}
c.close();
}
}
class FileManager {
String[] words = null;
int pos= 0;
public FileManager(String filename, char[] seperators) throws Exception {File f = new File(filename);
FileReader reader = new FileReader(f);
char[] buf = new char[(int) f.length()];
int len = reader.read(buf);
String results = new String(buf, 0, len);
String regex = null;
if (seperators.length> 1) {
regex = "" + seperators[0] + "|" + seperators[1];
} else {
regex = "" + seperators[0];
}
words = results.split(regex);
}
public String nextWord() {
if (pos==words.length)
return null;
return words[pos++];
}
}
答:listFiles 方法接受一個 FileFilter 對象,這個 FileFilter 對象就是過慮的策略對象,不一樣的人提供不一樣的 FileFilter 實現,即提供了不一樣的過濾策略。
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Jad2Java {
public static void main(String[] args) throws Exception {
File srcDir = new File("java");
if (!(srcDir.exists() && srcDir.isDirectory()))
throw new Exception("目錄不存在");
File[] files = srcDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
});
System.out.println(files.length);
File destDir = new File("jad");
if (!destDir.exists())
destDir.mkdir();
for (File f : files) {
FileInputStream fis = new FileInputStream(f);
String destFileName = f.getName().replaceAll("\\.java$", ".jad"); FileOutputStream fos = new FileOutputStream(new File(destDir,
destFileName));
copy(fis, fos);
fis.close();
fos.close();
}
}
private static void copy(InputStream ips, OutputStream ops)
throws Exception {
int len = 0;
byte[] buf = new byte[1024];
while ((len = ips.read(buf)) != -1) {
ops.write(buf, 0, len);
}
}
}
由本題總結的思想及策略模式的解析:
class jad2java{
1.1 獲得目錄 File srcDir = new File("d:\\java");
1.2 獲得目錄下的全部 java 文件:File[] files = srcDir.listFiles(new MyFileFilter());
1.3 只想獲得.java 的文件: class MyFileFilter implememyts FileFilter{ public boolean accept(File pathname){
return pathname.getName().endsWith(".java")
}
}
2.將每一個文件複製到另一個目錄,並改擴展名
2.1 獲得目標目錄,若是目標目錄不存在,則建立之
2.2 根據源文件名獲得目標文件名,注意要用正則表達式,注意.的轉義。
2.3 根據表示目錄的 File 和目標文件名的字符串,獲得表示目標文件的 File。
//要在硬盤中準確地建立出一個文件,須要知道文件名和文件的目錄。
2.4 將源文件的流拷貝成目標文件流,拷貝方法獨立成爲一個方法,方法的參數採用抽象流的形式。
//方法接受的參數類型儘可能面向父類,越抽象越好,這樣適應面更寬廣。
}
分析 listFiles 方法內部的策略模式實現原理
File[] listFiles(FileFilter filter){
File[] files = listFiles();
//Arraylist acceptedFilesList = new ArrayList();
File[] acceptedFiles = new File[files.length];
int pos = 0;
for(File file: files){
boolean accepted = filter.accept(file);
if(accepted){
//acceptedFilesList.add(file);
acceptedFiles[pos++] = file;
}
}
Arrays.copyOf(acceptedFiles,pos);
//return (File[])accpetedFilesList.toArray();
}
答:首先要了解中文字符有多種編碼及各類編碼的特徵。
假設 n 爲要截取的字節數。
public static void main(String[] args) throws Exception{ String str = "我 a 愛中華 abc 我愛中國 def'; String str = "我 ABC 漢";
int num = trimGBK(str.getBytes("GBK"),5);
System.out.println(str.substring(0,num) );
}
public static int trimGBK(byte[] buf,int n){
int num = 0;
boolean bChineseFirstHalf = false;
for(int i=0;i<n;i++){
if(buf[i]<0 && !bChineseFirstHalf){
bChineseFirstHalf = true;
}else{
num++;
bChineseFirstHalf = false;
}
}
return num;
}
答:哈哈,其實包含中文字符、英文字符、數字字符原來是出題者放的煙霧彈。 String content = 「中國 aadf 的 111 薩 bbb 菲的 zz 薩菲」; HashMap map = new HashMap();
for(int i=0;i<content.length;i++){
char c = content.charAt(i);
Integer num = map.get(c);
if(num == null)
num = 1;
else
num = num + 1;
map.put(c,num);
}
for(Map.EntrySet entry : map){
system.out.println(entry.getkey() + 「:」 + entry.getValue());
}
估計是當初面試的那我的表述不清楚,問題極可能是:
若是一串字符如"aaaabbc 中國 1512"要分別統計英文字符的數量,中文字符的數量,和數字字符的數量,假設字符中沒有中文字符、英文字符、數字字符以外的其餘特殊字符。
int engishCount;
int chineseCount;
int digitCount;
for(int i=0;i<str.length;i++){
char ch = str.charAt(i);
if(ch>=’0’ && ch<=’9’){
digitCount++
}
else if((ch>=’a’ && ch<=’z’) || (ch>=’A’ && ch<=’Z’)){ engishCount++;
}else{
chineseCount++;
}
}
System.out.println(……………);
這是組合設計模式。
我有不少個(假設 10 萬個)數據要保存起來,之後還須要從保存的這些數據中檢索是否存在某個數據,(我想說出二叉樹的好處,該怎麼說呢?那就是說別人的缺點),假如存在數組中,那麼,碰巧要找的數字位於 99999 那個地方,那查找的速度將很慢,由於要從第 1 個依次日後取,取出來後進行比較。平衡二叉樹(構建平衡二叉樹須要先排序,咱們這裏就不做考慮了)能夠很好地解決這個問題,但二叉樹的遍歷(前序,中序,後序)效率要比數組低不少,原理以下圖:
代碼以下:
package com.huawei.interview;
public class Node {
public int value;
public Nodeleft;
public Noderight;
public void store(int value){
if(value<this.value){
if(left== null){
left = new Node();
left.value=value;
}
else{
left.store(value);
}
}
else if(value>this.value){
if(right== null){
right = new Node();
right.value=value;
}
else{
right.store(value);
}
}
}
public boolean find(int value){
System.out.println("happen " + this.value);
if(value == this.value){
return true;
}
else if(value>this.value){
if(right== null) return false;
return right.find(value);
}else{
if(left == null) return false;
return left.find(value);
}
}
public void preList(){
System.out.print(this.value + ",");
if(left!=null)left.preList();
if(right!=null)right.preList();
}
public void middleList(){
if(left!=null)left.preList();
System.out.print(this.value + ",");
if(right!=null)right.preList();
}
public void afterList(){
if(left!=null)left.preList();
if(right!=null)right.preList();
System.out.print(this.value + ",");
}
public static void main(String [] args){
int [] data = new int[20];
for(int i=0;i<data.length;i++){
data[i] = (int)(Math.random()*100) + 1;
System.out.print(data[i] + ",");
}
System.out.println();
Node root = new Node();
root.value = data[0];
for(int i=1;i<data.length;i++){
root.store(data[i]);
}
root.find(data[19]);
root.preList();
System.out.println();
root.middleList();
System.out.println();
root.afterList();
}
}
-----------------又一次臨場寫的代碼---------------------------
import java.util.Arrays;
import java.util.Iterator;
public class Node {
private Node left;
private Node right;
private int value;
//private int num;
public Node(int value){
this.value = value;
}
public void add(int value){
if(value > this.value){
if(right != null)
right.add(value);
else{
Node node = new Node(value);
right = node;
}
}
else{
if(left != null)
left.add(value);
else{
Node node = new Node(value);
left = node;
}
}
}
public boolean find(int value){
if(value == this.value) return true;
else if(value > this.value){
if(right == null) return false;
else return right.find(value);
}else{
if(left == null) return false;
else return left.find(value);
}
}
public void display(){
System.out.println(value);
if(left != null) left.display();
if(right != null) right.display();
}
/*public Iterator iterator(){
}*/
public static void main(String[] args){
int[] values = new int[8];
for(int i=0;i<8;i++){
int num = (int)(Math.random() * 15);
//System.out.println(num);
//if(Arrays.binarySearch(values, num)<0)
if(!contains(values,num))
values[i] = num;
else
i--;
}
System.out.println(Arrays.toString(values));
Node root = new Node(values[0]);
for(int i=1;i<values.length;i++){
root.add(values[i]);
}
System.out.println(root.find(13));
root.display();
}
public static boolean contains(int [] arr, int value){
int i = 0;
for(;i<arr.length;i++){
if(arr[i] == value) return true;
}
return false;
}
}
1,張三,28
2,李四,35
3,張三,28
4,王五,35
5,張三,28
6,李四,35
7,趙六,28
8,田七,35
程序代碼以下(答題要博得用人單位的喜歡,包名用該公司,面試前就提早查好該公司的網址,若是查不到,現場問也是能夠的。還要加上實現思路的註釋):
package com.huawei.interview;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
public class GetNameTest {
public static void main(String[] args) {
//InputStream ips = GetNameTest.class.getResourceAsStream("/com/huawei/interview/info.txt");
//用上一行註釋的代碼和下一行的代碼均可以,由於 info.txt 與 GetNameTest 類在同一包下面,因此,能夠用下面的相對路徑形式
Map results = new HashMap();
InputStream ips = GetNameTest.class.getResourceAsStream("info.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(ips));
String line = null;
try {
while((line=in.readLine())!=null){
dealLine(line,results);
}
sortResults(results);
} catch (IOException e) { e.printStackTrace();
}
}
static class User{
public String name;
public Integervalue;
public User(String name,Integer value){
this.name= name;
this.value= value;
}
public boolean equals(Object obj) {
//下面的代碼沒有執行,說明往 treeset 中增長數據時,不會使用到 equals 方法。
boolean result = super.equals(obj);
System.out.println(result);
return result;
}
}
private static void sortResults(Map results) {
TreeSet sortedResults = new TreeSet(
new Comparator(){
public int compare(Object o1, Object o2) {
User user1 = (User)o1;
User user2 = (User)o2;
/*若是 compareTo 返回結果 0,則認爲兩個對象相等,新的對象
不會增長到集合中去
* 因此,不能直接用下面的代碼,不然,那些個數相同的其餘
姓名就打印不出來。
* */
//return user1.value-user2.value;
//return user1.value<user2.value?-1:user1.value==user2.value?0:1; if(user1.value<user2.value) {
return -1;
}else if(user1.value>user2.value){
return 1;
}else{
return user1.name.compareTo(user2.name);
}
}
}
);
Iterator iterator = results.keySet().iterator();
while(iterator.hasNext()){
String name = (String)iterator.next();
Integer value = (Integer)results.get(name);
if(value > 1){
sortedResults.add(new User(name,value));
}
}
printResults(sortedResults);
}
private static void printResults(TreeSet sortedResults) {Iterator iterator = sortedResults.iterator(); while(iterator.hasNext()){
User user = (User)iterator.next();
System.out.println(user.name + ":" + user.value);
}
}
public static void dealLine(String line,Map map){
if(!"".equals(line.trim())){
String [] results = line.split(",");
if(results.length== 3){
String name = results[1];
Integer value = (Integer)map.get(name);
if(value == null) value = 0;
map.put(name,value + 1);
}
}
}
}
第一種:飽漢模式
public class SingleTon {
private SingleTon(){
}
//實例化放在靜態代碼塊裏可提升程序的執行效率,但也可能更佔用空間 private final static SingleTon instance = new SingleTon(); public static SingleTon getInstance(){
return instance;
}
}
第二種:飢漢模式
public class SingleTon {
private SingleTon(){}
private static instance = null;//new SingleTon();
public static synchronized SingleTon getInstance(){
if(instance == null)
instance = new SingleTon();
return instance;
}
}
第三種:用枚舉
public enum SingleTon{
ONE;
}
第三:更實際的應用(在什麼狀況用單例)
public class SequenceGenerator{
//下面是該類自身的業務功能代碼
private int count = 0;
public synchronized int getSequence(){
++count;
}
//下面是把該類變成單例的代碼
private SequenceGenerator(){}
private final static instance = new SequenceGenerator(); public static SingleTon getInstance(){
return instance;
}
}
第四:public class MemoryDao{
private HashMap map = new HashMap();
public void add(Student stu1){
map.put(SequenceGenerator.getInstance().getSequence(),stu1);
}
//把 MemoryDao 變成單例
}
Singleton 模式主要做用是保證在 Java 應用程序中,一個類 Class 只有一個實例存在。
通常 Singleton 模式一般有幾種種形式:
第一種形式: 定義一個類,它的構造函數爲 private 的,它有一個 static 的 private 的該類變量,在類初始化時實例話,經過一個 public 的 getInstance 方法獲取對它的引用,繼而調用其中的方法。
public class Singleton {
private Singleton(){}
//在本身內部定義本身一個實例,是否是很奇怪?
//注意這是 private 只供內部調用
private static Singleton instance = new Singleton();
//這裏提供了一個供外部訪問本 class 的靜態方法,能夠直接訪問 public static Singleton getInstance() {
return instance;
}
}
第二種形式:
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次
//使用時生成實例,提升了效率!
if (instance==null)
instance=new Singleton();
return instance;
}
}
其餘形式:
定義一個類,它的構造函數爲 private 的,全部方法爲 static 的。
通常認爲第一種形式要更加安全些
一個整數,大於 0,不用循環和本地變量,按照 n,2n,4n,8n 的順序遞增,當值大於 5000 時,把值按照指定順序輸出來。
例:n=1237
則輸出爲:
1237,
2474,
4948,
9896,
9896,
4948,
2474,
1237,
提示:寫程序時,先致謝按遞增方式的代碼,寫好遞增的之後,再增長考慮遞減部分。 public static void doubleNum(int n){
System.out.println(n);
if(n<=5000)
doubleNum(n*2);
System.out.println(n);
}
Gaibaota(N) = Gaibaota(N-1) + n
第 1 我的 10 歲,第 2 個比第 1 我的大 2 歲,依次遞推,請用遞歸方式計算出第 8 我的多大? package cn.itcast;
import java.util.Date;
public class A1 {
public static void main(String [] args){
System.out.println(computeAge(8));
}
public static int computeAge(int n){
if(n==1) return 10;
return computeAge(n-1) + 2;
}
public static void toBinary(int n,StringBuffer result){ if(n/2 != 0)
toBinary(n/2,result);
result.append(n%2);
}
}
本人只研究過冒泡排序、選擇排序和快速排序,下面是快速排序的代碼:
public class QuickSort {
public void quickSort(String[] strDate, int left, int right) {String middle, tempDate;
int i, j;
i = left;
j = right;
middle = strDate[(i + j) / 2];
do {
while (strDate[i].compareTo(middle) < 0 && i <right)
i++; // 找出左邊比中間值大的數
while (strDate[j].compareTo(middle) > 0 && j > left)
j--; // 找出右邊比中間值小的數
if (i <= j) {//將左邊大的數和右邊小的數進行替換tempDate = strDate[i];
strDate[i] = strDate[j];
strDate[j] = tempDate;
i++;
j--;
}
} while (i <= j); // 當二者交錯時中止
if (i <right){
quickSort(strDate, i, right);// 從
}
if (j > left) {
quickSort(strDate, left, j);
}
}
public static void main(String[] args) {
String[] strVoid = new String[] { "11", "66", "22", "0", "55", "22", "0", "32" };
QuickSort sort = new QuickSort();
sort.quickSort(strVoid, 0, strVoid.length - 1);
for (int i = 0; i < strVoid.length; i++) {
System.out.println(strVoid[i] + " ");
}
}
}
//用下面的也能夠
//for(int i=0,int j=a.length-1;i<j;i++,j--) 是否等效於 for(int i=0;i<a.length/2;i++)呢?
import java.util.Arrays;
public class SwapDemo{
public static void main(String[] args){
int [] a = new int[]{
(int)(Math.random() * 1000),
(int)(Math.random() * 1000),
(int)(Math.random() * 1000),
(int)(Math.random() * 1000),
(int)(Math.random() * 1000)
};
System.out.println(a);
System.out.println(Arrays.toString(a));
swap(a);
System.out.println(Arrays.toString(a));
}
public static void swap(int a[]){
int len = a.length;
for(int i=0;i<len/2;i++){
int tmp = a[i];
a[i] = a[len-1-i];
a[len-1-i] = tmp;
}
}
}
去零的代碼:
return sb.reverse().toString().replaceAll(" 零 [ 拾 佰 仟 ]"," 零 ").replaceAll(" 零 + 萬 "," 萬").replaceAll("零+元","元").replaceAll("零+","零");
public class RenMingBi {
private static final char[] data = new char[]{
'零','壹','貳','叄','肆','伍','陸','柒','捌','玖'
};
private static final char[] units = new char[]{
'元','拾','佰','仟','萬','拾','佰','仟','億'
};
public static void main(String[] args) {
// TODO Auto-generated method stub System.out.println(
convert(135689123));
}
public static String convert(int money){
StringBuffer sbf = new StringBuffer();
int unit = 0;
while(money!=0){
sbf.insert(0,units[unit++]);
int number = money%10;
sbf.insert(0, data[number]);
money /= 10;
}
return sbf.toString();
}
}
4、html&JavaScript&ajax 部分
如何用腳本判斷用戶輸入的的字符串是下面的時間格式 2004-11-21 必需要保證用戶的輸入
是此格式,而且是時間,好比說月份不大於 12 等等,另外我須要用戶輸入兩個,而且後一
個要比前一個晚,只容許用 JAVASCRIPT,請詳細幫助做答,,
//這裏可用正則表達式判斷提早判斷一下格式,而後按下提取各時間字段內容
<script type="text/javascript">
window.onload = function(){
//這麼寫是爲了實現 js 代碼與 html 代碼的分離,當我修改 js 時,不能影響 html 代
碼。
document.getElementById("frm1").onsubmit =
function(){
var d1 = this.d1.value;
var d2 = this.d2.value;
if(!verifyDate (d1)) {alert("第一個日期格式不對");return false;} if(!verifyDate (d2)) {alert("第二個日期格式不對");return false;} if(!compareDate(d1,d2)) {alert(" 第二個日期比第一日期小");return false;}
};
}
function compareDate(d1,d2){
var arrayD1 = d1.split("-");
var date1 = new Date(arrayD1[0],arrayD1[1],arrayD1[2]); var arrayD2 = d2.split("-");
var date2 = new Date(arrayD2[0],arrayD2[1],arrayD2[2]); if(date1 > date2) return false; return true;
}
function verifyDate(d){
var datePattern = /^\d{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2]\d|3[0-1])$/; return datePattern.test(d);
}
</script>
<form id="frm1" action="xxx.html">
<input type="text" name="d1" />
<input type="text" name="d2" />
<input type="submit"/>
</form>
<body>
<table id="tbl">
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
<tr><td>5</td></tr>
<tr><td>6</td></tr>
<tr><td>7</td></tr>
<tr><td>8</td></tr>
<tr><td>9</td></tr>
<tr><td>10</td></tr>
</table>
</body>
<script type="text/javascript">
window.onload=function(){
var tbl = document.getElementById("tbl");
rows = tbl.getElementsByTagName("tr");
for(i=0;i<rows.length;i++){
var j = parseInt(i/3);
if(j%2==0) rows[i].style.backgroundColor="#f00";
else rows[i].style.backgroundColor="#0f0";
}
}
</script>
<form onsubmit=’return chkForm(this)’>
<input type="text" name="d1"/>
<input type="submit"/>
</form>
<script type=」text/javascript」 />
function chkForm(this){
var value = thist.d1.value;
var len = value.length;
for(var i=0;i<len;i++){
if(value.charAt(i)>"9" || value.charAt(i)<"0"){
alert("含有非數字字符");
return false;
}
}
return true;
}
</script>
<input type="text" id="d1" onblur=" chkNumber (this)"/> <script type=」text/javascript」 /> function chkNumber(eleText){
var value = eleText.value;
var len = value.length;
for(var i=0;i<len;i++){
if(value.charAt(i)>"9" || value.charAt(i)<"0"){
alert("含有非數字字符");
eleText.focus();
break;
}
}
}
</script>
除了寫完代碼,還應該在網頁上寫出實驗步驟和在代碼中加入實現思路,讓面試官一看就明白你的意圖和檢查你的結果。
答:去掉對 web.xml 的監視,把 jsp 提早編輯成 Servlet。
有富餘物理內存的狀況,加大 tomcat 使用的 jvm 的內存
5、Java web 部分
答:去掉對 web.xml 的監視,把 jsp 提早編輯成 Servlet。有富餘物理內存的狀況,加大 tomcat 使用的 jvm 的內存
當點擊一個連接時,瀏覽器首先找到站點的 IP 地址,這是經過 DNS 來實現的,在找到 IP 地址後就能夠創建 TCP 鏈接了,鏈接創建後咱們就能夠發送請求了.但這個請求是什麼樣子的呢 ? 咱們如今假設點擊了一個從 www.webmonkey.com/HTML/96/47/Index2A , HTML 點擊了 WWW.GRIPY.ORG/MATTARG/ 這時瀏覽器會發出下面的請求: Get/MATTARG/HTML/1.0
User-Agent: Mozilla/2.0(macitosh;1;PPC)
Accept: text/html: */*
Cookie: name = value
Refetet: http://www.webmonkey.com/html/96/47/index2a.html
Host: www.gtippy.org
第一行稱爲請求,它告訴服務器從 MATTMARG 取得文件,這是的目錄通常是要加 / 的,下面幾行通知服務器你所使用的瀏覽器是什麼類型,你所接收的數據是什麼類型,若是你之前訪問過這個站點,站點可能向你發送了 Cookie ,若是你已經有了一個這樣的 Cookie ,瀏覽器會將這個 Cookie 返回給服務器, referer 行通知服務器用戶從哪一頁到達此頁的.
下面服務器就要返回文件了,每次服務器返回文件時,都要返回一個 Http/1.0 響應,同進帶有狀態碼,在此以後是述內部的頭信息,下面就是一個響應: HTTP/1.0 200 Pound
Data: Mon 10 Feb 1997 23:48:22 GMT
Server: Apache/1.1 1 Hot&ired/1.0
Content-type: text/html
Last-Moditied: Tues,11 Feb 1997 22:45:55 GMT
不一樣的數據可能返回不一樣的 Content-type ,所以不一樣的內容須要不一樣的 Content-type ,所以有時候這個過程是很慢的。
答:Form 中的 get 和 post 方法,在數據傳輸過程當中分別對應了 HTTP 協議中的 GET 和 POST 方法。兩者主要區別以下:
1)Get 是用來從服務器上得到數據,而 Post 是用來向服務器上傳遞數據;
2)Get 將表單中數據按照 variable=value 的形式,添加到 action 所指向的 URL 後面,而且二者使用「?」鏈接,而各個變量之間使用「&」鏈接;Post 是將表單中的數據放在 form 的數據體中,按照變量和值相對應的方式,傳遞到 action 所指向 URL;
3)Get 是不安全的,由於在傳輸過程,數據被放在請求的 URL 中;Post 的全部操做對用戶來講都是不可見的;
4)Get 傳輸的數據量小,這主要是由於受 URL 長度限制;而 Post 能夠傳輸大量的數據,因此在上傳文件只能使用 Post;
5)Get 限制 Form 表單的數據集必須爲 ASCII 字符,而 Post 支持整個 ISO10646 字符集;
6)Get 是 Form 的默認方法。
Servlet 是一種獨立於平臺和協議的服務器端的 Java 技術,能夠用來生成動態的 Web 頁面。與傳統的 CGI(計算機圖形接口)和許多其餘相似 CGI 技術相比,Servlet 具備更好的可移植性、更強大的功能,更少的投資,更高的效率,更好的安全性等特色。
Servlet 是使用 Java Servlet 應用程序接口(API)及相關類和方法的 Java 程序。Java 語言可以實現的功能,Servlet 基本上都能實現(除了圖形界面以外)。Servlet 主要用於處理客戶端傳來的 Http 請求,並返回一個響應。一般所說的 Servlet 就是指 HttpServlet,用於處理 Http 請求,其可以處理的請求有 doGet()、doPost()、service()等方法。在開發 Servlet 時,能夠直接繼承 javax.servlet.http.HttpServlet。
Servlet 須要在 web.xml 中進行描述,例如:映射執行 Servlet 的名字,配置 Servlet 類、初始化參數,進行安全配置、URL 映射和設置啓動的優先權等。Servlet 不只能夠生成 HTML 腳本輸出,也能夠生成二進制表單輸出。
Servlet 應用範圍很普遍,咱們如今用的不少流行的框架技術,其最基本的代碼離不開 Servelt 的支持。好比我所熟悉的 SSH 框架,Spring 容器啓動時,要在 web.xml 中裝載 Spring 容器的 ActionContext 類來初始化 Spring 的一些參數,如進行依賴注入、數據庫表的映射、初始化系統的安全配置設置 read 等屬性等一些相關操做。
答:servlet 有良好的生存期的定義,包括加載和實例化、初始化、處理請求以及服務結束。
這個生存期由 javax.servlet.Servlet 接口的 init,service 和 destroy 方法表達。
Servlet 被服務器實例化後,容器運行其 init 方法,請求到達時運行其 service 方法,service方法自動派遣運行與請求對應的 doXXX 方法(doGet,doPost)等,當服務器決定將實例銷燬的時候調用其 destroy 方法。
web 容器加載 servlet,生命週期開始。經過調用 servlet 的 init()方法進行 servlet 的初始化。經過調用 service()方法實現,根據請求的不一樣調用不一樣的 do***()方法。結束服務,web 容器調用 servlet 的 destroy()方法。
package test;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletName extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
答:前者僅是容器中控制權的轉向,在客戶端瀏覽器地址欄中不會顯示出轉向後的地址;後者則是徹底的跳轉,瀏覽器將會獲得跳轉的地址,並從新發送請求連接。這樣,從瀏覽器的地址欄中能夠看到跳轉後的連接地址。因此,前者更加高效,在前者能夠知足須要時,儘可能使用 forward()方法,而且,這樣也有助於隱藏實際的連接。在有些狀況下,好比,須要跳轉到一個其它服務器上的資源,則必須使用 sendRedirect()方法。
Jsp 頁面中的 FORM 標籤裏的 method 屬性爲 get 時調用 doGet(),爲 post 時調用 doPost()。
setAttribute(String name,Object):設置名字爲 name 的 request 的參數值
getAttribute(String name):返回由 name 指定的屬性值
getAttributeNames():返回 request 對象全部屬性的名字集合,結果是一個枚舉的實例
getCookies():返回客戶端的全部 Cookie 對象,結果是一個 Cookie 數組
getCharacterEncoding():返回請求中的字符編碼方式
getContentLength():返回請求的 Body 的長度
getHeader(String name):得到 HTTP 協議定義的文件頭信息
getHeaders(String name):返回指定名字的 request Header 的全部值,結果是一個枚舉的實例getHeaderNames():返回因此 request Header 的名字,結果是一個枚舉的實例getInputStream():返回請求的輸入流,用於得到請求中的數據getMethod():得到客戶端向服務器端傳送數據的方法
getParameter(String name):得到客戶端傳送給服務器端的有 name 指定的參數值getParameterNames():得到客戶端傳送給服務器端的全部參數的名字,結果是一個枚舉的實
例
getParametervalues(String name):得到有 name 指定的參數的全部值
getProtocol():獲取客戶端向服務器端傳送數據所依據的協議名稱
getQueryString():得到查詢字符串
getRequestURI():獲取發出請求字符串的客戶端地址
getRemoteAddr():獲取客戶端的 IP 地址
getRemoteHost():獲取客戶端的名字
getSession([Boolean create]):返回和請求相關 Session
getServerName():獲取服務器的名字
getServletPath():獲取客戶端所請求的腳本文件的路徑
getServerPort():獲取服務器的端口號
removeAttribute(String name):刪除請求中的一個屬性
HttpSession 中能夠跟蹤並儲存用戶信息,把值設置到屬性中,有 2 個方法:
setAttribute(),getAttrribute();
例如:在一個方法中用 session.setAttribute(「student」,student);在 session 中設置一個屬性名爲student,值爲一個名爲 student 的對象。然後可在同一 session 範圍內用 getAttribute(「student」) 取出該屬性,獲得 student 對象。
答:目錄結構以下圖所示:
webapps
|
Applocation
|
__________________
| |
JSP 頁面 WEB-INF
|
___________________
| | |
classes lib web.xml
139. cookie和session的區別?
注意 cookie 有時候禁不掉
Cookie session
存儲在客戶端 存儲在服務器端
兩種類型 兩種實現方式
有聲明週期 依賴於 cookie
無聲明週期 url 重寫
父路徑不能訪問子路徑的 cookie 同一個 session 的窗口共享一個 session
典型應用: 典型應用:
3 個月不用再登錄 用戶登錄
購物車(http://www.china-pub.com/) 購物車也能夠用 session 實現。·
不可靠 可靠
forward 是服務器請求資源,服務器直接訪問目標地址的 URL,把那個 URL 的響應內容讀取過來,而後把這些內容再發給瀏覽器,瀏覽器根本不知道服務器發送的內容是從哪兒來的,因此它的地址欄中仍是原來的地址。
redirect 就是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器從新去請求那個地址,通常來講瀏覽器會用剛纔請求的全部參數從新請求,因此 session,request 參數均可以獲取。
當你要傳遞普通的數據類型給下一個頁面時,你在下一個頁面中就能夠用 getParameter()方法來得到上一個頁面傳遞過來的數據了!(普通的數據類型是指 int,float,double,string 等在 Java 中經常使用的基本類型,可是在下一個頁面中你用 getParameter()方法得到的值永遠只能時 String 類型的,你能夠把 String 類型轉換爲你所須要的類型!)
當你要傳遞一個對象給下一個頁面時,你就要使用 getAttribut()方法了!如:你要把一個 List
或 Map 傳遞到下一個頁面,這時你就必需要用 setAttribut()和 getAttribut()方法傳遞數據了!從更深層次的考慮,getParameter()方法傳遞數據,只會從 WEB 客戶端傳遞到 WEB 服務器,表明 HTTP 請求數據,getParameter()方法返回 String 類型的數據!setAttribut()和 getAttribut()
方法傳遞的數據只會在 WEB 服務器內部,在具備轉發關係的 WEB 組件之間傳遞,這兩個方法能設置 Object 類型的共享數據!
答:JSP 共有如下 9 個內置的對象:
request 用戶端請求,此請求會包含來自 GET/POST 請求的參數 response 網頁傳回用戶端的迴應
pageContext 網頁的屬性是在這裏管理
session 與請求有關的會話期
application servlet 正在執行的內容
out 用來傳送回應的輸出
config servlet 的構架部件
page JSP 網頁自己
exception 針對錯誤網頁,未捕捉的例外
request:表示 HttpServletRequest 對象。它包含了有關瀏覽器請求的信息,而且提供了幾個用於獲取 cookie, header, 和 session 數據的有用的方法。
response:表示 HttpServletResponse 對象,並提供了幾個用於設置送回 瀏覽器的響應的方法(如 cookies,頭信息等)
out:對象是 javax.jsp.JspWriter 的一個實例,並提供了幾個方法使你能用於向瀏覽器回送輸出結果。
pageContext:表示一個 javax.servlet.jsp.PageContext 對象。它是用於方便存取各類範圍的名
字空間、servlet 相關的對象的 API,而且包裝了通用的 servlet 相關功能的方法。
session:表示一個請求的 javax.servlet.http.HttpSession 對象。Session 能夠存貯用戶的狀態信
息
applicaton :表示一個 javax.servle.ServletContext 對象。這有助於查找有關 servlet 引擎和 servlet 環境的信息
config:表示一個 javax.servlet.ServletConfig 對象。該對象用於存取 servlet 實例的初始化參
數。
page:表示從該頁面產生的一個 servlet 實例
答:在 JSP 中使用 JavaBean 經常使用的動做有:
1)<jsp:useBean />:用來建立和查找 bean 對象;
2)<jsp:setProperty />:用來設置 bean 的屬性,即調用其 setXxx()方法;
3)<jsp:getProperty />:用來得到 bean 的屬性,即調用其 getXxx()方法。
(這個問題彷佛不重要,不明白爲什麼有此題)
答:JSP 共有如下 6 種基本動做
jsp:include:在頁面被請求的時候引入一個文件。
jsp:useBean:尋找或者實例化一個 JavaBean。
jsp:setProperty:設置 JavaBean 的屬性。
jsp:getProperty:輸出某個 JavaBean 的屬性。
jsp:forward:把請求轉到一個新的頁面。
jsp:plugin:根據瀏覽器類型爲 Java 插件生成 OBJECT 或 EMBED 標記
isErrorPage(是否能使用 Exception 對象),isELIgnored(是否忽略表達式)
答:a.page 是表明與一個頁面相關的對象和屬性。一個頁面由一個編譯好的 Java servlet 類(能夠帶有任何的 include 指令,可是沒有 include 動做)表示。這既包括 servlet 又包括被編譯成 servlet 的 JSP 頁面
b.request 是表明與 Web 客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個 Web 組件(因爲 forward 指令和 include 動做的關係)
c.session 是表明與用於某個 Web 客戶機的一個用戶體驗相關對象和屬性。一個 Web 會話能夠也常常會跨越多個客戶機請求
d.application 是表明與整個 Web 應用程序相關的對象和屬性。這實質上是跨越整個 Web 應用程序,包括多個頁面、請求和會話的一個全局做用域。
答:動態 INCLUDE 用 jsp:include 動做實現
<jsp:include page=included.jsp flush=true />它老是會檢查所含文件中的變化,適合用於包含動態頁面,而且能夠帶參數 靜態 INCLUDE 用 include 僞碼實現,定不會檢查所含文件的變化,適用於包含靜態頁面 <%@ include file=included.htm %>
(下面的回答嚴重錯誤,應該是想問 forward 和 sendRedirect 的區別,畢竟出題的人不是專業搞文字藝術的人,可能表達能力並不見得很強,用詞不必定精準,加之其自身的技術面也可能存在一些問題,不必定真正將他的意思表達清楚了,嚴格意思上來說,一些題目可能根本就無人能答,因此,答題時要掌握主動,只要把本身知道的表達清楚就夠了,而不要去推敲原始題目的具體含義是什麼,不要一味想着是在答題)答:有兩種,分別爲:
<jsp:include page=included.jsp flush=true>
<jsp:forward page= nextpage.jsp/>
前者頁面不會轉向 include 所指的頁面,只是顯示該頁的結果,主頁面仍是原來的頁面。執行完後還會回來,至關於函數調用。而且能夠帶參數.後者徹底轉向新頁面,不會再回來。至關於 go to 語句.
request,session,application,cookie 等
答:能夠驗證客戶是否來自可信的網絡,能夠對客戶提交的數據進行從新編碼,能夠從系統裏得到配置的信息,能夠過濾掉客戶的某些不該該出現的詞彙,能夠驗證用戶是否登陸,能夠驗證客戶的瀏覽器是否支持當前的應用,能夠記錄系統的日誌等等。
答:首先要實現(implements)Filter 接口,同時覆蓋 Filter 接口的三個方法:
init(FilterConfig config) //用於得到 FilterConfig 對象;
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //進行過濾處
理一些業務;
destroy() //銷燬 Filter。
答:JSP 中的請求轉發可利用 forward 動做實現:<jsp:forward />;
Serlvet 中實現請求轉發的方式爲:
getServletContext().getRequestDispatcher(path).forward(req,res)。
JSP 是 Servlet 技術的擴展,本質上是 Servlet 的簡易方式,更強調應用的外表表達。JSP 編譯後是"類 servlet"。Servlet 和 JSP 最主要的不一樣點在於,Servlet 的應用邏輯是在 Java 文件中,而且徹底從表示層中的 HTML 裏分離開來。而 JSP 的狀況是 Java 和 HTML 能夠組合成一個擴展名爲.jsp 的文件。JSP 側重於視圖,Servlet 主要用於控制邏輯。
答:基於 Java 的 Web 應用系統採用 MVC 架構模式,即 model(模型)、view(視圖)、control(控制)分離設計;這是目前 WEB 應用服務系統的主流設計方向。
Model:即處理業務邏輯的模塊,每一種處理一個模塊;
View:負責頁面顯示,顯示 MODEL 處理結果給用戶,主要實現數據到頁面轉換過程;Control:負責每一個請求的分發,把 FORM 數據傳遞給 MODEL 處理,把處理結果的數
據傳遞給 VIEW 顯示。
答 :MVC 是 Model - View -Controller 的簡寫。Model 表明的是應用的業務邏輯(經過JavaBean,EJB 組件實現), View 是應用的表示面(由 JSP 頁面產生),Controller 是提供應用的處理過程控制(通常是一個 Servlet),經過這種設計模型把應用邏輯,處理過程和顯示邏輯分紅不一樣的組件實現。這些組件能夠進行交互和重用。
Public String translate (String str) {
String tempStr = "";
try {
tempStr = new String(str.getBytes("ISO-8859-1"), "GBK"); tempStr = tempStr.trim();
}
catch (Exception e) {
System.err.println(e.getMessage());
}
return tempStr;
}
答案(1) public static String[] splitStringByComma(String source){ if(source==null||source.trim().equals(「」))
return null;
StringTokenizer commaToker = new StringTokenizer(source,」,」); String[] result = new String[commaToker.countTokens()]; int i=0;
while(commaToker.hasMoreTokens()){
result[i] = commaToker.nextToken();
i++;
}
return result;
}
循環遍歷 String 數組
Integer.parseInt(String s)變成 int 類型
組成 int 數組
Arrays.sort(int[] a),
a 數組升序
降序能夠從尾部開始輸出
答:J2EE 服務器啓動時會創建必定數量的池鏈接,並一直維持很多於此數目的池鏈接。客戶端程序須要鏈接時,池驅動程序會返回一個未使用的池鏈接並將其表記爲忙。若是當前沒有空閒鏈接,池驅動程序就新建必定數量的鏈接,新建鏈接的數量有配置參數決定。當使用的池鏈接調用完成後,池驅動程序將此鏈接表記爲空閒,其餘調用就能夠使用這個鏈接。
答:1)優勢:簡單易用,與 Java 有相似的語法,能夠使用任何文本編輯工具編寫,只須要瀏覽器就可執行程序,而且事先不用編譯,逐行執行,無需進行嚴格的變量聲明,並且內置大量現成對象,編寫少許程序能夠完成目標;
2)缺點:不適合開發大型應用程序;
3)Javascript 有 11 種內置對象: Array、String、Date、Math、Boolean、Number、Function、Global、Error、RegExp、Object。
6、數據庫部分
存儲過程是用戶定義的一系列 sql 語句的集合,涉及特定表或其它對象的任務,用戶能夠調用存儲過程,而函數一般是數據庫已定義的方法,它接收參數並返回某種類型的值而且不涉及特定用戶表。
事務是做爲一個邏輯單元執行的一系列操做,一個邏輯工做單元必須有四個屬性,稱爲
ACID(原子性、一致性、隔離性和持久性)屬性,只有這樣才能成爲一個事務:
原子性:事務必須是原子工做單元;對於其數據修改,要麼全都執行,要麼全都不執行。一致性:事務在完成時,必須使全部的數據都保持一致狀態。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構(如
B 樹索引或雙向鏈表)都必須是正確的。
隔離性:由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。事務查看數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看中間狀態的數據。這稱爲可串行性,由於它可以從新裝載起始數據,而且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。
持久性:事務完成以後,它對於系統的影響是永久性的。該修改即便出現系統故障也將一直保持。
遊標用於定位結果集的行,經過判斷全局變量@@FETCH_STATUS 能夠判斷是否到了最後,一般此變量不等於 0 表示出錯或到了最後。
事前觸發器運行於觸發事件發生以前,而過後觸發器運行於觸發事件發生以後。一般事前觸發器能夠獲取事件以前和新的字段值。 語句級觸發器能夠在語句執行前或後執行,而行級觸發在觸發器所影響的每一行觸發一次。
存儲過程:是數據庫管理系統裏的一個很重要的對象。用它能夠封裝一些功能。把多個 SQL 語句封裝到存儲過程裏面。起到封裝功能的做用。相似面向對象裏,封裝對象的一個功能同樣。幾乎任何可寫成批處理的 Transact-SQL 代碼均可用於建立存儲過程。
觸發器:觸發器是在用戶進行某項操做的時候,會觸發觸發器的執行。它相似於 JAVA 中圖形截面編程裏的事件操做同樣,是觸發執行。和存儲過程的主要區別在於:存儲過程相似 JAVA 裏面的對象同樣,進行功能的封裝(方法)。在調用的時候纔會執行。而觸發器只能在別的操做執行的時候纔會觸發觸發器的執行。
事務:相似於 JAVA 裏面線程的同步同樣,做爲一個單元執行。它有四大特性:原子性,隔離性,一致性,持久性。在 SQL SERVER 2000 裏面還支持存儲點的用法。你們都知道,事務是作爲一個單元運行,要麼所有執行,要麼所有不執行。可是有時候咱們能夠保證事務的一部分可能正確執行,而且這些執行能夠直接刷新到數據庫裏面。那麼咱們就能夠在這個事務的中間部分設置一個或者多個存儲點。這樣在這個事務大單元裏就分紅了幾個小部分。若是上面的部分執行正確,下面的部分執行錯誤,那麼就不必回滾整個事務,只須要回滾到存儲點的地方就能夠了
範式:目地:規範化目的是使結構更合理,消除存儲異常,使數據冗餘儘可能小,便於插入、刪除和更新
原則:聽從概念單一化 "一事一地"原則,即一個關係模式描述一個實體或實體間的一種聯繫。規範的實質就是概念的單一化。
方法:將關係模式投影分解成兩個或兩個以上的關係模式。
要求:分解後的關係模式集合應當與原關係模式"等價",即通過天然聯接能夠恢復原關係而不丟失信息,並保持屬性間合理聯繫。
165. 寫一個SQL Server中的存儲過程:
如下爲一個帶有一個輸入參數 Vdeptno ,返回部門爲 Vdeptnor 的全部職員的信息.
create procedure Emp_dept
@Vdeptno number(2) AS
begin
select * from emp where deptno=@Vdeptno
end
帶 IN 參數的過程
create or replace procedure addnew(dno IN number,
name IN varchar2,
location IN varchar2)IS
begin
insert into dept values(dno,name,location);
dbms_output.put_line(‘1 record inserted’);
end;
帶 OUT 參數的過程
create or replace procedure getsal(name IN varchar2,
salary OUT number)AS
begin
select sal into salary from emp where ename=name;
end;
10六、有 3 個表(15 分鐘):【基礎】
Student 學生表 (學號,姓名,性別,年齡,組織部門)
Course 課程表 (編號,課程名稱)
Sc 選課表 (學號,課程編號,成績)
表結構以下:
1)寫一個 SQL 語句,查詢選修了’計算機原理’的學生學號和姓名(3 分鐘)
2)寫一個 SQL 語句,查詢’周星馳’同窗選修了的課程名字(3 分鐘)
3)寫一個 SQL 語句,查詢選修了 5 門課程的學生學號和姓名(9 分鐘)
答:1)SQL 語句以下:
select stu.sno, stu.sname from Student stu
where (select count(*) from sc where sno=stu.sno and cno =
(select cno from Course where cname='計算機原理')) != 0;
2)SQL 語句以下:
select cname from Course
where cno in ( select cno from sc where sno =
(select sno from Student where sname='周星馳'));
3)SQL 語句以下:
select stu.sno, stu.sname from student stu
where (select count(*) from sc where sno=stu.sno) = 5;
10七、有三張表,學生表 S,課程 C,學生課程表 SC,學生能夠選修多門課程,一門課程能夠被多個學生選修,經過 SC 表關聯。【基礎】
1)寫出建表語句;
2)寫出 SQL 語句,查詢選修了全部選修課程的學生;
3)寫出 SQL 語句,查詢選修了至少 5 門以上的課程的學生。
答:1)建表語句以下(mysql 數據庫):
create table s(id integer primary key, name varchar(20)); create table c(id integer primary key, name varchar(20)); create table sc(
sid integer references s(id),
cid integer references c(id),
primary key(sid,cid)
);
2)SQL 語句以下:
select stu.id, stu.name from s stu
where (select count(*) from sc where sid=stu.id)
= (select count(*) from c);
3)SQL 語句以下:
select stu.id, stu.name from s stu
where (select count(*) from sc where sid=stu.id)>=5;
10八、數據庫表(Test)結構以下:【基礎】
ID |
NAME |
AGE |
MANAGER(所屬主管人 ID) |
106 |
A |
30 |
104 |
109 |
B |
19 |
104 |
104 |
C |
20 |
111 |
107 |
D |
35 |
109 |
112 |
E |
25 |
120 |
119 |
F |
45 |
NULL |
要求:列出全部年齡比所屬主管年齡大的人的 ID 和名字?
答:SQL 語句以下:
select employee.name from test employee
where employee.age > (select manager.age from test manager where manager.id=employee.manager);
10九、有以下兩張表:【中等難度】
表 city: 表 state:
CityNo |
CityName |
StateNo |
BJ |
北京 |
(Null) |
SH |
上海 |
(Null) |
GZ |
廣州 |
GD |
DL |
大連 |
LN |
欲獲得以下結果: |
|
|
|
City No |
City Name |
State No State Name |
|
BJ |
北京 |
(Null) |
(Null) |
DL |
大連 |
LN |
遼寧 |
GZ |
廣州 |
GD |
廣東 |
SH |
上海 |
(Null) |
(Null) |
寫相應的 SQL 語句。
答:SQL 語句爲:
State No |
State Name |
GD |
廣東 |
LN |
遼寧 |
SD |
山東 |
NMG |
內蒙古 |
SELECT C.CITYNO, C.CITYNAME, C.STATENO, S.STATENAME
FROM CITY C, STATE S
WHERE C.STATENO=S.STATENO(+)
ORDER BY(C.CITYNO);
employee:
eid,ename,salary,deptid;
select * from employee order by deptid desc,salary
建立表:
mysql> create table employee921(id int primary key auto_increment,name varchar(5 0),salary bigint,deptid int);
插入實驗數據:
mysql> insert into employee921 values(null,'zs',1000,1),(null,'ls',1100,1),(null ,'ww',1100,1),(null,'zl',900,1) ,(null,'zl',1000,2), (null,'zl',900,2) ,(null,'z l',1000,2) , (null,'zl',1100,2);
編寫 sql 語句:
()select avg(salary) from employee921 group by deptid;
()mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep
tid tid from employee921 where salary > (select avg(salary) from employee921 where deptid = tid);
效率低的一個語句,僅供學習參考使用(在 group by 以後不能使用 where,只能使用having,在 group by 以前能夠使用 where,即表示對過濾後的結果分組):
mysql> select employee921.id,employee921.name,employee921.salary,employee921.dep
tid tid from employee921 where salary > (select avg(salary) from employee921 group by deptid
having deptid = tid);
()select count(*) ,tid
from (
select employee921.id,employee921.name,employee921.salary,employee921.deptid tid
from employee921
where salary >
(select avg(salary) from employee921 where deptid = tid)
) as t
group by tid ;
另一種方式:關聯查詢
select a.ename,a.salary,a.deptid
from emp a,
(select deptd,avg(salary) avgsal from emp group by deptid ) b where a.deptid=b.deptid and a.salary>b.avgsal;
create procedure insert_Student (_name varchar(50),_age int ,out _id int)
begin
insert into student value(null,_name,_age);
select max(stuId) into _id from student;
end;
call insert_Student('wfz',23,@id);
select @id;
mysql> create trigger update_Student BEFORE update on student FOR EACH ROW -> select * from student;
觸發器不容許返回結果
create trigger update_Student BEFORE update on student FOR EACH ROW insert into student value(null,'zxx',28); mysql 的觸發器目前不能對當前表進行操做
create trigger update_Student BEFORE update on student FOR EACH ROW delete from articles where id=8;
這個例子不是很好,最好是用刪除一個用戶時,順帶刪除該用戶的全部帖子這裏要注意使用 OLD.id
觸發器用處仍是不少的,好比校內網、開心網、Facebook,你發一個日誌,自動通知好友,其實就是在增長日誌時作一個後觸發,再向通知表中寫入條目。由於觸發器效率高。而 UCH 沒有用觸發器,效率和數據處理能力都很低。存儲過程的實驗步驟:
mysql> delimiter |
mysql> create procedure insertArticle_Procedure (pTitle varchar(50),pBid int,out pId int)
-> begin
-> insert into article1 value(null,pTitle,pBid);
-> select max(id) into pId from article1;
-> end;
-> |
Query OK, 0 rows affected (0.05 sec)
mysql> call insertArticle_Procedure('中國北京',1,@pid); -> |
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> select @pid;
+------ |
+ |
| @pid | |
|
+------ |
+ |
| 3 |
| |
+------ |
+ |
1 row in set (0.00 sec)
mysql> select * from article1;
+---- |
+-------------- |
+------ |
+ |
| id | title |
| bid |
| |
|
+---- |
+-------------- |
+------ |
+ |
| 1 |
| test |
|
| 1 |
| |
| 2 |
| chuanzhiboke | 1 |
| |
||
| 3 |
| 中國北京 |
|
| 1 |
| |
+---- |
+-------------- |
+ |
------+ |
|
3 rows in set (0.00 sec)
觸發器的實驗步驟:
create table board1(id int primary key auto_increment,name varchar(50),ar ticleCount int);
create table article1(id int primary key auto_increment,title varchar(50)
,bid int references board1(id));
delimiter |
create trigger insertArticle_Trigger after insert on article1 for each ro w begin
-> update board1 set articleCount=articleCount+1 where id= NEW.bid;
-> end;
-> |
delimiter ;
insert into board1 value (null,'test',0);
insert into article1 value(null,'test',1);
還有,每插入一個帖子,都但願將版面表中的最後發帖時間,帖子總數字段進行同步更新,用觸發器作效率就很高。下次課設計這樣一個案例,寫觸發器時,對於最後發帖時間可能須要用 declare 方式聲明一個變量,或者是用 NEW.posttime 來生成。
第一範式(1NF):字段具備原子性,不可再分。全部關係型數據庫系統都知足第一範式)數據庫表中的字段都是單一屬性的,不可再分。例如,姓名字段,其中的姓和名必須做爲一個總體,沒法區分哪部分是姓,哪部分是名,若是要區分出姓和名,必須設計成兩個獨立的字段。
第二範式(2NF):第二範式(2NF)是在第一範式(1NF)的基礎上創建起來的,即知足第二範式(2NF)必須先知足第一範式(1NF)。要求數據庫表中的每一個實例或行必須能夠被唯一地區分。一般須要爲表加上一個列,以存儲各個實例的唯一標識。這個唯一屬性列被稱爲主關鍵字或主鍵。第二範式(2NF)要求實體的屬性徹底依賴於主關鍵字。所謂徹底依賴是指不能存在僅依賴主關鍵字一部分的屬性,若是存在,那麼這個屬性和主關鍵字的這一部分應該分離出來造成一個新的實體,新實體與原實體之間是一對多的關係。爲實現區分一般須要爲表加上一個列,以存儲各個實例的唯一標識。簡而言之,第二範式就是非主屬性非部分依賴於主關鍵字。
第三範式的要求以下: 知足第三範式(3NF)必須先知足第二範式(2NF)。簡而言之,
第三範式(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。因此第三範式具備以下特徵:
1,每一列只有一個值 。2,每一行都能區分。3,每個表都不包含其餘表已經包含的非主關鍵字信息。
例如,帖子表中只能出現發帖人的 id,而不能出現發帖人的 id,還同時出現發帖人姓名,不然,只要出現同一發帖人 id 的全部記錄,它們中的姓名部分都必須嚴格保持一致,這就是數據冗餘。
用 PreparedStatement 通常來講比 Statement 性能高:一個 sql 發給服務器執行,步驟:語法
檢查、語義分析, 編譯,緩存
「inert into user values(1,1,1)」-à二進制
「inert into user values(2,2,2)」-à二進制
「inert into user values(?,?,?)」-à二進制
有外鍵約束會影響插入和刪除性能,若是程序可以保證數據的完整性,那在設計數據庫時就去掉外鍵。(比喻:就比如免檢產品,就是爲了提升效率,充分相信產品的製造商)
(對於 hibernate 來講,就應該有一個變化:empleyee->Deptment 對象,如今設計時就成了employeeàdeptid)
看 mysql 幫助文檔子查詢章節的最後部分,例如,根據掃描的原理,下面的子查詢語句要比第二條關聯查詢的效率高:
表中容許適當冗餘,譬如,主題帖的回覆數量和最後回覆時間等
將姓名和密碼單獨從用戶表中獨立出來。這能夠是很是好的一對一的案例喲!
sql 語句所有大寫,特別是列名和表名都大寫。特別是 sql 命令的緩存功能,更加須要統一大小寫,sql 語句à發給 oracle 服務器à語法檢查和編譯成爲內部指令à緩存和執行指令。根據緩存的特色,不要拼湊條件,而是用?和 PreparedStatment
還有索引對查詢性能的改進也是值得關注的。
備註:下面是關於性能的討論舉例
4 航班 3 個城市
m*n
select * from flight,city where flight.startcityid=city.cityid and city.name='beijing'; m + n
select * from flight where startcityid = (select cityid from city where cityname='beijing');
select flight.id,'beijing',flight.flightTime from flight where startcityid = (select cityid from city where cityname='beijing')
假設咱們有一個表 Student,包括如下字段與數據:
drop table student;
create table student
(
id int primary key,
name nvarchar2(50) not null,
score number not null
);
insert into student values(1,'Aaron',78);
insert into student values(2,'Bill',76);
insert into student values(3,'Cindy',89);
insert into student values(4,'Damon',90);
insert into student values(5,'Ella',73);
insert into student values(6,'Frado',61);
insert into student values(7,'Gill',99);
insert into student values(8,'Hellen',56);
insert into student values(9,'Ivan',93);
insert into student values(10,'Jay',90);
commit;
Union 和 Union All 的區別。
select *from studentwhere id < 4union
select *from studentwhere id > 2 and id < 6
結果將是 |
|
|
1 |
Aaron |
78 |
2 |
Bill |
76 |
3 |
Cindy |
89 |
4 |
Damon 90 |
|
5 |
Ella |
73 |
若是換成 Union All 鏈接兩個結果集,則返回結果是:
1 |
Aaron |
78 |
2 |
Bill |
76 |
3 |
Cindy |
89 |
3 |
Cindy |
89 |
4 |
Damon 90 |
|
5 |
Ella |
73 |
能夠看到,Union 和 Union All 的區別之一在於對重複結果的處理。
UNION 在進行表連接後會篩選掉重複的記錄,因此在表連接後會對所產生的結果集進行排序運算,刪除重複的記錄再返回結果。實際大部分應用中是不會產生重複的記錄,最常
見的是過程表與歷史表 UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
這個 SQL 在運行時先取出兩個表的結果,再用排序空間進行排序刪除重複的記錄,最後返回結果集,若是表數據量大的話可能會致使用磁盤進行排序。
而 UNION ALL 只是簡單的將兩個結果合併後就返回。這樣,若是返回的兩個結果集中有重複的數據,那麼返回的結果集就會包含重複的數據了。
從效率上說,UNION ALL 要比 UNION 快不少,因此,若是能夠確認合併的兩個結果集中不包含重複的數據的話,那麼就使用 UNION ALL,
取出 sql 表中第 31 到 40 的記錄(以自動增加 ID 爲主鍵)
sql server 方案 1:
select top 10 * from t where id not in (select top 30 id from t order by id ) orde by id sql server 方案 2:
select top 10 * from t where id in (select top 40 id from t order by id) order by id desc
mysql 方案:select * from t order by id limit 30,10
oracle 方案:select * from (select rownum r,* from t where r<=40) where r>30
--------------------待整理進去的內容-------------------------------------
pageSize=20;
pageNo = 5;
1.分頁技術 1(直接利用 sql 語句進行分頁,效率最高和最推薦的) mysql:sql = "select * from articles limit " + (pageNo-1)*pageSize + "," + pageSize;
oracle: sql = "select * from " +"(select rownum r,* from " +"(select * from articles order by postime desc)" +"where rownum<= " + pageNo*pageSize +") tmp " +"where r>" + (pageNo-1)*pageSize;
註釋:第 7 行保證 rownum 的順序是肯定的,由於 oracle 的索引會形成 rownum 返回不一樣的值
簡洋提示:沒有 order by 時,rownum 按順序輸出,一旦有了 order by,rownum 不按順序輸出了,這說明 rownum 是排序前的編號。若是對 order by 從句中的字段創建了索引,那麼, rownum 也是按順序輸出的,由於這時候生成原始的查詢結果集時會參照索引表的順序來構建。
sqlserver:sql = "select top 10 * from id not id(select top " + (pageNo-1)*pageSize + "id from articles)"
DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection();
//"select * from user where id=?" --->binary directive PreparedStatement pstmt = cn.prepareSatement(sql); ResultSet rs = pstmt.executeQuery() while(rs.next()){
out.println(rs.getString(1));
}
2.不可滾動的遊標
pageSize=20;
pageNo = 5;
cn = null
stmt = null;
rs = null;
try{
sqlserver:sql = "select * from articles";
DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection();
//"select * from user where id=?" --->binary directive PreparedStatement pstmt = cn.prepareSatement(sql); ResultSet rs = pstmt.executeQuery() for(int j=0;j<(pageNo-1)*pageSize;j++){
rs.next();
}
int i=0;
while(rs.next() && i<10){
i++;
out.println(rs.getString(1));
}
}
cacth(){}
finnaly{
if(rs!=null)try{rs.close();}catch(Exception e){}
if(stm.........
if(cn............
}
3.可滾動的遊標
pageSize=20;
pageNo = 5;
cn = null
stmt = null;
rs = null;
try{
sqlserver:sql = "select * from articles";
DataSource ds = new InitialContext().lookup(jndiurl); Connection cn = ds.getConnection();
//"select * from user where id=?" --->binary directive
PreparedStatement pstmt =
cn.prepareSatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,...);
//根據上面這行代碼的異常 SQLFeatureNotSupportedException,就可判斷驅動是否支持可滾
動遊標
ResultSet rs = pstmt.executeQuery()
rs.absolute((pageNo-1)*pageSize)
int i=0;
while(rs.next() && i<10){
i++;
out.println(rs.getString(1));
}
}
cacth(){}
finnaly{
if(rs!=null)try{rs.close();}catch(Exception e){}
if(stm.........
if(cn............
}
name kecheng fenshu
張三 語文 81
張三 數學 75
李四 語文 76
李四 數學 90
王五 語文 81
王五 數學 100
王五 英語 90
準備數據的 sql 代碼:
create table score(id int primary key auto_increment,name varchar(20),subject varchar(20),score int);
insert into score values
(null,'張三','語文',81),
(null,'張三','數學',75),
(null,'李四','語文',76),
(null,'李四','數學',90),
(null,'王五','語文',81),
(null,'王五','數學',100),
(null,'王五 ','英語',90);
提示:當百思不得其解時,請理想思惟,把小變成大作,把大變成小作,答案:
A: select distinct name from score where name not in (select distinct name from score where score<=80)
B:select distince name t1 from score where 80< all (select score from score where name=t1);
一個叫 department 的表,裏面只有一個字段 name,一共有 4 條紀錄,分別是 a,b,c,d,對應四個球對,如今四個球對進行比賽,用一條 sql 語句顯示全部可能的比賽組合.
答:select a.name, b.name from team a, team b where a.name < b.name
請用 SQL 語句實現:從 TestDB 數據表中查詢出全部月份的發生額都比 101 科目相應月份的發生額高的科目。請注意:TestDB 中有不少科目,都有 1-12 月份的發生額。AccID:科目代碼,Occmonth:發生額月份,DebitOccur:發生額。
數據庫名:JcyAudit,數據集:Select * from TestDB 準備數據的 sql 代碼:
drop table if exists TestDB;
create table TestDB(id int primary key auto_increment,AccID varchar(20), Occmonth date,
DebitOccur bigint);
insert into TestDB values
(null,'101','1988-1-1',100),
(null,'101','1988-2-1',110),
(null,'101','1988-3-1',120),
(null,'101','1988-4-1',100),
(null,'101','1988-5-1',100),
(null,'101','1988-6-1',100),
(null,'101','1988-7-1',100),
(null,'101','1988-8-1',100);
--複製上面的數據,故意把第一個月份的發生額數字改小一點
insert into TestDB values
(null,'102','1988-1-1',90),
(null,'102','1988-2-1',110),
(null,'102','1988-3-1',120),
(null,'102','1988-4-1',100),
(null,'102','1988-5-1',100),
(null,'102','1988-6-1',100),
(null,'102','1988-7-1',100),
(null,'102','1988-8-1',100);
--複製最上面的數據,故意把全部發生額數字改大一點
insert into TestDB values
(null,'103','1988-1-1',150),
(null,'103','1988-2-1',160),
(null,'103','1988-3-1',180),
(null,'103','1988-4-1',120),
(null,'103','1988-5-1',120),
(null,'103','1988-6-1',120),
(null,'103','1988-7-1',120),
(null,'103','1988-8-1',120);
--複製最上面的數據,故意把全部發生額數字改大一點
insert into TestDB values
(null,'104','1988-1-1',130),
(null,'104','1988-2-1',130),
(null,'104','1988-3-1',140),
(null,'104','1988-4-1',150),
(null,'104','1988-5-1',160),
(null,'104','1988-6-1',170),
(null,'104','1988-7-1',180),
(null,'104','1988-8-1',140);
--複製最上面的數據,故意把第二個月份的發生額數字改小一點
insert into TestDB values
(null,'105','1988-1-1',100),
(null,'105','1988-2-1',80),
(null,'105','1988-3-1',120),
(null,'105','1988-4-1',100),
(null,'105','1988-5-1',100),
(null,'105','1988-6-1',100),
(null,'105','1988-7-1',100),
(null,'105','1988-8-1',100);
答案:
select distinct AccID from TestDB
where AccID not in
(select TestDB.AccIDfrom TestDB,
(select * from TestDB where AccID='101') as db101
where TestDB.Occmonth=db101.Occmonth and TestDB.DebitOccur<=db101.DebitOccur );
year month amount
1991 1 1.1
1991 2 1.2
1991 3 1.3
1991 4 1.4
1992 1 2.1
1992 2 2.2
1992 3 2.3
1992 4 2.4
查成這樣一個結果
year m1 m2 m3 m4
1991 1.1 1.2 1.3 1.4
1992 2.1 2.2 2.3 2.4
提示:這個與工資條很是相似,與學生的科目成績也很類似。
準備 sql 語句:
drop table if exists sales;
create table sales(id int auto_increment primary key,year varchar(10), month varchar(10), amount float(2,1));
insert into sales values
(null,'1991','1',1.1),
(null,'1991','2',1.2),
(null,'1991','3',1.3),
(null,'1991','4',1.4),
(null,'1992','1',2.1),
(null,'1992','2',2.2),
(null,'1992','3',2.3),
(null,'1992','4',2.4);
答案1、select sales.year ,
(select t.amount from sales t where t.month='1' and t.year= sales.year) '1',
(select t.amount from sales t where t.month='1' and t.year= sales.year) '2',
(select t.amount from sales t where t.month='1' and t.year= sales.year) '3',
(select t.amount from sales t where t.month='1' and t.year= sales.year) as '4'
from sales group by year;
表:id,title,postuser,postdate,parentid
準備 sql 語句:
drop table if exists articles;
create table articles(id int auto_increment primary key,title varchar(50), postuser varchar(10), postdate datetime,parentid int references articles(id)); insert into articles values
(null,'第一條','張三','1998-10-10 12:32:32',null),
(null,'第二條','張三','1998-10-10 12:34:32',null),
(null,'第一條回覆 1','李四','1998-10-10 12:35:32',1),
(null,'第二條回覆 1','李四','1998-10-10 12:36:32',2),
(null,'第一條回覆 2','王五','1998-10-10 12:37:32',1),
(null,'第一條回覆 3','李四','1998-10-10 12:38:32',1),
(null,'第二條回覆 2','李四','1998-10-10 12:39:32',2),
(null,'第一條回覆 4','王五','1998-10-10 12:39:40',1);
答案:
select a.title,a.postuser,
(select max(postdate) from articles where parentid=a.id) reply from articles a where a.parentid is null;
註釋:子查詢能夠用在選擇列中,也可用於 where 的比較條件中,還能夠用於 from 從句中。
2.學生表 以下: |
|
|
|
|
|
id 號 |
學號 姓名 課程編號 課程名稱 分數 |
||||
1 |
2005001 |
張三 0001 |
數學 |
69 |
|
2 |
2005002 |
李四 |
0001 |
數學 |
89 |
3 |
2005001 |
張三 |
0001 |
數學 |
69 |
A: delete from tablename where id 號 not in(select min(id 號) from tablename group by 學號,姓
名,課程編號,課程名稱,分數)
實驗:
create table student2(id int auto_increment primary key,code varchar(20),name varchar(20)); insert into student2 values(null,'2005001','張三'),(null,'2005002','李四'),(null,'2005001','張三');
//以下語句,mysql 報告錯誤,可能刪除依賴後面統計語句,而刪除又致使統計語句結果不一致。
delete from student2 where id not in(select min(id) from student2 group by name); //可是,以下語句沒有問題:
select * from student2 where id not in(select min(id) from student2 group by name);
//因而,我想先把分組的結果作成虛表,而後從虛表中選出結果,最後再將結果做爲刪除的條件數據。
delete from student2 where id not in(select mid from (select min(id) mid from student2 group by name) as t);
或者:
delete from student2 where id not in(select min(id) from (select * from s tudent2) as t group by t.name);
表結構以下:
flight{flightID,StartCityID ,endCityID,StartTime}
city{cityID, CityName)
實驗環境:
create table city(cityID int auto_increment primary key,cityName varchar(20)); create table flight (flightID int auto_increment primary key,
StartCityID int references city(cityID),
endCityID int references city(cityID),
StartTime timestamp);
//航班原本應該沒有日期部分纔好,可是下面的題目當中涉及到了日期 insert into city values(null,'北京'),(null,'上海'),(null,'廣州'); insert into flight values
(null,1,2,'9:37:23'),(null,1,3,'9:37:23'),(null,1,2,'10:37:23'),(null,2,3,'10:37:23');
一、查詢起飛城市是北京的全部航班,按到達城市的名字排序
參與運算的列是我起碼可以顯示出來的那些列,但最終我不必定把它們顯示出來。各個表組合出來的中間結果字段中必須包含全部運算的字段。
select * from flight f,city c
where f.endcityid = c.cityid and startcityid =
(select c1.cityid from city c1 where c1.cityname = "北京")
order by c.cityname asc;
mysql> select flight.flightid,'北京' startcity, e.cityname from flight,city e wh ere flight.endcityid=e.cityid and flight.startcityid=(select cityid from city wh ere cityname='北京');
mysql> select flight.flightid,s.cityname,e.cityname from flight,city s,city e wh ere flight.startcityid=s.cityid and s.cityname='北京' and flight.endCityId=e.cit yID order by e.cityName desc;
二、查詢北京到上海的全部航班紀錄(起飛城市,到達城市,起飛時間,航班號) select c1.CityName,c2.CityName,f.StartTime,f.flightID from city c1,city c2,flight f
where f.StartCityID=c1.cityID
and f.endCityID=c2.cityID
and c1.cityName='北京'
and c2.cityName='上海'
三、查詢具體某一天(2005-5-8)的北京到上海的的航班次數 select count(*) from
(select c1.CityName,c2.CityName,f.StartTime,f.flightID
from city c1,city c2,flight f
where f.StartCityID=c1.cityID
and f.endCityID=c2.cityID
and c1.cityName='北京'
and c2.cityName='上海'
and 查幫助得到的某個日期處理函數(startTime) like '2005-5-8%'
mysql 中提取日期部分進行比較的示例代碼以下:
select * from flight where date_format(starttime,'%Y-%m-%d')='1998-01-02'
Drop table if not exists employees;
create table employees(id int primary key auto_increment,name varchar(50)
,salary int,managerid int references employees(id));
insert into employees values (null,' lhm',10000,null), (null,' zxx',15000,1 ),(null,'flx',9000,1),(null,'tg',10000,2),(null,'wzg',10000,3);
Wzg 大於 flx,lhm 大於 zxx
解題思路:
根據 sql 語句的查詢特色,是逐行進行運算,不可能兩行同時參與運算。
涉及了員工薪水和經理薪水,全部,一行記錄要同時包含兩個薪水,全部想到要把這個表自關聯組合一下。
首先要組合出一個包含有各個員工及該員工的經理信息的長記錄,譬如,左半部分是員工,右半部分是經理。而迪卡爾積會組合出不少垃圾信息,先去除這些垃圾信息。
select e.* from employees e,employees m where e.managerid=m.id and e.sala ry>m.salary;
數據庫中有 3 個表 teacher 表,student 表,tea_stu 關係表。
teacher 表 teaID name age
student 表 stuID name age
teacher_student 表 teaID stuID
要求用一條 sql 查詢出這樣的結果
1.顯示的字段要有老師 name, age 每一個老師所帶的學生人數
2.只列出老師 age 爲 40 如下,學生 age 爲 12 以上的記錄
預 備 知 識 : 1.sql 語 句 是 對 每 一 條 記 錄 依 次 處 理 , 條 件 爲 真 則 執 行 動 做(select,insert,delete,update)
2.只要是迪卡爾積,就會產生「垃圾」信息,因此,只要迪卡爾積了,咱們首先就要想到清除「垃圾」信息
實驗準備: drop table if exists tea_stu;
drop table if exists teacher;
drop table if exists student;
create table teacher(teaID int primary key,name varchar(50),age int); create table student(stuID int primary key,name varchar(50),age int);
create table tea_stu(teaID int references teacher(teaID),stuID int references student(stuID));
insert into teacher values(1,'zxx',45), (2,'lhm',25) , (3,'wzg',26) , (4,'tg',27); insert into student values(1,'wy',11), (2,'dh',25) , (3,'ysq',26) , (4,'mxc',27); insert into tea_stu values(1,1), (1,2), (1,3); insert into tea_stu values(2,2), (2,3), (2,4);
insert into tea_stu values(3,3), (3,4), (3,1);
insert into tea_stu values(4,4), (4,1), (4,2) , (4,3);
結果:2à3,3à2,4à3
解題思路:(真實面試答題時,也要寫出每一個分析步驟,若是紙張不夠,就找別人要)
1.要會統計分組信息,統計信息放在中間表中:
select teaid,count(*) from tea_stu group by teaid;
2.接着其實應該是篩除掉小於 12 歲的學生,而後再進行統計,中間表必須與 student 關聯才能獲得 12 歲如下學生和把該學生記錄從中間表中剔除,代碼是:
select tea_stu.teaid,count(*) total from student,tea_stu
where student.stuid=tea_stu.stuid and student.age>12 group by tea_stu.teaid
3.接着把上面的結果作成虛表與 teacher 進行關聯,並篩除大於 45 的老師 select teacher.teaid,teacher.name,total from teacher ,(select tea_stu.tea id,count(*) total from student,tea_stu where student.stuid=tea_stu.stuid and stu dent.age>12 group by tea_stu.teaid) as tea_stu2 where teacher.teaid=tea_stu2.tea id and teacher.age<45;
select authorid,count(*) total from articles
group by authorid
having total=(select max(total2) from (select count(*) total2 from articles group by authorid) as t); select t.authorid,max(t.total) from(select authorid,count(*) total from articles )as t
這條語句不行,由於 max 只有一列,不能與其餘列混淆。
select authorid,count(*) total from articles
group by authorid having total=max(total)也不行。
alter table drop column score;
alter table add colunm score int;
可能會很快,可是須要試驗,試驗不能拿真實的環境來操刀,而且要注意,
這樣的操做時沒法回滾的,在個人印象中,只有 inert update delete 等 DML 語句才能回滾,對於 create table,drop table ,alter table 等 DDL 語句是不能回滾。
解決方案一,update user set score=0;
解決方案二,假設上面的代碼要執行好長時間,超出咱們的容忍範圍,那我就 alter table user drop column score;alter table user add column score int。
下面代碼實現每一年的那個凌晨時刻進行清零。
Runnable runnable =
new Runnable(){
public void run(){
clearDb();
schedule(this,new Date(new Date().getYear()+1,0,0));
}
};
schedule(runnable,
new Date(new Date().getYear()+1,0,1));
select count(*) as num,tb.id from tb,
(select role from tb where id=xxx) as t1
where tb.role = t1.role and tb.id != t1.id
group by tb.id
having num = select count(role) from tb where id=xxx;
Table EMPLOYEES Structure:
EMPLOYEE_ID NUMBER Primary Key,
FIRST_NAME VARCHAR2(25),
LAST_NAME VARCHAR2(25),
Salary number(8,2),
HiredDate DATE,
Departmentid number(2)
Table Departments Structure:
Departmentid number(2) Primary Key,
DepartmentName VARCHAR2(25).
(2)基於上述 EMPLOYEES 表寫出查詢:寫出僱用日期在今年的,或者工資在[1000,2000]之間的,或者員工姓名(last_name)以’Obama’打頭的全部員工,列出這些員工的所有我的信息。(4 分)
select * from employees
where Year(hiredDate) = Year(date())
or (salary between 1000 and 200)
or left(last_name,3)='abc';
(3) 基於上述 EMPLOYEES 表寫出查詢:查出部門平均工資大於 1800 元的部門的全部員工,
列出這些員工的所有我的信息。
mysql> select id,name,salary,deptid did from employee1 where (select avg(salary)
from employee1 where deptid = did) > 1800;
(4) 基於上述 EMPLOYEES 表寫出查詢:查出我的工資高於其所在部門平均工資的員工,
列出這些員工的所有我的信息及該員工工資高出部門平均工資百分比。(5 分) select employee1.*,(employee1.salary-t.avgSalary)*100/employee1.salary from employee1,
(select deptid,avg(salary) avgSalary from employee1 group by deptid) as t where employee1.deptid = t.deptid and employee1.salary>t.avgSalary;
以下:
1 中,不須要.newInstance()
2 中,經過系統的屬性設置便可
3 中,是看起來比較直觀的一種方式,註冊相應的 db 的 jdbc 驅動,
總結:推薦 1,和 2 兩種方式。
緣由:3 在編譯時須要導入對應的 lib。1,2 不須要。
補充:2 的方式的話,能夠同時導入多個 jdbc 驅動,中間用冒號「:」分開
代碼以下:
package com.huawei.interview.lym;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;
public class JdbcTest {
public static void main(String[] args) {
Connection cn = null;
CallableStatement cstmt = null;
try {
//這裏最好不要這麼幹,由於驅動名寫死在程序中了
Class.forName("com.mysql.jdbc.Driver");
//實際項目中,這裏應用 DataSource 數據,若是用框架,
//這個數據源不須要咱們編碼建立,咱們只需 Datasource ds = context.lookup()
//cn = ds.getConnection();
cn = DriverManager.getConnection("jdbc:mysql:///test","root","root");
cstmt = cn.prepareCall("{call insert_Student(?,?,?)}");
cstmt.registerOutParameter(3,Types.INTEGER);
cstmt.setString(1, "wangwu");
cstmt.setInt(2, 25);
cstmt.execute();
//get 第幾個,不一樣的數據庫不同,建議不寫
System.out.println(cstmt.getString(3));
} catch (Exception e) { e.printStackTrace();
}
finally{
/*try{cstmt.close();}catch(Exception e){}
try{cn.close();}catch(Exception e){}*/
try {
if(cstmt != null)
cstmt.close();
if(cn != null)
cn.close();
} catch (SQLException e) { e.printStackTrace();
}
}
}
答:一個 sql 命令發給服務器去執行的步驟爲:語法檢查,語義分析,編譯成內部指令,緩存指令,執行指令等過程。
select * from student where id =3----緩存--àxxxxx 二進制命令 select * from student where id =3----直接取-àxxxxx 二進制命令 select * from student where id =4--- -à會怎麼幹?
若是當初是 select * from student where id =?--- -à又會怎麼幹?
上面說的是性能提升
能夠防止 sql 注入。
public void testJdbc(){
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//step1:註冊驅動;
Class.forName("oracle.jdbc.driver.OracleDriver");
//step 2:獲取數據庫鏈接;
con=DriverManager.getConnection(
"jdbc:oracle:thin:@192.168.0.39:1521:TARENADB",
"sd0605","sd0605");
/************************查 詢************************/
//step 3:建立 Statement;
String sql = "SELECT id, fname, lname, age, FROM Person_Tbl"; ps = con.prepareStatement(sql);
//step 4 :執行查詢語句,獲取結果集;
rs = ps.executeQuery();
//step 5:處理結果集—輸出結果集中保存的查詢結果; while (rs.next()){
System.out.print("id = " + rs.getLong("id"));
System.out.print(" , fname = " + rs.getString("fname"));
System.out.print(" , lname = " + rs.getString("lname"));
System.out.print(" , age = " + rs.getInt("age"));
}
/************************JDBC 修 改*********************/
sql = "UPDATE Person_Tbl SET age=23 WHERE id = ?"; ps = con.prepareStatement(sql); ps.setLong(1, 88);
int rows = ps.executeUpdate();
System.out.println(rows + " rows affected.");
} catch (Exception e){ e.printStackTrace();
} finally{
try{
con.close(); //關閉數據庫鏈接,以釋放資源。
} catch (Exception e1) {
}
}
}
答:按參數中指定的字符串形式的類名去搜索並加載相應的類,若是該類字節碼已經被加載過,則返回表明該字節碼的 Class 實例對象,不然,按類加載器的委託機制去搜索和加載該
類,若是全部的類加載器都沒法加載到該類,則拋出 ClassNotFoundException。加載完這個 Class 字節碼後,接着就能夠使用 Class 字節碼的 newInstance 方法去建立該類的實例對象了。有時候,咱們程序中全部使用的具體類名在設計時(即開發時)沒法肯定,只有程序運行時才能肯定,這時候就須要使用 Class.forName 去動態加載該類,這個類名一般是在配置文件中配置的,例如,spring 的 ioc 中每次依賴注入的具體類就是這樣配置的,jdbc 的驅動類名一般也是經過配置文件來配置的,以便在產品交付使用後不用修改源程序就能夠更換驅動類名。
答:最好的辦法是利用 sql 語句進行分頁,這樣每次查詢出的結果集中就只包含某頁的數據內容。再 sql 語句沒法實現分頁的狀況下,能夠考慮對大的結果集經過遊標定位方式來獲取某頁的數據。
sql 語句分頁,不一樣的數據庫下的分頁方案各不同,下面是主流的三種數據庫的分頁 sql:
sql server:
String sql = "select top " + pageSize + " * from students where id not in" + "(select top " + pageSize * (pageNumber-1) + " id from students order by id)" + "order by id";
mysql:String sql = "select * from students order by id limit " + pageSize*(pageNumber-1) + "," + pageSize;
oracle:String sql = "select * from " + (select *,rownum rid from (select * from students order
by postime desc) where rid<=" + pagesize*pagenumber + ") as t" + "where t>" +
pageSize*(pageNumber-1);
Connection cn = null;
PreparedStatement pstmt =null;
Resultset rs = null;
try{
Class.forname(driveClassName);
cn = DriverManager.getConnection(url,username,password);
pstmt = cn.prepareStatement(「select score.* from score ,student 「 + 「where score.stuId = student.id and student.name = ?」);
pstmt.setString(1,studentName);
Resultset rs = pstmt.executeQuery();
while(rs.next()){
system.out.println(rs.getInt(「subject」) + 「 」 + rs.getFloat(「score」) );
}
}catch(Exception e){e.printStackTrace();}
finally{
if(rs != null) try{ rs.close() }catch(exception e){} if(pstmt != null) try{pstmt.close()}catch(exception e){} if(cn != null) try{ cn.close() }catch(exception e){}
}
try {
Connection conn = ...;
Statement stmt = ...;
ResultSet rs = stmt.executeQuery("select * from table1");
while(rs.next()) {
}
} catch(Exception ex) {
}
答:沒有 finally 語句來關閉各個對象,另外,使用 finally 以後,要把變量的定義放在 try 語句塊的外面,以便在 try 語句塊以外的 finally 塊中仍能夠訪問這些變量。
J2EE 服務器啓動時會創建必定數量的池鏈接,並一直維持很多於此數目的池鏈接。客戶端程序須要鏈接時,池驅動程序會返回一個未使用的池鏈接並將其表記爲忙。若是當前沒有空閒鏈接,池驅動程序就新建必定數量的鏈接,新建鏈接的數量有配置參數決定。當使用的池鏈接調用完成後,池驅動程序將此鏈接表記爲空閒,其餘調用就能夠使用這個鏈接。
實現方式,返回的 Connection 是原始 Connection 的代理,代理 Connection 的 close 方法不是真正關鏈接,而是把它代理的 Connection 對象還回到鏈接池中。
orm 是一種思想,就是把 object 轉變成數據庫中的記錄,或者把數據庫中的記錄轉變成objecdt,咱們能夠用 jdbc 來實現這種思想,其實,若是咱們的項目是嚴格按照 oop 方式編寫的話,咱們的 jdbc 程序無論是有意仍是無心,就已經在實現 orm 的工做了。
如今有許多 orm 工具,它們底層調用 jdbc 來實現了 orm 工做,咱們直接使用這些工具,就省去了直接使用 jdbc 的繁瑣細節,提升了開發效率,如今用的較多的 orm 工具是 hibernate。也據說一些其餘 orm 工具,如 toplink,ojb 等。
答:一種分頁方法
<%
int i=1;
int numPages=14;
String pages = request.getParameter("page") ;
int currentPage = 1;
currentPage = (pages==null)?(1):{Integer.parseInt(pages)} sql = "select count(*) from tables"; ResultSet rs = DBLink.executeQuery(sql) ;
while(rs.next()) i = rs.getInt(1) ;
int intPageCount=1;
intPageCount=(i%numPages==0)?(i/numPages):(i/numPages+1);
int nextPage ;
int upPage;
nextPage = currentPage+1;
if (nextPage>=intPageCount) nextPage=intPageCount;
upPage = currentPage-1;
if (upPage<=1) upPage=1;
rs.close();
sql="select * from tables";
rs=DBLink.executeQuery(sql);
i=0;
while((i<numPages*(currentPage-1))&&rs.next()){i++;}
%>
//輸出內容
//輸出翻頁鏈接
合計:<%=currentPage%>/<%=intPageCount%>頁
<a href="List.jsp?page=1">第一頁</a>
<a href="List.jsp?page=<%=upPage%>">上一頁</a> <%
for(int j=1;j<=intPageCount;j++){
if(currentPage!=j){
%>
<a href="list.jsp?page=<%=j%>">[<%=j%>]</a>
<%
}else{
out.println(j);
}
}
%>
<a href="List.jsp?page=<%=nextPage%>">下一頁</a>
<a href="List.jsp?page=<%=intPageCount%>">最後頁</a>
答:方法分別爲:
1) Hibernate 的分頁:
Query query = session.createQuery("from Student"); query.setFirstResult(firstResult);//設置每頁開始的記錄號query.setMaxResults(resultNumber);//設置每頁顯示的記錄數 Collection students = query.list();
2) JDBC 的分頁:根據不一樣的數據庫採用不一樣的 sql 分頁語句
例 如 : Oracle 中 的 sql 語 句 爲 : "SELECT * FROM (SELECT a.*, rownum r FROM TB_STUDENT) WHERE r between 2 and 10" 查詢從記錄號 2 到記錄號 10 之間的全部記錄
7、 XML 部分
答:有 DOM,SAX,STAX 等
DOM:處理大型文件時其性能降低的很是厲害。這個問題是由 DOM 的樹結構所形成的,這
種結構佔用的內存較多,並且 DOM 必須在解析文件以前把整個文檔裝入內存,適合對 XML 的隨機訪問 SAX:不現於 DOM,SAX 是事件驅動型的 XML 解析方式。它順序讀取 XML 文件,不須要一次所有裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶經過在其回調事件中寫入處理代碼來處理 XML 文件,適合
對 XML 的順序訪問
STAX:Streaming API for XML (StAX)
答:用到了數據存貯,信息配置兩方面。在作數據交換平臺時,將不能數據源的數據組裝成 XML 文件,而後將 XML 文件壓縮打包加密後經過網絡傳送給接收者,接收解密與解壓縮後再同 XML 文件中還原相關信息進行處理。在作軟件配置時,利用 XML 能夠很方便的進行,軟件的各類配置參數都存貯在 XML 文件中。
答:看以下代碼,用編碼方式加以解決
package test;
import java.io.*;
public class DOMTest {
private String inFile = "c:\\people.xml"
private String outFile = "c:\\people.xml"
public static void main(String args[]) {
new DOMTest();
}
public DOMTest() {
try {
javax.xml.parsers.DocumentBuilder builder =
javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();
org.w3c.dom.Document doc = builder.newDocument();
org.w3c.dom.Element root = doc.createElement("老師");
org.w3c.dom.Element wang = doc.createElement("王");
org.w3c.dom.Element liu = doc.createElement("劉");
wang.appendChild(doc.createTextNode("我是王老師"));
root.appendChild(wang);
doc.appendChild(root);
javax.xml.transform.Transformer transformer =
javax.xml.transform.TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "gb2312");
transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
transformer.transform(new javax.xml.transform.dom.DOMSource(doc),
new
javax.xml.transform.stream.StreamResult(outFile));
}
catch (Exception e) {
System.out.println (e.getMessage());
}
}
}
答:用 SAX 方式解析 XML,XML 文件以下:
<?xml version=1.0 encoding=gb2312?>
<person>
<name>王小明</name>
<college>信息學院</college>
<telephone>6258113</telephone>
<notes>男,1955 年生,博士,95 年調入海南大學</notes> </person>
事件回調類 SAXHandler.java
import java.io.*;
import java.util.Hashtable;
import org.xml.sax.*;
public class SAXHandler extends HandlerBase {
private Hashtable table = new Hashtable();
private String currentElement = null;
private String currentValue = null;
public void setTable(Hashtable table) {
this.table = table;
}
public Hashtable getTable() {
return table;
}
public void startElement(String tag, AttributeList attrs)
throws SAXException {
currentElement = tag;
}
public void characters(char[] ch, int start, int length)
throws SAXException {
currentValue = new String(ch, start, length);
}
public void endElement(String name) throws SAXException { if (currentElement.equals(name)) table.put(currentElement, currentValue); }
}
JSP 內容顯示源碼,SaxXml.jsp:
<HTML>
<HEAD>
<TITLE>剖析 XML 文件 people.xml</TITLE>
</HEAD>
<BODY>
<%@ page errorPage=ErrPage.jsp
contentType=text/html;charset=GB2312 %>
<%@ page import=java.io.* %>
<%@ page import=java.util.Hashtable %>
<%@ page import=org.w3c.dom.* %>
<%@ page import=org.xml.sax.* %>
<%@ page import=javax.xml.parsers.SAXParserFactory %> <%@ page import=javax.xml.parsers.SAXParser %> <%@ page import=SAXHandler %> <%
File file = new File(c:\people.xml);
FileReader reader = new FileReader(file);
Parser parser;
SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); SAXHandler handler = new SAXHandler();
sp.parse(new InputSource(reader), handler);
Hashtable hashTable = handler.getTable();
out.println(<TABLE BORDER=2><CAPTION>教師信息表</CAPTION>);
out.println(<TR><TD>姓名</TD> + <TD> +
(String)hashTable.get(new String(name)) + </TD></TR>);
out.println(<TR><TD>學院</TD> + <TD> +
(String)hashTable.get(new String(college))+</TD></TR>);
out.println(<TR><TD>電話</TD> + <TD> +
(String)hashTable.get(new String(telephone)) + </TD></TR>);
out.println(<TR><TD>備註</TD> + <TD> +
(String)hashTable.get(new String(notes)) + </TD></TR>);
out.println(</TABLE>);
%>
</BODY>
</HTML>
a: 兩種形式 dtd schema,b: 本質區別:schema 自己是 xml 的,能夠被 XML 解析器解析(這
也是從 DTD 上發展 schema 的根本目的),c:有 DOM,SAX,STAX 等
DOM:處理大型文件時其性能降低的很是厲害。這個問題是由 DOM 的樹結構所形成的,這種結構佔用的內存較多,並且 DOM 必須在解析文件以前把整個文檔裝入內存,適合對 XML 的隨機訪問。
SAX:不現於 DOM,SAX 是事件驅動型的 XML 解析方式。它順序讀取 XML 文件,不須要一次所有裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶經過在其回調事件中寫入處理代碼來處理 XML 文件,適合對 XML 的順序訪問
STAX:Streaming API for XML (StAX)
8、流行的框架與新技術
典型的 JavaEE 三層結構,分爲表現層、中間層(業務邏輯層)和數據服務層。三層體系將業務規則、數據訪問及合法性校驗等工做放在中間層處理。客戶端不直接與數據庫交互,而是經過組件與中間層創建鏈接,再由中間層與數據庫交互。
表現層是傳統的 JSP 技術,自 1999 年問世以來,通過多年的發展,其普遍的應用和穩定的表現,爲其做爲表現層技術打下了堅實的基礎。
中間層採用的是流行的 Spring+Hibernate,爲了將控制層與業務邏輯層分離,又細分爲如下幾種。
Web 層,就是 MVC 模式裏面的「C」(controller),負責控制業務邏輯層與表現層的交互,調用業務邏輯層,並將業務數據返回給表現層做組織表現,該系統的 MVC 框架採用 Struts。 Service 層(就是業務邏輯層),負責實現業務邏輯。業務邏輯層以 DAO 層爲基礎,經過對 DAO 組件的正面模式包裝,完成系統所要求的業務邏輯。
DAO 層,負責與持久化對象交互。該層封裝了數據的增、刪、查、改的操做。
PO,持久化對象。經過實體關係映射工具將關係型數據庫的數據映射成對象,很方便地實現以面向對象方式操做數據庫,該系統採用 Hibernate 做爲 ORM 框架。
Spring 的做用貫穿了整個中間層,將 Web 層、Service 層、DAO 層及 PO 無縫整合,其數據服務層用來存放數據。
/WEB-INF/struts-config.xml)將各個請求分別分配給不一樣的 action 去處理。
一個擴展知識點:struts 的配置文件能夠有多個,能夠按模塊配置各自的配置文件,這樣可
以防止配置文件的過分膨脹;
對象呢?看配置文件。
3.要說明的是, ActionServlet 把 formbean 對象傳遞給 action 的 execute 方法以前,可能會調
用 formbean 的 validate 方法進行校驗,只有校驗經過後纔將這個 formbean 對象傳遞給 action
的 execute 方法,不然,它將返回一個錯誤頁面,這個錯誤頁面由 input 屬性指定,(看配置文件)做者爲何將這裏命名爲 input 屬性,而不是 error 屬性,咱們後面結合實際的運行
效果進行分析。
4.action 執行完後要返回顯示的結果視圖,這個結果視圖是用一個 ActionForward 對象來表示的,actionforward 對象經過 struts-config.xml 配置文件中的配置關聯到某個 jsp 頁面,由於程序中使用的是在 struts-config.xml 配置文件爲 jsp 頁面設置的邏輯名,這樣能夠實現 action 程序代碼與返回的 jsp 頁面名稱的解耦。
你對 struts 可能還有本身的應用方面的經驗,那也要一併說出來。
和操做怎麼樣,本質上都是要獲得一個結果,程序上一個時刻和下一個時刻的運行結果的差別就表如今內存中的對象狀態發生了變化。
2.爲了在關機和內存空間不夠的情況下,保持程序的運行狀態,須要將內存中的對象狀態保存到持久化設備和從持久化設備中恢復出對象的狀態,一般都是保存到關係數據庫來保存大量對象信息。從 Java 程序的運行功能上來說,保存對象狀態的功能相比系統運行的其餘功能來講,應該是一個很不起眼的附屬功能,java 採用 jdbc 來實現這個功能,這個不起眼的功能卻要編寫大量的代碼,而作的事情僅僅是保存對象和恢復對象,而且那些大量的 jdbc 代碼並無什麼技術含量,基本上是採用一套例行公事的標準代碼模板來編寫,是一種苦活和重複性的工做。
3.經過數據庫保存 java 程序運行時產生的對象和恢復對象,其實就是實現了 java 對象與關係數據庫記錄的映射關係,稱爲 ORM(即 Object Relation Mapping),人們能夠經過封裝 JDBC 代碼來實現了這種功能,封裝出來的產品稱之爲 ORM 框架,Hibernate 就是其中的一種流行 ORM 框架。使用 Hibernate 框架,不用寫 JDBC 代碼,僅僅是調用一個 save 方法,就能夠將對象保存到關係數據庫中,僅僅是調用一個 get 方法,就能夠從數據庫中加載出一個對象。
4.使用 Hibernate 的基本流程是:配置 Configuration 對象、產生 SessionFactory、建立 session 對象,啓動事務,完成 CRUD 操做,提交事務,關閉 session。
5.使用 Hibernate 時,先要配置 hibernate.cfg.xml 文件,其中配置數據庫鏈接信息和方言等,還要爲每一個實體配置相應的 hbm.xml 文件,hibernate.cfg.xml 文件中須要登記每一個 hbm.xml 文件。
6.在應用 Hibernate 時,重點要了解 Session 的緩存原理,級聯,延遲加載和 hql 查詢。
面向切面編程(AOP)提供另一種角度來思考程序結構,經過這種方式彌補了面向對象編程(OOP)的不足
除了類(classes)之外,AOP 提供了切面。切面對關注點進行模塊化,例如橫切多個類型和對象的事務管理
Spring 的一個關鍵的組件就是 AOP 框架,能夠自由選擇是否使用 AOP 提供聲明式企業服務,特別是爲了替代 EJB 聲明式服務。最重要的服務是聲明性事務管理,這個服務創建在 Spring 的抽象事物管理之上
容許用戶實現自定義切面,用 AOP 來完善 OOP 的使用.能夠把 Spring AOP 看做是對 Spring 的一種加強
1.Spring 實現了工廠模式的工廠類(在這裏有必要解釋清楚什麼是工廠模式),這個類名爲BeanFactory(其實是一個接口),在程序中一般 BeanFactory 的子類 ApplicationContext。 Spring 至關於一個大的工廠類,在其配置文件中經過<bean>元素配置用於建立實例對象的類名和實例對象的屬性。
Computer computer = null; public void code(){
//Computer computer = new IBMComputer(); //Computer computer = beanfacotry.getComputer();
computer.write();
}
public void setComputer(Computer computer){
this.computer = computer;
}
}
另外兩種方式都由依賴,第一個直接依賴於目標類,第二個把依賴轉移到工廠上,第三個完全與目標和工廠解耦了。在 spring 的配置文件中配置片斷以下:
<bean id=」computer」 class=」cn.itcast.interview.Computer」> </bean>
<bean id=」programmer」 class=」cn.itcast.interview.Programmer」> <property name=」computer」 ref=」computer」></property>
</bean>
能夠實現相同的方法聲明,一是實現相同的接口,二是做爲目標的子類在,JDK 中採用 Proxy 類產生動態代理的方式爲某個接口生成實現類,若是要爲某個類生成子類,則能夠用 CGLI
B。在生成的代理類的方法中加入系統功能和調用目標類的相應方法,系統功能的代理以 Advice 對象進行提供,顯然要建立出代理對象,至少須要目標類和 Advice 類。spring 提供了這種支持,只須要在 spring 配置文件中配置這兩個元素便可實現代理和 aop 功能,例如, <bean id=」proxy」 type=」org.spring.framework.aop.ProxyBeanFactory」>
<property name=」target」 ref=」」></property>
<property name=」advisor」 ref=」」></property>
</bean>
優勢:1. 實現 MVC 模式,結構清晰,使開發者只關注業務邏輯的實現.
2.有豐富的 tag 能夠用 ,Struts 的標記庫(Taglib),如能靈活動用,能大大提升開發效率
勢體現得更加明顯。
缺點:
轉到展現層時,須要配置 forward,若是有十個展現層的 jsp,須要配置十次 struts,並且還不包括有時候目錄、文件變動,須要從新修改 forward,注意,每次修改配置以後,要求從新部署整個項目,而 tomcate 這樣的服務器,還必須從新啓動服務器
2、 Struts 的 Action 必需是 thread-safe 方式,它僅僅容許一個實例去處理全部的請求。
因此 action 用到的全部的資源都必需統一同步,這個就引發了線程安全的問題。
測試不方便. Struts 的每一個 Action 都同 Web 層耦合在一塊兒,這樣它的測試依賴於 Web 容器,
單元測試也很難實現。不過有一個 Junit 的擴展工具 Struts TestCase 能夠實現它的單元測試。
類型的轉換 . Struts 的 FormBean 把全部的數據都做爲 String 類型,它能夠使用工具 Commons-Beanutils 進行類型轉化。但它的轉化都是在 Class 級別,並且轉化的類型是不可配置的。類型轉化時的錯誤信息返回給用戶也是很是困難的。
對 Servlet 的依賴性過強. Struts 處理 Action 時必須要依賴 ServletRequest 和 ServletResponse,
全部它擺脫不了 Servlet 容器。
前端表達式語言方面.Struts 集成了 JSTL,因此它主要使用 JSTL 的表達式語言來獲取數據。
但是 JSTL 的表達式語言在 Collection 和索引屬性方面處理顯得很弱。
對 Action 執行的控制困難. Struts 建立一個 Action,若是想控制它的執行順序將會很是困難。
甚至你要從新去寫 Servlet 來實現你的這個功能需求。
對 Action 執行前和後的處理. Struts 處理 Action 的時候是基於 class 的 hierarchies,很難在 action 處理前和後進行操做。
對事件支持不夠. 在 struts 中,實際是一個表單 Form 對應一個 Action 類(或 DispatchAction),
換一句話說:在 Struts 中實際是一個表單只能 對應一個事件,struts 這種事件方式稱爲 application event,application event 和 component event 相比是一種粗粒度的事件.
Struts 是採用 Java Servlet/JavaServer Pages 技術,開發 Web 應用程序的開放源碼的 framework。採用 Struts 能開發出基於 MVC(Model-View-Controller)設計模式的應用構架。 Struts 有以下的主要功能: 一.包含一個 controller servlet,能將用戶的請求發送到相應的 Action 對象。二.JSP 自由 tag 庫,而且在 controller servlet 中提供關聯支持,幫助開發員建立交互式表單應用。 三.提供了一系列實用對象:XML 處理、經過 Java reflection APIs 自動處理 JavaBeans 屬性、國際化的提示和消息。
1.都是 MVC 的 WEB 框架,
2.struts1 的老牌框架,應用很普遍,有很好的羣衆基礎,使用它開發風險很小,成本更低! struts2 雖然基於這個框架,可是應用羣衆並多,相對不成熟,未知的風險和變化不少,開發人員相對很差招,使用它開發項目的風險係數更大,用人成本更高!
3.struts2 畢竟是站在前輩的基礎設計出來,它會改善和完善 struts1 中的一些缺陷,struts1 中一些懸而未決問題在 struts2 獲得瞭解決。
4.struts1 的前端控制器是一個 Servlet,名稱爲 ActionServlet,struts2 的前端控制器是一個 filter,
在 struts2.0 中叫 FilterDispatcher,在 struts2.1 中叫 StrutsPrepareAndExecuteFilter。
5.struts1 的 action 須要繼承 Action 類,struts2 的 action 能夠不繼承任何類;struts1 對同一個路徑的全部請求共享一個 Action 實例,struts2 對同一個路徑的每一個請求分別使用一個獨立 Action 實例對象,全部對於 struts2 的 Action 不用考慮線程安全問題。
6.在 struts1 中使用 formbean 封裝請求參數,在 struts2 中直接使用 action 的屬性來封裝請求參數。
7.struts1 中的多個業務方法放在一個 Action 中時(即繼承 DispatchAction 時),要麼都校驗,要麼都不校驗;對於 struts2 ,能夠指定只對某個方法進行校驗,當一個 Action 繼承了 ActionSupport 且在這個類中只編寫了 validateXxx()方法,那麼則只對 Xxx()方法進行校驗。(一個請求來了的執行流程進行分析,struts2 是自動支持分模塊開發,並能夠不一樣模塊設置不一樣的 url 前綴,這是經過 package 的 namespace 來實現的;struts2 是支持多種類型的視圖; struts2 的視圖地址能夠是動態的,即視圖的名稱是支持變量方式的,舉例,論壇發帖失敗後回來還要傳遞 boardid。視圖內容顯示方面:它的標籤用 ognl,要 el 強大不少,在國際化方
面支持分模塊管理,兩個模塊用到一樣的 key,對應不一樣的消息;)
與 Struts1 不一樣,Struts2 對用戶的每一次請求都會建立一個 Action,因此 Struts2 中的 Action
是線程安全的。
我印象最深入的是:struts 配置文件中的 redirect 視圖的 url 不能接受參數,而 struts2 配置文件中的 redirect 視圖能夠接受參數。
答://首先得到 SessionFactory 的對象
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); //而後得到 session 的對象
Session session = sessionFactory.openSession();
//其次得到 Transaction 的對象
Transaction tx = session.beginTransaction();
//執行相關的數據庫操做:增,刪,改,查
session.save(user); //增長, user 是 User 類的對象
session.delete(user); //刪除
session.update(user); //更新
Query query = session.createQuery(「from User」); //查詢
List list = query.list();
//提交事務
tx.commit();
//若是有異常,咱們還要做事務的回滾,恢復到操做以前 tx.rollback();
//最後還要關閉 session,釋放資源
session.close()
答:Configuration 接口:配置 Hibernate,根據其啓動 hibernate,建立 SessionFactory 對象;
SessionFactory 接口:初始化 Hibernate,充當數據存儲源的代理,建立 session 對象, sessionFactory 是線程安全的,意味着它的同一個實例能夠被應用的多個線程共享,是重量級、二級緩存;
Session 接口:負責保存、更新、刪除、加載和查詢對象,是線程不安全的,避免多個線程共享同一個 session,是輕量級、一級緩存;
Transaction 接口:管理事務;
Query 和 Criteria 接口:執行數據庫的查詢。
相同點:屏蔽 jdbc api 的底層訪問細節,使用咱們不用與 jdbc api 打交道,就能夠訪問數據。 jdbc api 編程流程固定,還將 sql 語句與 java 代碼混雜在了一塊兒,常常須要拼湊 sql 語句,細節很繁瑣。
ibatis 的好處:屏蔽 jdbc api 的底層訪問細節;將 sql 語句與 java 代碼進行分離;提供了將結
果集自動封裝稱爲實體對象和對象的集合的功能, queryForList 返回對象集合,用 queryForObject 返回單個對象;提供了自動將實體對象的屬性傳遞給 sql 語句的參數。 Hibernate 是一個全自動的 orm 映射工具,它能夠自動生成 sql 語句,ibatis 須要咱們本身在 xml 配置文件中寫 sql 語句,hibernate 要比 ibatis 功能負責和強大不少。由於 hibernate 自動生成 sql 語句,咱們沒法控制該語句,咱們就沒法去寫特定的高效率的 sql。對於一些不太複雜的 sql 查詢,hibernate 能夠很好幫咱們完成,可是,對於特別複雜的查詢,hibernate 就很難適應了,這時候用 ibatis 就是不錯的選擇,由於 ibatis 仍是由咱們本身寫 sql 語句。
答:對象關係映射(Object—Relational Mapping,簡稱 ORM)是一種爲了解決面向對象與面向關係數據庫存在的互不匹配的現象的技術;簡單的說,ORM 是經過使用描述對象和數據庫之間映射的元數據,將 java 程序中的對象自動持久化到關係數據庫中;本質上就是將數據從一種形式轉換到另一種形式。
解決方案一,按照 Object[]數據取出數據,而後本身組 bean
解決方案二,對每一個表的 bean 寫構造函數,好比表一要查出 field1,field2 兩個字段,那麼有一個構造函數就是 Bean(type1 filed1,type2
field2) ,而後在 hql 裏面就能夠直接生成這個 bean 了。
解決方案一,按照 Object[]數據取出數據,而後本身組 bean
解決方案二,對每一個表的 bean 寫構造函數,好比表一要查出 field1,field2 兩個字段,那麼有一個構造函數就是 Bean(type1 filed1,type2
field2) ,而後在 hql 裏面就能夠直接生成這個 bean 了。
解決方案一,按照 Object[]數據取出數據,而後本身組 bean
解決方案二,對每一個表的 bean 寫構造函數,好比表一要查出 field1,field2 兩個字段,那麼有一個構造函數就是 Bean(type1 filed1,type2
field2) ,而後在 hql 裏面就能夠直接生成這個 bean 了。
解決方案一,按照 Object[]數據取出數據,而後本身組 bean
解決方案二,對每一個表的 bean 寫構造函數,好比表一要查出 field1,field2 兩個字段,那麼有一個構造函數就是 Bean(type1 filed1,type2
field2) ,而後在 hql 裏面就能夠直接生成這個 bean 了。
解決方案一,按照 Object[]數據取出數據,而後本身組 bean
解決方案二,對每一個表的 bean 寫構造函數,好比表一要查出 field1,field2 兩個字段,那麼有一個構造函數就是 Bean(type1 filed1,type2
field2) ,而後在 hql 裏面就能夠直接生成這個 bean 了。
按照如下思路來回答:(1)首先說清楚什麼是緩存,(2)再說有了 hibernate 的 Session 就是一級緩存,即有了一級緩存,爲何還要有二級緩存,(3)最後再說如何配置 Hibernate 的二級緩存。
(1)緩存就是把之前從數據庫中查詢出來和使用過的對象保存在內存中(一個數據結構中),這個數據結構一般是相似於 Hashmap,當之後要使用某個對象時,先查詢緩存中是否有這個對象,若是有則使用緩存中的對象,若是沒有則去查詢數據庫,並將查詢出來的對象保存在緩存中,以便下次使用。下面是緩存的僞代碼:
引出 hibernate 的第二級緩存,用下面的僞代碼分析了 Cache 的實現原理 Dao{
hashmap map = new map();
User getUser(integer id){
User user = map.get(id)
if(user == null){
user = session.get(id);
map.put(id,user);
}
return user;
}
}
Dao{
Cache cache = null
setCache(Cache cache){
this.cache = cache
}
User getUser(int id){
if(cache!=null){
User user = cache.get(id);
if(user ==null){
user = session.get(id);
cache.put(id,user);
}
return user;
}
return session.get(id);
}
}
(2)Hibernate 的 Session 就是一種緩存,咱們一般將之稱爲 Hibernate 的一級緩存,當想使
用 session 從數據庫中查詢出一個對象時,Session 也是先從本身內部查看是否存在這個對象,存在則直接返回,不存在纔去訪問數據庫,並將查詢的結果保存在本身內部。因爲 Session 表明一次會話過程,一個 Session 與一個數據庫鏈接相關連,因此 Session 最好不要長時間保持打開,一般僅用於一個事務當中,在事務結束時就應關閉。而且 Session 是線程不安全
的,被多個線程共享時容易出現問題。一般只有那種全局意義上的緩存纔是真正的緩存應用,纔有較大的緩存價值,所以,Hibernate 的 Session 這一級緩存的緩存做用並不明顯,應用價值不大。Hibernate 的二級緩存就是要爲 Hibernate 配置一種全局緩存,讓多個線程和多個事務均可以共享這個緩存。咱們但願的是一我的使用過,其餘人也能夠使用,session 沒有這種效果。
(3)二級緩存是獨立於 Hibernate 的軟件部件,屬於第三方的產品,多個廠商和組織都提供有緩存產品,例如,EHCache 和 OSCache 等等。在 Hibernate 中使用二級緩存,首先就要在 hibernate.cfg.xml 配置文件中配置使用哪一個廠家的緩存產品,接着須要配置該緩存產品本身的配置文件,最後要配置 Hibernate 中的哪些實體對象要歸入到二級緩存的管理中。明白了二級緩存原理和有了這個思路後,很容易配置起 Hibernate 的二級緩存。擴展知識:一個 SessionFactory 能夠關聯一個二級緩存,也即一個二級緩存只能負責緩存一個數據庫中的數據,當使用 Hibernate 的二級緩存後,注意不要有其餘的應用或 SessionFactory 來更改當前數據庫中的數據,這樣緩存的數據就會與數據庫中的實際數據不一致。
"Hello"的 XML 配置文件該怎麼寫?
JDO 是 Java 對象持久化的新的規範,爲 java data object 的簡稱,也是一個用於存取某種數據倉庫中的對象的標準化 API。JDO 提供了透明的對象存儲,所以對開發人員來講,存儲數據對象徹底不須要額外的代碼(如 JDBC API 的使用)。這些繁瑣的例行工做已經轉移到 JDO 產品提供商身上,使開發人員解脫出來,從而集中時間和精力在業務邏輯上。另外,JDO很靈活,由於它能夠在任何數據底層上運行。JDBC 只是面向關係數據庫(RDBMS)JDO更通用,提供到任何數據底層的存儲功能,好比關係數據庫、文件、XML 以及對象數據庫(ODBMS)等等,使得應用可移植性更強。
第一:
在 web 應用啓動時就會加載初始化 ActionServlet,ActionServlet 從 struts-config.xml 文件中讀取配置信息,把它們存放到各類配置對象,例如:Action 的映射信息存放在 ActionMapping 對象中.當 ActionServlet 接收到一個客戶請求時,將執行以下流程.
-(1)檢索和用戶請求匹配的 ActionMapping 實例,若是不存在,就返回請求路徑無效信息;
-(2)若是 ActionForm 實例不存在,就建立一個 ActionForm 對象,把客戶提交的表單數據保存到 ActionForm 對象中;
-(3)根據配置信息決定是否須要表單驗證.若是須要驗證,就調用 ActionForm 的 validate()方法;
-(4) 若是 ActionForm 的 validate() 方法返回 null 或返回一個不包含 ActionMessage 的 ActuibErrors 對象,就表示表單驗證成功;
-(5)ActionServlet 根據 ActionMapping 所包含的映射信息決定將請求轉發給哪一個 Action,若是相應的 Action 實例不存在,就先建立這個實例,而後調用 Action 的 execute()方法; -(6)Action 的 execute()方法返回一個 ActionForward 對象,ActionServlet 再把客戶請求轉發給 ActionForward 對象指向的 JSP 組件;
-(7)ActionForward 對象指向 JSP 組件生成動態網頁,返回給客戶;
第二:
在 web 服務器啓動的時候就會自動加載 ActionServlet,ActionServlet 從 struts-config.xml 文
件中讀取配置信息,把他們存放到各類配置對象.當 ActionServlet 接收到一個客戶請求的時
候,將執行以下流程:
(1)檢索和用戶請求匹配的 ActionMapping 實例,若是不存在,就返回請求路徑無效
(2)若是 ActionForm 實例不存在,就建立一個 ActionForm 對象,把客戶提交的表單數據保存到 ActionForm 中
(3)根據配置信息決定是否須要表單驗證,若是須要驗證,就調用 ActionForm 的 validate() 方法;
(4)在 ActionForm()的方法返回一個 null 或者一個不包含 ActionMeassage 的 ActionError 對象,就表示表單驗證經過
(5)ActionServlet 根據 ActionMapping 所包含的映射信息決定將請求轉發個哪一個 Action,若是相應的 Action 不存在,就建立這個實例,再調用其 execute()方法
(6)Action 的 execute()方法返回一個 ActionForward 對象,ActionServlet 再把客戶請求轉發
給 ActionForward 對象所指的 jsp 組建
(7)ActionForward 對象指向 jsp 組件生成的動態網頁,返回給客戶。
Struts 使用 Model 2 架構。Struts 的 ActionServlet 控制導航流。其餘 Struts 類,好比 Action, 用來訪問業務邏輯類。當 ActionServlet 從容器接收到一個請求,它使用 URI (或者
路徑「path」) 來決定那個 Action 將用來處理請求。一個 Action 能夠校驗輸入,而且訪問業務層以從數據庫或其餘數據服務中檢索信息。爲校驗輸入或者使用輸入來更新數據庫, Action 須要知道什麼指被提交上來。並非強制每一個 Action 從請求中抓取這些值,而是由
ActionServlet 將輸入綁定到 JavaBean 中。
輸入 bean 是 Struts ActionForm c 類的子類。ActionServlet 經過查找請求的路徑能夠決定使用 哪 個 ActionForm , Action 也 是 通 過 同 樣 的 方 法 選 取 的 。 ActionForm 擴 展 org.apache.struts.action.ActionForm 類。每一個都必須以 HTTP 響應進行應答。一般, Struts Action 並不自行加工響應信息,而是將請求轉發到其餘資源,好比 JSP 頁面。Struts 提供一個 ActionForward 類,用來將一個頁面的路徑存儲爲邏輯名稱。當完成業務邏輯後, Action 選擇並向 Servlet 返回一個 ActionForward。Servlet 而後使用存儲在 ActionForward 對
象中的路徑來調用頁面完成響應。
Struts 將這些細節都綁定在一個 ActionMapping 對象中。每一個 ActionMapping 相對於一個特定的路徑。當某個路徑被請求時,Servlet 就查詢 ActionMapping 對象。ActionMapping 對象告訴 servlet,哪一個 Actions, ActionForms, 和 ActionForwards 將被使用。全部這些細節,關
於 Action , ActionForm , ActionForward , ActionMapping ,以及其餘一些東西,都在 struts-config.xml 文件中定義。 ActionServlet 在啓動時讀取這個配置文件,並建立一個配置對象數據庫。在運行時,Struts 應用根據文件建立的配置對象,而不是文件自己。
第三:
1. ActionServlet:Struts 的 ActionServlet 控制導航流。當 ActionServlet 從容器接到一個請求,它使用 URI(或者「path」)也決定哪一個 Action 來處理請求。< Control Layout >
2. Action:用來訪問業務邏輯類。一個 Action 能夠校驗輸入,而且訪問業務層以從數
據庫檢索信息。爲校驗輸入或者使用輸入來更新數據庫,Action 須要知道什麼值被提交上來。它並非強制每一個 Action 都要從請求中抓取這些值,而是由 ActionServlet 將輸入綁定
到 JavaBean 中。< Model Layout >
3.ActionForm:輸入 bean 是 Struts ActionServlet 類的子類。ActionServlet 經過查找請求的路徑能夠決定使用哪一個 ActionForm(輸入 Javabean ) ,Action 也是經過一樣的方法選取的。ActionForm 擴展了 org.apache.struts.action.ActionForm 類。< Data >
4.ActionMapping : Struts 將這些細節綁定在一個 ActionMapping 對象中。每一個 ActionMapping 相對於一個特定的路徑。當某個路徑被請求時, Servlet 就查詢
ActionMapping 對象。ActionMapping 對象告訴 servlet ,哪些個 Action ,ActionForm ,和 ActionForward 將被本次請求使用。
每一個請求都必須以 HTTP 響應進行應答。一般,Struts Action 並不自行渲染響應信息,而是將請求轉發到其餘資源,好比 JSP 頁面。Struts 提供一個 ActionForward 類,用於將一個頁面的路徑保存爲邏輯名稱。當完成業務邏輯後,Action 選擇並向 Servlet 返回一個 ActionForward 。Servlet 而後使用保存在 ActionForward 對象中的路徑來調用頁面完成響應。
全部這些細節,關於 Action,ActionForm,ActionForward,ActionMapping,以及其它一些東西,都在 struts-config.xml 文件中定義。ActionServlet 在啓動時讀取這個配置文件,
並建立一個配置對象數據庫。在運行時,Struts 應用根據文件建立的配置對象,而不是文件自己。
9、軟件工程與設計模式
標準建模語言 UML。用例圖,靜態圖(包括類圖、對象圖和包圖),行爲圖,交互圖(順序圖,合做圖),實現圖。
總共 23 種,分爲三大類:建立型,結構型,行爲型 |
|
|
我只記得其中經常使用的 六、7 種,分別是: |
|
|
建立型(工廠、工廠方法、抽象工廠、單例) |
|
|
結構型(包裝、適配器,組合,代理) |
|
|
行爲(觀察者,模版,策略) |
|
|
而後再針對你熟悉的模式談談你的理解便可。 |
|
|
Java 中的 23 種設計模式: |
|
|
Factory(工廠模式), |
Builder(建造模式), |
Factory Method(工廠方法模式), |
Prototype(原始模型模式),Singleton(單例模式), |
Facade(門面模式), |
|
Adapter(適配器模式), |
Bridge(橋樑模式), |
Composite(合成模式), |
Decorator(裝飾模式), |
Flyweight(享元模式), |
Proxy(代理模式), |
Command(命令模式), |
Interpreter(解釋器模式), Visitor(訪問者模式), |
|
Iterator(迭代子模式), |
Mediator(調停者模式), |
Memento(備忘錄模式), |
Observer(觀察者模式), |
State(狀態模式), |
Strategy(策略模式), |
Template Method(模板方法模式), Chain Of Responsibleity(責任鏈模式)
工廠模式:工廠模式是一種常常被使用到的模式,根據工廠模式實現的類能夠根據提供的數據生成一組類中某一個類的實例,一般這一組類有一個公共的抽象父類而且實現了相同的方法,可是這些方法針對不一樣的數據進行了不一樣的操做。首先須要定義一個基類,該類的子類經過不一樣的方法實現了基類中的方法。而後須要定義一個工廠類,工廠類能夠根據條件生成不一樣的子類實例。當獲得子類的實例後,開發人員能夠調用基類中的方法而沒必要考慮到底返回的是哪個子類的實例。
每一個模式都描述了一個在咱們的環境中不斷出現的問題,而後描述了該問題的解決方案的核
心。經過這種方式,你能夠無數次地使用那些已有的解決方案,無需在重複相同的工做。主要用到了 MVC 的設計模式。用來開發 JSP/Servlet 或者 J2EE 的相關應用。簡單工廠模式等。
10、j2ee 部分
C/S 是 Client/Server 的縮寫。服務器一般採用高性能的 PC、工做站或小型機,並採用大型數據庫系統,如 Oracle、Sybase、InFORMix 或 SQL Server。客戶端須要安裝專用的客戶端軟件。
B/S是 Brower/Server 的縮寫,客戶機上只要安裝一個瀏覽器(Browser),如 Netscape Navigator
或 Internet Explorer,服務器安裝 Oracle、Sybase、InFORMix 或 SQL Server 等數據庫。在這種結構下,用戶界面徹底經過 WWW 瀏覽器實現,一部分事務邏輯在前端實現,可是主要
事務邏輯在服務器端實現。瀏覽器經過Web Server 同數據庫進行數據交互。
C/S 與 B/S 區別:
1.硬件環境不一樣:
C/S 通常創建在專用的網絡上, 小範圍裏的網絡環境, 局域網之間再經過專門服務器提供鏈接和數據交換服務.
B/S 創建在廣域網之上的, 沒必要是專門的網絡硬件環境,例與電話上網, 租用設備. 信息本身管理. 有比 C/S 更強的適應範圍, 通常只要有操做系統和瀏覽器就行
2.對安全要求不一樣
C/S 通常面向相對固定的用戶羣, 對信息安全的控制能力很強. 通常高度機密的信息系統採用 C/S 結構適宜. 能夠經過 B/S 發佈部分可公開信息.
B/S 創建在廣域網之上, 對安全的控制能力相對弱, 可能面向不可知的用戶。
3.對程序架構不一樣
C/S 程序能夠更加註重流程, 能夠對權限多層次校驗, 對系統運行速度能夠較少考慮. B/S 對安全以及訪問速度的多重的考慮, 創建在須要更加優化的基礎之上. 比 C/S 有更
高的要求 B/S 結構的程序架構是發展的趨勢, 從 MS 的.Net 系列的 BizTalk 2000 Exchange 2000 等, 全面支持網絡的構件搭建的系統. SUN 和 IBM 推的 JavaBean 構件技術等,使 B/S
更加成熟.
4.軟件重用不一樣
C/S 程序能夠不可避免的總體性考慮, 構件的重用性不如在 B/S 要求下的構件的重用性
好.
B/S 對的多重結構,要求構件相對獨立的功能. 可以相對較好的重用.就入買來的餐桌能夠再利用,而不是作在牆上的石頭桌子
5.系統維護不一樣
C/S 程序因爲總體性, 必須總體考察, 處理出現的問題以及系統升級. 升級難. 多是再作一個全新的系統
B/S 構件組成,方面構件個別的更換,實現系統的無縫升級. 系統維護開銷減到最小.用戶從網上本身下載安裝就能夠實現升級.
6.處理問題不一樣
C/S 程序能夠處理用戶面固定, 而且在相同區域, 安全要求高需求, 與操做系統相關.應該都是相同的系統
B/S 創建在廣域網上, 面向不一樣的用戶羣, 分散地域, 這是 C/S 沒法做到的. 與操做系統平臺關係最小.
7.用戶接口不一樣
C/S 可能是創建的 Window 平臺上,表現方法有限,對程序員廣泛要求較高
B/S 創建在瀏覽器上, 有更加豐富和生動的表現方式與用戶交流. 而且大部分難度減低,減低開發成本.
8.信息流不一樣
C/S 程序通常是典型的中央集權的機械式處理, 交互性相對低
B/S 信息流向可變化, B-B B-C B-G 等信息、流向的變化, 更像交易中心。
BEA WebLogic Server,IBM WebSphere Application Server,Oracle9i Application Server,jBoss, Tomcat
答:Je22 是 Sun 公司提出的多層(multi-diered),分佈式(distributed),基於組件(component-base)的企業級應用模型(enterpriese application model).在這樣的一個應用系統中,可按照功能劃分爲不一樣的組件,這些組件又可在不一樣計算機上,而且處於相應的層次(tier)中。所屬層次包括客戶層(clietn tier)組件,web 層和組件,Business 層和組件,企業信息系統(EIS)層。一個另類的回答:j2ee 就是增刪改查。
J2EE 自己是一個標準,一個爲企業分佈式應用的開發提供的標準平臺。
J2EE 也是一個框架,包括 JDBC、JNDI、RMI、JMS、EJB、JTA 等技術。
web 容器:給處於其中的應用程序組件(JSP,SERVLET)提供一個環境,使 JSP,SERVLET 直接更容器中的環境變量接口交互,沒必要關注其它系統問題。主要有 WEB 服務器來實現。例如:TOMCAT,WEBLOGIC,WEBSPHERE 等。該容器提供的接口嚴格遵照 J2EE 規範中的 WEB APPLICATION 標準。咱們把遵照以上標準的 WEB 服務器就叫作 J2EE 中的 WEB 容器。
EJB 容器:Enterprise java bean 容器。更具備行業領域特點。他提供給運行在其中的組件 EJB 各類管理功能。只要知足 J2EE 規範的 EJB 放入該容器,立刻就會被容器進行高效率的管理。而且能夠經過現成的接口來得到系統級別的服務。例如郵件服務、事務管理。
JNDI:(Java Naming & Directory Interface)JAVA 命名目錄服務。主要提供的功能是:提供一個目錄系統,讓其它各地的應用程序在其上面留下本身的索引,從而知足快速查找和定位分佈式應用程序的功能。
JMS:(Java Message Service)JAVA 消息服務。主要實現各個應用程序之間的通信。包括點對點和廣播。
JTA:(Java Transaction API)JAVA 事務服務。提供各類分佈式事務服務。應用程序只需調用其提供的接口便可。
JAF:(Java Action FrameWork)JAVA 安全認證框架。提供一些安全控制方面的框架。讓開
發者經過各類部署和自定義實現本身的個性安全控制策略。
RMI/IIOP:(Remote Method Invocation /internet 對象請求中介協議)他們主要用於經過遠程調用服務。例如,遠程有一臺計算機上運行一個程序,它提供股票分析服務,咱們能夠在本地計算機上實現對其直接調用。固然這是要經過必定的規範才能在異構的系統之間進行通
信。RMI 是 JAVA 特有的。
(這個問題不做具體回答,列出來只是告訴讀者可能會遇到什麼問題,你不須要面面俱到,什麼都精通。)
在啓動 Weblogic 的腳本中(位於所在 Domian 對應服務器目錄下的 startServerName),增長 set MEM_ARGS=-Xms32m -Xmx200m,能夠調整最小內存爲 32M,最大 200M
能夠在管理控制檯中修改對應服務器的啓動模式爲開發或產品模式之一。或者修改服務的啓動文件或者 commenv 文件,增長 set PRODUCTION_MODE=true。
修改服務啓動文件,增長 WLS_USER 和 WLS_PW 項。也能夠在 boot.properties 文件中增長加密過的用戶名和密碼.
保存在此 Domain 的 config.xml 文件中,它是服務器的核心配置文件。
放入何目錄下,然的在瀏覽器上就可打入 http://主機:端口號//helloword.jsp 就能夠看到運行結果了? 又好比這其中用到了一個本身寫的 javaBean 該如何辦?
Domain 目錄服務器目錄 applications,將應用目錄放在此目錄下將能夠做爲應用訪問,若是
是 Web 應用,應用目錄須要知足 Web 應用目錄要求,jsp 文件能夠直接放在應用目錄中, Javabean 須要放在應用目錄的 WEB-INF 目錄的 classes 目錄中,設置服務器的缺省應用將能夠實如今瀏覽器上無需輸入應用名。
不 同 類 型 的 EJB 涉 及 的 配 置 文 件 不 同 , 都 涉 及 到 的 配 置 文 件 包 括 ejb-jar.xml,weblogic-ejb-jar.xmlCMP 實體 Bean 通常還須要 weblogic-cmp-rdbms-jar.xml
缺省安裝中使用 DemoIdentity.jks 和 DemoTrust.jks KeyStore 實現 SSL,須要配置服務器使
用 Enable SSL,配置其端口,在產品模式下須要從 CA 獲取私有密鑰和數字證書,建立 identity 和 trust keystore,裝載得到的密鑰和數字證書。能夠配置此 SSL 鏈接是單向仍是雙向的。
能夠使用管理控制檯,在它的 Deployment 中能夠查看全部已發佈的 EJB
11、 ejb 部分
EJB 包括 Session Bean、Entity Bean、Message Driven Bean,基於 JNDI、RMI、JAT 等技術
實現。
SessionBean 在 J2EE 應用程序中被用來完成一些服務器端的業務操做,例如訪問數據庫、調用其餘 EJB 組件。EntityBean 被用來表明應用系統中用到的數據。
對於客戶機,SessionBean 是一種非持久性對象,它實現某些在服務器上運行的業務邏輯。對於客戶機,EntityBean 是一種持久性對象,它表明一個存儲在持久性存儲器中的實體的對象視圖,或是一個由現有企業應用程序實現的實體。
Session Bean 還能夠再細分爲 Stateful Session Bean 與 Stateless Session Bean ,這兩種的 Session Bean 均可以將系統邏輯放在 method 之中執行,不一樣的是 Stateful Session Bean 能夠記錄呼叫者的狀態,所以一般來講,一個使用者會有一個相對應的 Stateful Session Bean 的實體。Stateless Session Bean 雖然也是邏輯組件,可是他卻不負責記錄使用者狀態,也就是說當使用者呼叫 Stateless Session Bean 的時候, EJB Container 並不會找尋特定的 Stateless Session Bean 的實體來執行這個 method。換言之,極可能數個使用者在執行某個 Stateless Session Bean 的 methods 時,會是同一個 Bean 的 Instance 在執行。從內存方面來看, Stateful Session Bean 與 Stateless Session Bean 比較, Stateful Session Bean 會消耗 J2EE Server 較多的內存,然而 Stateful Session Bean 的優點卻在於他能夠維持使用者的狀態。
Java Bean 是可複用的組件,對 Java Bean 並無嚴格的規範,理論上講,任何一個 Java 類均可以是一個 Bean。但一般狀況下,因爲 Java Bean 是被容器所建立(如 Tomcat)的,所
以 Java Bean 應具備一個無參的構造器,另外,一般 Java Bean 還要實現 Serializable 接口用於實現 Bean 的持久性。Java Bean 實際上至關於微軟 COM 模型中的本地進程內 COM 組件,它是不能被跨進程訪問的。Enterprise Java Bean 至關於 DCOM,即分佈式組件。它是基於
Java 的遠程方法調用(RMI)技術的,因此 EJB 能夠被遠程訪問(跨進程、跨計算機)。
但 EJB 必須被佈署在諸如 Webspere、WebLogic 這樣的容器中,EJB 客戶從不直接訪問真正的 EJB 組件,而是經過其容器訪問。EJB 容器是 EJB 組件的代理,EJB 組件由容器所建立和管理。客戶經過容器來訪問真正的 EJB 組件。
SessionBean:Stateless Session Bean 的生命週期是由容器決定的,當客戶機發出請求要創建一個 Bean 的實例時,EJB 容器不必定要建立一個新的 Bean 的實例供客戶機調用,而是隨便找一個現有的實例提供給客戶機。當客戶機第一次調用一個 Stateful Session Bean 時,容器必須當即在服務器中建立一個新的 Bean 實例,並關聯到客戶機上,之後此客戶機調用 Stateful Session Bean 的方法時容器會把調用分派到與此客戶機相關聯的 Bean 實例。
EntityBean:Entity Beans 能存活相對較長的時間,而且狀態是持續的。只要數據庫中的數據存在,Entity beans 就一直存活。而不是按照應用程序或者服務進程來講的。即便 EJB 容器崩潰了,Entity beans 也是存活的。Entity Beans 生命週期可以被容器或者 Beans 本身管理。 EJB 經過如下技術管理實務:對象管理組織(OMG)的對象實務服務(OTS),Sun Microsystems
的 Transaction Service(JTS)、Java Transaction API(JTA),開發組(X/Open)的 XA 接口。
主要提供聲明週期管理、代碼產生、持續性管理、安全、事務管理、鎖和併發行管理等服務。
以 Stateful Session Bean 爲例:其 Cache 大小決定了內存中能夠同時存在的 Bean 實例的數量,根據 MRU 或 NRU 算法,實例在激活和去激活狀態之間遷移,激活機制是當客戶端調用某個 EJB 實例業務方法時,若是對應 EJB Object 發現本身沒有綁定對應的 Bean 實例則從其去
激活 Bean 存儲中(經過序列化機制存儲實例)回覆(激活)此實例。狀態變遷前會調用對應的 ejbActive 和 ejbPassivate 方法。
會話(Session)Bean ,實體(Entity)Bean 消息驅動的(Message Driven)Bean會話 Bean 又可分爲有狀態(Stateful)和無狀態(Stateless)兩種
實體 Bean 可分爲 Bean 管理的持續性(BMP)和容器管理的持續性(CMP)兩種
設置 JNDI 服務工廠以及 JNDI 服務地址系統屬性,查找 Home 接口,從 Home 接口調用 Create 方法建立 Remote 接口,經過 Remote 接口調用其業務方法。
12、 webservice 部分
Web Service:Web Service 是基於網絡的、分佈式的模塊化組件,它執行特定的任務,遵照
具體的技術規範,這些規範使得 Web Service 能與其餘兼容的組件進行互操做。
JAXP:(Java API for XML Parsing) 定義了在 Java 中使用 DOM, SAX, XSLT 的通用的接口。
這樣在你的程序中你只要使用這些通用的接口,當你須要改變具體的實現時候也不須要修改
代碼。
JAXM:(Java API for XML Messaging) 是爲 SOAP 通訊提供訪問方法和傳輸機制的 API。
SOAP:即簡單對象訪問協議(Simple Object Access Protocol),它是用於交換 XML 編碼信息
的輕量級協議。
UDDI:的目的是爲電子商務創建標準;UDDI 是一套基於 Web 的、分佈式的、爲 Web Service 提供的、信息註冊中心的實現標準規範,同時也包含一組使企業能將自身提供的 Web Service 註冊,以使別的企業可以發現的訪問協議的實現標準。
WSDL:是一種 XML 格式,用於將網絡服務描述爲一組端點,這些端點對包含面向文檔信息或面向過程信息的消息進行操做。這種格式首先對操做和消息進行抽象描述,而後將其綁定到具體的網絡協議和消息格式上以定義端點。相關的具體端點即組合成爲抽象端點(服務)。
CORBA 標準是公共對象請求代理結構(Common Object Request Broker Architecture),由對象管理組織 (Object Management Group,縮寫爲 OMG)標準化。它的組成是接口定義語言(IDL),語言綁定(binding:也譯爲聯編)和容許應用程序間互操做的協議。 其目的爲:用不一樣的程序設計語言書寫在不一樣的進程中運行,爲不一樣的操做系統開發。
十3、 Linux
LINUX 實現的就是基於核心輕量級進程的"一對一"線程模型,一個線程實體對應一個核心
輕量級進程,而線程之間的管理在覈外函數庫中實現。
GDI 類爲圖像設備編程接口類庫。
十4、 問得稀裏糊塗的題
會話做用域 ServletsJSP 頁面描述
page 否是表明與一個頁面相關的對象和屬性。一個頁面由一個編譯好的 Java servlet 類(能夠帶有任何的 include 指令,可是沒有 include 動做)表示。這既包括 servlet 又包括被編譯成 servlet 的 JSP 頁面
request 是是表明與 Web 客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個 Web 組件(因爲 forward 指令和 include 動做的關係)
session 是是表明與用於某個 Web 客戶機的一個用戶體驗相關的對象和屬性。一個 Web 會話能夠也常常會跨越多個客戶機請求
application 是是表明與整個 Web 應用程序相關的對象和屬性。這實質上是跨越整個 Web 應用程序,包括多個頁面、請求和會話的一個全局做用域
區別主要答兩點:a.條件操做只能操做布爾型的,而邏輯操做不只能夠操做布爾型,並且能夠操做數值型
b.邏輯操做不會產生短路
十5、 其餘
四、WEB SERVICE 名詞解釋。JSWDL 開發包的介紹。JAXP 、JAXM 的解釋。SOAP 、 UDDI,WSDL 解釋。
Apache Tomcat is the servlet container that is used in the official Reference Implementation for the Java Servlet and JavaServer Pages technologies. The Java Servlet and JavaServer Pages specifications are developed by Sun under the Java Community Process.
Apache Tomcat is developed in an open and participatory environment and released under the Apache Software License. Apache Tomcat is intended to be a collaboration of the best-of-breed developers from around the world. We invite you to participate in this open development project. To learn more about getting involved, click here.
Apache Tomcat powers numerous large-scale, mission-critical web applications across a diverse range of industries and organizations. Some of these users and their stories are listed on the PoweredBy wiki page.
1. Talk about overriding, overloading.
2. Talk about JAVA design patterns you known.
3. Talk about the difference between LinkList, ArrayList and Victor.
4. Talk about the difference between an Abstract class and an Interface.
6. Why we use StringBuffer when concatenating strings?
7. Try to explain Singleton to us? Is it thread safe? If no, how to make it thread safe?
8. Try to explain Ioc?
9. How to set many-to-many relationship in Hibernate?
Administrator 10:34:20
1,堆和棧的區別,有一個 64k 的字符串,是放到堆上,仍是放到棧上,爲何?
2,何時用到接口,何時用到抽象類,兩者區別
3,有一個 100 萬的數組,裏邊有兩個市重複的,如何設計算法找到。
4,設計數據庫時,n 維,如何設計。
例如[省份][城市][網吧],這是三維關係,它的表也應該有三個,網吧有外鍵引用城市,城市有外鍵應用省份,這個規律就是下層的要有一外鍵去引用上層。
一:Java 基礎部分新特性:
自動拆箱
可變參數與 for each 循環
靜態導入
二:IO 流部分:
printf 格式輸出
Scanner
三:其餘
泛型
枚舉
Annotation
試題 1:編寫一個程序,這個程序把一個整數數組中的每一個元素用逗號鏈接成一個字符串,例如,根據內容爲[1][2][3]的數組造成內容爲"1,2,3"的字符串。
試題 2:請在一個類中編寫一個方法,這個方法搜索一個字符數組中是否存在某個字符,若是存在,則返回這個字符在字符數組中第一次出現的位置(序號從 0 開始計算),不然,返回-1。要搜索的字符數組和字符都以參數形式傳遞給該方法,若是傳入的數組爲 null,應拋
出 IllegalArgumentException 異常。在類的 main 方法中以各類可能出現的狀況測試驗證該方法編寫得是否正確,例如,字符不存在,字符存在,傳入的數組爲 null 等。
試題 3:編寫一個程序,它先將鍵盤上輸入的一個字符串轉換成十進制整數,而後打印出這個十進制整數對應的二進制形式。這個程序要考慮輸入的字符串不能轉換成一個十進制整數的狀況,並對轉換失敗的緣由要區分出是數字太大,仍是其中包含有非數字字符的狀況。
提示:十進制數轉二進制數的方式是用這個數除以 2,餘數就是二進制數的最低位,接着再用獲得的商做爲被除數去除以 2,此次獲得的餘數就是次低位,如此循環,直到被除數
爲 0 爲止。其實,只要明白了打印出一個十進制數的每一位的方式(不斷除以 10,獲得的餘數就分別是個位,十位,百位),就很容易理解十進制數轉二進制數的這種方式。
試題 4:請用移位的方式打印出一個十進制整數的十六進制形式。提示:按每4個二進制位對整數進行移位和去高位處理,獲得的結果就是十六進制數的一位,而後按下面三種方式之一(做爲做業,要求每種方式都用到)計算出一個十六進制數值對應的十六進制形式:
(1)0-9 之間的數值直接加上字符'0',9 以上的數值減去 10 之後再加上字符'A'
(2)定義一個數組,其中包含 0-F 這些字符,而後用要計算的數值做爲數組的索引號,即
可得到其對應的十六進制數據。
(3)Character.forDigit 靜態方法能夠將一個十六進制的數字轉變成其對應的字符表示形式,例如,根據數值 15 返回字符'F'。
試題 5:編寫一個程序,用於實現文件的備份,程序運行時的命令語法爲:
java MyCopy (sourcefile) (destfile)
試題 6:請編寫一個字符輸入流的包裝類,經過這個包裝類對底層字符輸入流進行包裝,讓程序經過這個包裝類讀取某個文本文件(例如,一個 java 源文件)時,可以在讀取的每行前面都加上有行號和冒號。
試題 7:請參照《Java就業培訓教程》書第八章編寫一個窗口程序,用戶單擊窗口上的「×」按紐時,能關閉該窗口。
試題 8:請結合咱們的《javascript網頁開發》一書中介紹的正則表達式與String.split方法,從"http://www.it315.org/get.jsp?user=zxx&pass=123"這樣的 URL 地址中提取出每一個參數的名稱和值。這裏要注意在正則表達式中要對?進行轉義處理。
試題 9:利用Socket套接字進行面向鏈接通訊的編程。客戶端讀取本地文件併發送;服務器接收文件並保存到本地文件系統中。
試題 10:在javascript視頻的第七講的第一個片段,講到了將一個保存有ip地址與地區對照關係的文本文件導入到數據庫時,應該將其中的某些空格替換成逗號(,),即對於以下格式的文本文件內容:
起始 IP |
結束 IP |
地區 |
61.54.231.245 |
61.54.231.245 |
河南省安陽市 新世紀網吧 |
61.54.231.246 |
61.54.231.246 |
河南省安陽市 未知地區 |
61.54.231.9 |
61.54.231.247 |
河南省安陽市 紅日網吧 |
61.54.231.248 |
61.54.231.248 |
河南省安陽市 安陽師範學院 |
61.54.231.249 |
61.54.231.249 |
河南省安陽市 黑蜘蛛網吧(師範學院附近) |
應轉換成下面的這種格式:
61.54.231.245,61.54.231.245 ,河南省安陽市 新世紀網吧
61.54.231.246,61.54.231.246,河南省安陽市 未知地區
61.54.231.9,61.54.231.247 ,河南省安陽市 紅日網吧
61.54.231.248,61.54.231.248,河南省安陽市 安陽師範學院
61.54.231.249,61.54.231.249,河南省安陽市 黑蜘蛛網吧(師範學院附近)
在視頻教程中,講解了使用 UltraEdit 的正則表達式替換功能來完成上面的轉換。從 jdk1.4 開始,java 語言中提供用於處理正則表達式的相關 API 類和方法,在 jdk 幫助文檔中,查看 String 類的 replaceAll 方法,能夠看到該方法就支持相似 UltraEdit 的正則表達式替換功能。任務 1:閱讀String.replaceAll方法的幫助,以及它提供的相關超連接,瞭解該方法的用法後,編寫一個 java 程序來自動實現上面的正則表達式替換,將 a.txt(下載)中的 IP 地址數
字後的空格替換成「,」號後,將替換結果保存到 b.txt 文件中。
任務 2:咱們在實現www.it315.org網站中的ip地區查詢系統時,使用的是相似以下的sql語法:
select 地區 from ip 表 where 用戶 ip>起始 IP and 用戶 ip<結束 ip
經過這條 sql 語句就能夠查詢出用戶 ip 所對應的地區結果。因爲用戶 ip 與起始 ip 和結
束 ip 的比較屬於字符串比較,若是用戶 ip 爲 9.1.1.1,那麼它與 61.54.231.245 比較的結果就是前者大於後者,由於用戶 ip 的第一個字符「9」大於 61.54.231.245 中的第一個字符「6」。現
在請你想出一種解決辦法,讓上面的 sql 語句可以返回正確結果。提示:將 9.1.1.1 變化成 009.001.001.001 後與 061.054.231.245 進行比較就能夠了。
請按這種思路用正則表達式改進你的程序,即程序在把 a.txt 文件中的 IP 地址轉換後保存到 b.txt 文件中時,能在每一個不足 3 位的 IP 地址前補 0,以補齊 3 位。例如 61.5.23.115,這個 IP 地址保存到 b.txt 文件中的形式應爲 061.005.023.115。
在源程序中,要對程序代碼的功能進行註釋說明,提交你編寫的程序給咱們時,請附帶該程序的使用說明。
2、Javasript 試題部分
試題 1:請編寫一個相似於以下形式的表單頁面:
試題 2:請按下面內容編寫一個頁面,點頁面裏的「全選」時,能選中或清除上面的全部水果。
選擇你喜歡的水果:
□蘋果
□桔子
□香蕉
□葡萄
□桃子
□全選
試題 3:請登錄訪問http://www.it315.org/bbs/頁面,這個頁面左側導航欄部分能夠收縮、顯示,請參照此頁面編寫一個也能把導航欄收縮、顯示的頁面。
3、JavaWEB 試題部分
試題 1:請設計一個XML格式的文件,該文件能表達出一個國家中的每一個省及省長名字,每一個省下面的每一個市及市長名字,每一個市下面的每一個鎮及鎮長名字。
試題 2:請簡述HTTP1.0和HTTP1.1的區別,GET提交和POST提交方式的區別。
試題 3:請編寫一個Servlet程序,這個程序能打印出Javascript試題部分試題1表單提交的數據。