1. 程式人生 > >2017 java程式設計師面試寶典

2017 java程式設計師面試寶典

<pre class="java" name="code">1:JDK與JRE
JDK:JAVA Development Kit, java開發工具包; 包括各種類庫和工具,當然也包括JRE
JRE:JAVA Runtime Environment,java程式執行環境,包括JAVA類庫的class檔案和JVM

2:JAVA_HOME PATH CLASSPATH
JAVA_HOME :JDK的安裝目錄,很多web伺服器如tomcat沒有內建JDK,它們通過JAVA_HOME找到JDK
PATH:在原有值後加“;%JAVA_HOME%\bin”;通過配置PATH,可以再任何命令提示符視窗中使用JAVAC、JAVA等命令了
CLASSPATH:用來指定JAVA程式搜尋類的路徑的,JAVA程式在編譯和執行時,先搜尋jre/lib/rt.jar中的類,然後搜尋CLASSPATH中指定的類;一般CLASSPATH會包括當前目錄“.”


3:JAVA程式動態的指定類搜尋路徑方法
利用-cp或-classpath選項,如
javac –cp D:\wrok\log4j.jar Hello.java (編譯時指定D:\wrok\log4j.jar為搜尋路徑)
java –cp D:\wrok\log4j.jar Hello


4:JAVA和C++程式在編譯及執行上的區別
C,C++這類語言的編譯器(例如 UNIX下的CC命令,WINDOWS下的CL命令)都是把原始碼直接編譯成計算機可以認識的機器碼,如EXE、DLL之類的檔案,然後直接執行。
JAVA為了實現跨平臺,多了一箇中間步驟,就是先生成位元組碼檔案,javac命令先把原始檔編譯成計算機無法直接識別的class檔案,但是它可以被JVM所認識,JVM有多個平臺版本,因此可以在多個平臺執行。


5:什麼是JVM及其工作原理
JVM是一個想象中的機器,在實際的計算機上通過軟體模擬來實現。Java虛擬機器有自己想象中的硬體,如處理器、堆疊、暫存器,還有相應的指令系統。JVM在執行位元組碼時,把位元組碼解釋成具體平臺上的機器指令執行。


6:JAVA垃圾回收機制
JVM中棧存放的是非static的自動變數、函式引數、表示式的臨時結果和函式返回值。棧中這些實體資料的分配和釋放均是由系統自動完成的。
堆中存放的實體資料是程式設計師顯示分配的(new),沒有自動垃圾回收機制的系統中(有些JVM沒有垃圾回收機制)必須顯示的釋放這些實體
C、C++中也有棧和堆,對堆的管理,C是通過malloc()和free();而C++是通過new和delete

Object物件中有個finalize(),它會在物件被回收之前被呼叫;
System.gc();和Runtime.getRunime().gc()兩個方法都可以顯示請求開始垃圾回收執行緒



7:jar和war
兩者都是java可執行檔案,jar是對於桌面應用程式,war是對於web應用程式;
Jar和war打包都通過JDK的jar命令


8:JAVA變數及作用範圍
分為:靜態變數、成員變數、區域性變數
靜態變數在類中用static修飾,生存週期由類決定;成員變數是類中沒有用static修飾的變數,生存週期有物件來決定;區域性變數是定義在方法裡的變數、方法的引數、程式碼塊裡的變數,它們的作用範圍用大括號{}來界定


9:JAVA變數分為哪兩種大的資料型別
基本資料型別和引用資料型別 ,它們最大的區別在於引用資料型別存放的是資料所在的地址,而基本資料型別直接存放資料的值;二者都儲存在棧中


10:裝箱、拆箱指的是基本基本資料型別和包裝型別的自動相互轉化


11:C++指標和JAVA引用的區別
相同:都是指向一塊記憶體地址
不同:
一 型別轉換:引用的型別轉換,可能丟擲java.lang.ClassCastException,引用對應的型別不同的話,轉換不成功;C++指標則一定能轉換成功,指向哪兒,還是一個地址
二 初始值:引用型別的初始值為null;C++指標是int,如不初始化,值是隨機的
三 計算:引用不可計算;指標可計算,如++或—
四 記憶體洩露:JAVA引用基本不會產生記憶體洩露;指標容易,程式設計師須及時回收
五 作為引數:JAVA方法本質上只有傳值,引用作為引數使用時,回給函式內引用的COPY,所以在函式內交換兩個引用引數是沒意義的,但改變一個引用引數的屬性是有意義的,因為引用引數的COPY所引用的物件和引用引數是同一個物件;
C++指標作為引數,實際上就是它所指的地址在函式中被操作。

12:equals()和==
==運用在基本資料型別是比較的是實際的值;用於比較引用型別時候,比較兩個引用的地址是否相同;(都是比較棧中資料)
Object有equals()方法,預設的比較方式與==相同,String類重寫了該方法,使其能比較字串的內容;


13:JAVA三元運算子
表示式1?表示式2:表示式3 表示式1為true則執行表示式2 ;否則執行表示式3


14:Java註釋型別
①行註釋 //
②塊註釋 /* ….. */
③文件註釋 /** …. */
④Annotation

15:類與物件
類是一種抽象,JVM對類只加載一次
物件是類的實現,通過new建立,可以建立多個物件;

面向物件特性:封裝、繼承、多型


16:什麼是多型
本質是可傳送訊息給某個物件,讓該物件自行決定響應何種行為。通過將子類物件引用賦值給超類物件引用變數(向上轉型)來實現動態方法呼叫


17:java中靜態成員的特點
類的靜態成員是通過static修飾的成員,主要有:靜態成員變數、靜態方法、靜態程式碼塊;它們具有如下特點:
① 在類載入的時候,就進行建立、初始化或執行程式碼
② 對於一個類來說,都只有一份
③ 類的所有例項都能訪問到它們


18:子類呼叫父類的建構函式
super([args])必須放在子類建構函式的第一行



19:抽象類和介面的區別
① 介面中的方法都是抽象方法,不必寫abstract
② 介面中的屬性為static final 靜態不可修改的常量
③ 最大區別是一個類可以實現多個介面,但只能繼承一個抽象類
④ 抽象類中可以有非抽象方法
面向功能用介面,面向繼承用抽象類;如果屬性得繼承用抽象類;



20:內部類
成員式:靜態內部類和成員內部類
區域性式:普通區域性內部類和匿名內部類

① 靜態內部類:相當於外部類的靜態成員一樣,用static修飾 ,外部類載入時內部類也隨之載入,完整類名是abc.Outter.Inner;無法訪問外部類的非靜態成員
② 成員內部類:相當於外部類的普通成員一樣,沒有用static修飾,需等外部類建立了物件以後才會被載入到JVM中;建立成員內部類方法:
Outter o = new Outter();
Outter.Inner I = o.new Inner();
③ 區域性內部類:定義在一個方法體中,它往往僅作為方法短暫的使用,只能訪問用final
修飾的區域性變數
④ 匿名內部類:也定義在方法體中,但沒有一個具體的名字
如:
Public void adc(){
new OneInterface(){//OneInterface為一個介面名
… //直接提供具體的實現
}
}




21:int和Integer有什麼區別
int 屬於8中基本資料型別之一,4位元組長度,取值範圍:-231~231-1;它儲存在棧中;可以用算術運算子進行加、減…;在引數傳遞時傳遞的是它的值。
Integer是int的包裝類;儲存在堆中;不可以用算術運算子進行加、減…(因此得轉為int);在引數傳遞時傳遞的是它所代表物件的引用。

int a = 10;
Integer b = new Integer(a);//int–Integer
Integer c = Integer.valueOf(a);//int-Integer
a = b.intValue()+1;//Integer–int



22:float f = 2.3(錯) 2.3預設為double型 float f = 2.3f或float f = (float)2.3


23:型別轉換
隱式轉換:由型別位元組長度小的向型別位元組長度大的轉換 如int隱式轉換為double
或者是子類物件賦值給父類的引用
顯示轉換:與上相反
當實型向整型轉換時,會出現精度的損失,而且在進行float或double進行計算時會出現奇怪的問題,這時可以用BigDecimal類進行精確計算

24:用BigDecimal類進行精確計算
BigDecimal提供add() substract() multiply() divide()方法
如:
BigDecimal b1 = new BigDecimal(Double.toString(0.2));
BigDecimal b2 = new BigDecimal(Double.toString(0.3));
System.out.println(b1.add(b2).doubleValue());



25:JAVA不能用0代表false;也不能用非0代表true; 只能用boolean型別的true和false
因此C++中 while(1){…}在JAVA是錯的


26:JAVA中 char型別採用unicode編碼格式,用2個位元組表示一個字元,範圍從0到216-1
char能儲存中文,且相容英文字母(ASCII 0~127)


27:JAVA物件池
從JDK5.0開始,JVM啟動時會例項化9個物件池。這個物件池分別用來儲存8中基本型別的包裝類物件和String物件。主要是為了效率問題。例子:
①String str1 = “hello”;
String str2 = “hello”;
②String str3 = new String(“hello”);
System.out.println(str1==str2);//輸出true
System.out.println(str1==str3);//輸出false


①處用字串字面量(雙引號),JVM到String物件池中去檢查是否有一個值相同的物件,如果有就取現成的物件,如果沒有,則建立一個新的物件,並加入到物件池中;
②處直接建立新的字串,且未加入到物件池


物件池的簡單實現:
class Dog{
private String name;
private int age;
private static HashSet<Dog>pool = new HashSet<Dog>();
public static Dog newInstance(String name,int age){
for(Dog dog:pool){
if(dog.name.equals(name)&&dog.age==age)
return dog;
}
//如果物件池中沒有,建立並加入物件池
Dog dog = new Dog(String,name);
pool.add(dog);
return dog;
}



28:StringBuffer和StringBuilder
Java字串String物件有個特性—不變性,它只能被建立,而不能被修改(只要一改變就新建立一個物件);因此,一些大量使用字串的程式(如字串拼接)可能會出現效能瓶頸,甚至記憶體溢位;這時需要用到StringBuffer或StringBuilder,兩者API相似,但StringBuilder能保證執行緒安全。
簡單示例:
StringBuffer sb = new StringBuffer();
sb.append(“a”);
String str = sb.toString();


29:如何輸出反轉過後的字串
方法1:利用字串儲存字元的原理,取出它的char陣列,進行重新排列;
方法2:利用StringBuffer的reverse()方法



30:JAVA陣列本質上是一個類,該類儲存了資料型別的資訊。通過成員變數的形式來儲存資料,並且通過[],使用下標來訪問這些資料。將基本資料型別初始為各自預設的初始值,如將int初始為0(若程式設計師未提供初始值),將引用資料型別初始為null


31:集合
List: 有序,允許重複
Set: 無序,不允許重複
SortedSet: 排好序的Set
Map: 鍵值對 鍵不可重複
SortedMap: 排好序的Map(根據key排序);

List和Set是Collection的子類

32:迭代器
提供一種訪問一個集合物件中各個元素的途徑,同時又不需要暴露該物件的內部細節。JAVA通過提供Iterable和Iterator兩個介面來實現集合類的可迭代性。從JAVA5.0開始,迭代器可以被foreach迴圈所替代,但是foreach迴圈的本質就是使用Iterator進行遍歷的。


33:比較器
方法1:要比較的自定義類實現Comparable介面,實現其compareTo(Object o)方法
方法2:定義比較器,實現Comparator介面,實現compare(Object o1,Object o2)方法
String類實現了Comparable,用方法1實現了比較
Collections.sort(Collections c,Comparator com)//這時用方法2定義比較器傳入sort方法
Collections.sort(Collections c)//這時用方法1

示例1:
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
………setters and getters…
}


public class CompareByName implements Comparator{
public int compare(Object o1, Object o2) {
Person p1=null;
Person p2=null;
if(o1 instanceof Person&&o2 instanceof Person){
p1 = (Person)o1;
p2 = (Person)o2;
}

/*
if(p1.getName().compareToIgnoreCase(p2.getName())>0){
return 1;
}
if(p1.getName().compareToIgnoreCase(p2.getName())<0){
return -1;
}
return 0;*/
return (p1.getName()).compareTo(p2.getName());
}



public class DemoPerson {
public static void main(String[] args) {
Person p1 = new Person("c",24);
Person p2 = new Person("b",8);
Person p3 = new Person("a",34);

List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(p3);

//未排序之前
for(Person p:list){
System.out.println(p.getName()+":"+p.getAge());
}

//按年齡排序之後
System.out.println("按年齡排序之後");
Collections.sort(list,new CompareByAge());
for(Person p:list){
System.out.println(p.getName()+":"+p.getAge());
}
}



示例2:
public class Dog implements Comparable{
private String name;
private int age;
public Dog(String name,int age){
this.name = name;
this.age = age;
}
//實現Comparable介面的方法
public int compareTo(Object o) {//引數必須是Object
Dog d = null;
if(o instanceof Dog){
d = (Dog)o;
}

if(this.getAge()>d.getAge()){
return 1;
}

if(this.getAge()<d.getAge()){
return -1;
}
return 0;

}

}


public class DemoDog {
public static void main(String[] args) {
Dog d1 = new Dog("c",24);
Dog d2 = new Dog("b",8);
Dog d3 = new Dog("a",34);

List<Dog> list = new ArrayList<Dog>();
list.add(d1);
list.add(d2);
list.add(d3);

//未排序之前
for(Dog d:list){
System.out.println(d.getName()+":"+d.getAge());
}

//按年齡排序之後
System.out.println("按年齡排序之後");
Collections.sort(list);
for(Dog d:list){
System.out.println(d.getName()+":"+d.getAge());
}
}
}


34:Vector和ArrayList
Verctor的大多數成員方法都會加上synchronized關鍵字,也就是說Vector是執行緒安全的;也正因如此,它的執行效率沒ArrayList高;通常建議使用ArrayList


35:HashTable和HashMap的區別
① HashTable是執行緒安全的,HashMap不是
② HashTable不允許null值(key和value都不可以),HashMap可以
③ HashTable有個contains()方法,功能和HashMap的containsValue()一樣
④ HashTable使用Enumeration遍歷,HashMap使用Iterator


36:符合什麼條件的資料集合可以使用foreach迴圈
從JDK5開始可以使用foreach代替迭代器,從語法上講,陣列或者實現了Iterable介面的類例項,都可以用foreach迴圈。

例項1:(迭代器模式方法)
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
………setters and getters…
}


public class Persons implements Iterable<Person>{
List<Person>personlist = new ArrayList<Person>();
//實現Iterable的方法
public Iterator<Person> iterator() {
PersonIterate<Person> pt = new PersonIterate<Person>();
pt.setPersonList(personlist);
return pt;

}

public void add(Person p){
personlist.add(p);
}
}
public class PersonIterate<Person> implements Iterator<Person>{
List<Person>personlist = new ArrayList<Person>();
private int index = 0;

public void setPersonList(List<Person>personlist){
this.personlist = personlist;
}
public boolean hasNext() {
// TODO Auto-generated method stub
return personlist.size()>index;
}

public Person next() {
// TODO Auto-generated method stub
return personlist.get(index++);
}

public void remove() {
// TODO Auto-generated method stub
personlist.remove(index);
}
}

這樣下面的foreach就能成功;
Persons persons = new Persons();
persons.add(new Person("a",1));
persons.add(new Person("b",2));
persons.add(new Person("c",3));

for(Person p:persons){
System.out.println(p.getName()+":"+p.getAge());
}

例項2:(內部類方法)
public class Persons2 implements Iterable<Person>{
List<Person>personlist = new ArrayList<Person>();
public void add(Person p){
personlist.add(p);
}
public Iterator<Person> iterator() {

// TODO Auto-generated method stub
return new Iterator<Person>(){ //用區域性內部類實現
private int index=0;
public boolean hasNext() {
return personlist.size()>index;//可以訪問外部類成員
}

public Person next() {
return personlist.get(index++);
}

public void remove() {
personlist.remove(index);
}
};
}
}

這樣也能進行foreach迴圈了

說明:Persons也可直接實現Iterator介面,並實現其hasNext(),next()等方法,但是這樣的話Persons必須維護一個索引index,其index值是不確定的,如進行迴圈一次,index變為a,緊接著進行第二次迴圈遍歷會得到空結果;
方法1和方法2每次進行迴圈迭代都將產生一個新的索引為0;


37:目錄和檔案操作
Java提供了java.io.File類對目錄和檔案進行操作,主要操作方法包括:路徑字串的構造方法、isDirectory、isFile、createNewFile、list(返回檔案陣列)、getName、delete、getLastModify(返回最近修改時間)、listFile(返回檔名陣列)等


38:隨機存取檔案RandomAccessFile類
主要方法包括new RandomAccessFile(“路徑”,”rw|r|w…”);
length()方法獲得檔案內容長度
seek()定位
read()獲取當前位置資料
write()寫資料
close()關閉開啟的檔案
示例:(將檔案中所有字母a替換成c)
RandomAccessFile raf =new RandomAccessFile("d:/1.txt", "rw");
int len = (int) raf.length();//length()返回long型別
for(int i=0;i<len;i++){
byte b = (byte) raf.read();//raf.read()返回int型別
char c = (char)b;//字母能轉為byte
if(c==’a’){
raf.seek(i);
raf.write(‘c’);
}

}
raf.close();




38:位元組流和字元流
位元組流處理的是計算機最基本的單位byte,它可以處理任何資料格式的資料。主要的操作物件是byte陣列,通過read()和write()方法把byte陣列的資料寫入或讀出
字元流有位元組流包裝而來,字元流建立時,一般要包裝一個位元組流;inputStreamReader提供位元組流向字元流的轉換,其建構函式中可以指定字元編碼格式;
讀取或輸出文字一般用BufferedReader.readLine()和PrintWriter.println()方法;
PrintWriter和BufferedWriter同樣提供可緩衝輸出,但是PrintWriter可設定自動清除緩衝,不需要pw.flush();
PrintWriter pw = new PrintWriter(OutputStreamWirter(“d.txt”,”UTF-8”),true);//true表示自動清除緩衝
注意:①寫時需要flush() ,PrintWriter可設定自動清除
②別忘了關閉流


39:序列化
Java可以通過序列化儲存或傳輸一個物件。序列化本質上是把資料,變成一系列的位元組資料。然後把這些位元組資料寫入到流中。
java.io.Serializable介面是可以進行序列化的類的標誌性介面,該介面本身沒有任何需要實現的方法,僅提供一個能唯一辨別一個類的serialVersionUID.作用是在序列化和反序列化過程中,起到辨別作用。在反序列過程中,若有兩個相同類名的類,就通過serialVersionUID來判斷到底反序列化成哪個類。

序列化:ObjectInputStream.readObject()
反序列化:ObjectOutputStream.writeObject()


40:程序與執行緒區別
① 執行緒隸屬於某個程序
② 程序能獨佔資源,而執行緒不能
③ 執行緒是排程和分配的基本單位,程序是擁有資源的基本單位
④ 程序間通訊困難,而執行緒間共享一塊記憶體區域,通訊方便
⑤ 建立銷燬程序及程序切換系統開銷大,而對於執行緒開銷小


41:Runnable介面和Thread類的區別
兩者都可以實現多執行緒程式設計,區別如下:
① 通過繼承Thread方法定義執行緒就不能繼承其他類,而實現Runnable介面方法可以
② Thread方法為“多執行緒多例項”;Runnable:當多個執行緒公用一個Runnable時為“多執行緒單例項”,這時可以共享一個變數,但可能有執行緒安全問題;當每個執行緒都有各自的Runnable,則為“多執行緒多例項”
③ Runnable可以方便的訪問同一變數,而Thread需要用內部類實現。


ThreadLocal: 執行緒區域性變數(ThreadLocal)其實的功用非常簡單,就是為每一個使用該變數的執行緒都提供一個變數值的副本,是每一個執行緒都可以獨立地改變自己的副本,而不會和其它執行緒的副本衝突。從執行緒的角度看,就好像每一個執行緒都完全擁有該變數。一個ThreadLocal只能存放一個專有物件,若執行緒需要多個專有物件的話,必須設計多個ThreadLocal。ThreadLocal主要有兩個方法set(Object) 和 get()


42:執行緒安全問題是指兩方面的問題:變數安全和執行緒同步(見深入JDK206頁)
解決變數安全的方法:1:多執行緒多例項 2:變數定義在方法中 3:使用ThreadLocal
解決執行緒同步問題用synchronized(監視器) 監視器預設為this;

(單例項,多執行緒中多個Thread基於同一個Runnable建立,Runnable中的變數為多個
Thread共享,所以存在變數安全問題)

ThreadLocal示例:(每個執行緒都實現從1~10累加)
public class Accumulator implements Runnable{
ThreadLocal threadLocal = new ThreadLocal();//建立threadlocal
public void run() {
for(int i=1;i<=10;i++){
if(threadLocal.get()==null){
threadLocal.set(new Integer(0));
}
int x = (Integer) threadLocal.get();
x = x+i;
threadLocal.set(x);

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+
"\t累加和="+threadLocal.get());
}
}
}

public class Main {
public static void main(String[] args) {
Accumulator a = new Accumulator();//單例項多執行緒
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start();
}
}
說明:單例項多執行緒,假如不用ThreadLocal方法,在Accumulator中定義一個成員變數SUM儲存每次累加和,則執行緒共享Runnable的變數SUM,會有變數安全問題;若改為多例項多執行緒則沒有這問題;若將SUM定義在run()方法中也沒有這樣的問題;


43:同步鎖Synchronized(括號內為可能發生同步衝突的監視器,預設為this)
當執行緒執行到Sychronized時候,檢查傳入的監視器物件,並得到該物件的同步鎖,如果得不到(被佔用),就會被加入到一個與該監視器物件相關聯的等待執行緒池中,一直等待同步鎖被釋放,池中的等待執行緒就會得到該同步鎖,然後繼續執行下去,當執行緒執行完同步程式碼塊,就會自動釋放它佔的同步鎖。


44:協調執行—執行緒通訊
wait()方法:使當前呼叫同步方法的執行緒等待,直到其他執行緒呼叫notify()或notifyAll()
notify():喚醒當前同步監視器(sychronized的引數)上正在等待的單個執行緒,如果有多個,隨機喚醒一個。
notifyAll():喚醒當前同步監視器(sychronized的引數)上正在等待的所有執行緒

示例:(實現生產者消費者模式)

public class Store {//倉庫類
private final int MAX_SIZE;
private int count;

public Store(int size,int c){
MAX_SIZE = size;
count = c;
}

//往倉庫放貨物
synchronized public void add(){//synchronized預設的同步監視器為this即Store
if(count>=MAX_SIZE){
System.out.println("倉庫滿了");
try {
wait();//進入與Store例項相關聯的等待執行緒池中
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println("put "+count);
notifyAll();//從執行緒池中喚醒所有等待執行緒,(若是producer被喚醒,則從等待處繼續執行,(count++處)
}

//從倉庫取貨物
synchronized public void get(){
if(count<=0){
System.out.println("倉庫空了");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("get "+count);
count–;
notifyAll();
}
}
public class Producer extends Thread{//生產者執行緒
private Store s;
public Producer(Store s){
this.s = s;
}

public void run(){
while(true){
s.add();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public class Consumer extends Thread{//消費者執行緒
private Store s;
public Consumer(Store s){
this.s = s;
}
public void run(){
while(true){
s.get();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

public class Main {
public static void main(String[] args) {
Store s = new Store(5,0);//單例項多執行緒 共享倉庫
Producer producer1 = new Producer(s);
Producer producer2 = new Producer(s);
Consumer consumer1 = new Consumer(s);
Consumer consumer2 = new Consumer(s);
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
}
}
45:JAVA實現執行緒池
思想:啟動一個執行緒的開銷是相當高的,利用執行緒池,程式向執行緒池傳遞一個實現Runnable介面的類的例項時,程式就會從執行緒池中啟動一個空閒執行緒,並執行run()方法,run方法執行結束後,該執行緒又成為空閒執行緒,回到執行緒池,等待下一個Runnable物件,並執行物件的run()方法,從而大大提高了系統的效能。
通過java.util.concurrent包中的Executors類可以建立執行緒池物件,即建立執行緒
池類ExecutorService的例項。

示例:
public class Thread1 implements Runnable{
public void run() {
System.out.println("this is thread1");
}
}

public class Thread2 implements Runnable{
public void run(){
System.out.println("this is thread2");
}
}

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
//建立執行緒池物件
ExecutorService es = Executors.newFixedThreadPool(5);
es.submit(new Thread1());//傳遞Runnable介面實現類 這是利用回撥嗎?
es.submit(new Thread2());
es.shutdown();//關閉執行緒池物件
}
}

46:反射的原理:反射是為了能夠動態的載入一個類,動態地呼叫一個方法,動態地訪問
一個屬等動態要求而設計的。它的出發點在於JVM會為每個類建立一個java.lang.Class類的例項,通過該物件可以獲取這個類的資訊,然後通過使用java.lang.reflect包下的API以達到各種動態要求。

類在以下情況下會被載入:
① 需要使用該類建立物件。如Student s = new Student() Student會被載入
② 訪問該類的靜態成員。 如system.out.println(Calendar.MONDAY);
③ 使用Class類的forName()方法


類一旦被載入,JVM就會為其建立一個Class物件,如何得到一個類的Class物件?
① Class.forName()返回就是一個Class物件
② 類名.class
③ 物件名.getClass()

47:Field、Method、Construtor
Field通過Class類物件的getDeclaredField()和getDeclaredFields()方法得到;Field方法主要分為兩大類:setXXX(object,value)和getXXX(object)其中object為例項物件。
Method通過Class類物件的getMethod()或getMethods()獲得,Method最常用的方法是invoke(object,引數…)。
Constructor類通過Class類物件的getConstructor(引數型別.class…)獲得。Constructor類最常用的方法是newInstance(),通過建構函式建立例項(Class物件也能通過newInstance()建立例項,但該類中提供無參建構函式)
獲得一個類中私有變數:呼叫Field的setAccessible(true)

示例:
public class Person {
public String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void getInfo(){
System.out.println(name+":"+age);
}
}

public class Main {
public static void main(String[] args) {
Class personclass = Person.class;
try {
Constructor con = personclass.getConstructor(String.class,int.class);
Person person = (Person) con.newInstance("li",20);

Field f = personclass.getDeclaredField("name");
String name = (String) f.get(person);

Field f2 = personclass.getDeclaredField("age");
f2.setAccessible(true);//設定可以訪問私有變數age
int age = (Integer) f2.get(person);

System.out.println(name+":"+age);

Method m = personclass.getDeclaredMethod("getInfo",null);//引數為null,可不寫
m.invoke(person, null);//呼叫方法

} catch (Exception e) {
e.printStackTrace();
}
}
}


48:TCP通訊特點
① 面向連線的傳輸
② 端到端的通訊
③ 可靠性,確保傳輸資料的正確性,不出現丟失或亂序
④ 採用位元組流方式,即以位元組為單位傳輸位元組序列
程式設計模型:
伺服器端:①建立ServerSocket
②accept()等待客戶請求
③獲得輸入流和輸出流,並進行傳輸
④釋放資源,關閉輸入流輸出流、Socket、ServerSocket

49:UDP通訊特點
① UDP是一個無連線協議,當它想傳送資料時就簡單地抓取來自應用程式的資料,並儘可能快地把它扔到網路上
② 不需要維護連線狀態,包括收發狀態等
③ 位元組開銷很小,傳輸速度快,效率高
程式設計模型:(點對點,沒有伺服器與客戶端之分;分為接收端和傳送端)
接收端和客戶端都需要有一下步驟:
① 建立DatagramSocket,指定一個埠號.
② 提供一個byte陣列進行資料的儲存;對於傳送端,還需要提供對端的IP地址和埠號。
③ 呼叫DatagramPacket的receive()或send()方法進行資料的接收或傳送
④ 呼叫DatagramPacket的getData()方法得到byte陣列的資料。
⑤ 釋放資源
說明: DatagramSocket ds = new DatagramSocket(port);//port為接收埠
接收:DatagramPacket dp = new DatagramPacket(buff,len);
ds.receive(dp);
傳送: Datagrampacket dp = new DatagramPacket (str.getBytes() ,0,str.length(),InetAddress.getByName(“localhost”),sendPort);//引數:位元組陣列,offset,len,主機號或IP,對方接收埠號
ds.send(dp);



50:TCP實現web伺服器
public class WebServer {
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(80);
Socket s = null;
System.out.println("伺服器已啟動!");
while(true){
s = ss.accept();
new HTTPThread(s).start();
}
}
}

public class HTTPThread extends Thread{
private Socket socket;
public HTTPThread(Socket s){
this.socket = s;
}

public void run(){
try{
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.println("<HTML>");
pw.println("<body>");
pw.println("hello world");
pw.println("</body>");
pw.println("</html>");
pw.flush();
pw.close();
socket.close();
}catch(IOException e){
e.printStackTrace();
}
}
}

在瀏覽器輸入http://localhost 會顯示hello world



50:UDP實現一個即時聊天軟體
QQ、MSN對安全的要求不是太高,一般會採用UDP的通訊模式。UDP為點對點模式,沒有伺服器和客戶端之分,兩端都建立兩個執行緒,SendThread(傳送訊息)還有ReceiveThread (接收訊息)。程式碼如下:
public class ReceiveThread extends Thread{
private DatagramSocket ds;
public ReceiveThread(int recport){
try {
this.ds = new DatagramSocket(recport);//接收埠
} catch (SocketException e) {
e.printStackTrace();
}
}

public void run(){
try {
byte []buff = new byte[1024];
DatagramPacket dp = new DatagramPacket(buff,1024);
while(true){
ds.receive(dp);//接收資料
String str = new String (dp.getData(),0,dp.getLength());
System.out.println("receive:"+str);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
ds.close();
}
}
}

public class SendThread extends Thread{
private DatagramSocket ds;
private int sendPort;
public SendThread(int sendport,int otherPort){//傳送、對方埠
this.sendPort = sendPort;
try {
this.ds = new DatagramSocket(sendport);//傳送埠
} catch (SocketException e) {
e.printStackTrace();
}
}

public void run(){
try{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while((str = br.readLine())!=null){
DatagramPacket dp = new DatagramPacket (str.getBytes(),0,str.length(),
InetAddress.getByName("localhost"),otherPort);
ds.send(dp);
System.out.println("send:"+str);
}
}catch(Exception ex){
ex.printStackTrace();
}finally{
ds.close();
}
}
}

public class Chat {
public static void main(String[] args) {
new ReceiveThread(7000).start();
new SendThread(7500,9000).start();//傳送埠和對方接受埠
}
}
說明:在本機模擬執行時要兩次執行Chat,並且設定好埠。
如第一次為:7000 7500 9000 第二次則為:9000 7600 7000

TCP實現聊天室思路:多執行緒實現,伺服器維護一個socket的List,每次接收訊息時遍歷list將訊息傳送給每個客戶端。

UDP實現聊天室思路:可以用UDP組播 MultiCastSocekt實現



51:使用JAVA訪問WEB站點
網路爬蟲的原理就是模擬瀏覽器挨個訪問web站點,得到站點網頁的對映
JAVA也可以用程式設計的方式去訪問網站,步驟:
① 用URL建立一個資源定位物件
② 呼叫URL的openConnection()得到HttpURLConnection物件
③ 呼叫HttpURLConnection的connect()方法開啟連線
④ 用getHeaderFields()方法得到響應結果的頭資訊
⑤ 用getInputStream()方法得到輸入流物件,得到響應內容

public class HttpConnTest {
public static void main(String[] args) throws Exception{
//建立URL物件
URL url = new URL("http://www.baidu.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();//開啟連線
//獲取請求響應的頭部資訊
Map<String,List<String>> header = conn.getHeaderFields();
for(String key:header.keySet()){
System.out.println(key+":"+header.get(key));
}
//獲取響應內容
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(),"UTF-8"));
String str = null;
while((str=br.readLine())!=null){
System.out.println(str);
}
conn.disconnect();
}
}


52:JDBC(java data base connectivity)工作原理
JDBC採用了一種驅動模式的設計,提供了兩套的介面:開發者使用的API和資料庫廠商使用的SPI(service provider interface 資料庫廠商需要實現的介面),充分體現了面向介面程式設計的好處。程式設計師無需關心具體資料庫的連線和呼叫,只需要使用JDK中提供的標準API程式設計即可,而具體的實現由特定的資料庫生產商提供,也就是JDBC驅動。

53:JDBC程式設計步驟:
① 註冊驅動程式。就是把驅動程式類載入到JVM,一般用Class.forName(“完整類名”)
② 獲取資料庫連線Connection conn = DriverManager.getConnection(“URL”,”使用者名稱”,”密碼”);
③ 建立會話Statement(用於向資料庫傳送SQL命令,並返回結果) 實際開發中用的更多的是PreparedStatement,它是一種預編譯的會話,用佔位符的方法效率高,且可以避免SQL注入
④ 執行SQL語句。 executeQuery()或executeUpdate()
⑤ 處理結果集。若是查詢操作的話,會返回ResultSet 用其next()方法操作結果集
⑥ 關閉連線。關閉順序ResultSet、Statement、Conection


53:如何使用JDBC事務
事務特性:ACID(原子性、一致性、隔離性、永續性)
1. Atomicity(原子性)
原子性很容易理解,也就是說事務裡的所有操作要麼全部做完,要麼都不做,事務成功的條件是事務裡的所有操作都成功,只要有一個操作失敗,整個事務就失敗,需要回滾。
2. Consistency(一致性)
一致性也比較容易理解,也就是說資料庫要一直處於一致的狀態,事務開始前是一個一致狀態,事務結束後是另一個一致狀態,事務將資料庫從一個一致狀態轉移到另一個一致狀態。
3. Isolation(獨立性)
從字面上來說,獨立性是其中最難理解的一點,但如果結合Oracle中的undo,也就不難理解了。所謂的獨立性就是指併發的事務之間不會互相影響,如果一個事務要訪問的資料正在被另外一個事務修改,只要另外一個事務還未提交,它所訪問的資料就不受未提交事務的影響。換句話說,一個事務的影響在該事務提交前對其它事務是不可見的。
注意:這裡的Isolation跟隔離級別(Isolation Level)是無關的。
4. Durability(永續性)
永續性也不難理解,是指一旦事務提交後,它所做的修改將會永久的儲存在資料庫上,即使出現宕機也不會丟失。

JDBC使用事務步驟:
① 關閉自動提及事務:conn.setAutoCommit(false);
② 捕獲(try catch)執行程式碼,如果發生異常,在catch中conn.rollback()
③ 關閉連線。一般在finally{}中


54:如何使用可滾動的結果集
Statement stmt = conn.createStatement(sql,type,concurrency);
其中concurrency變數用於指定是否可更新的結果集,可取以下值:
① TYPE_FORWARD_ONLY:不允許滾動,只能向前(next());
② TYPE_SCROLL_INSENSITIVE:可滾動,對資料庫變化不敏感,資料庫查詢生產結果集後若發生變化,結果集不發生變化
③ TYPE_SCROLL_SENSITIVE:可滾動,且對資料庫變化敏感
可滾動的結果集方法:
rs.next()、rs.last();//到最後一行、rs.previous();//回一行
rs.absolute(6);//直接定位到第六行

55:如何使用可更新的結果集
當concurrency變數為以下值時:
① CONCUR_READ_ONLY:不能用於更新資料庫
② CONCUR_UPDATABLE:結果集可用於更新資料庫

如:
PreparedSatement pstmt = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
rs = pstmt.executeQuery();
rs.last();
//更新最後一行資料
rs.updateString(2,”aaaa”);//修改值
rs.updateRow();//更新


56:什麼是Servlet
Servlet在JAVA Web伺服器中充當了資訊資源的最小表示單位,代表了一個使用者可以通過瀏覽器獲取的資源,Servlet可以進行無限的擴充套件,它可以使用java的所有類庫資源,為使用者返回文字、圖片、視訊等各類資訊資源。
從程式設計角度看,Servlet是一個java類,這個類需要實現Servlet介面,提供一個公開的無參構造方法。有WEB容器來控制它的建立、初始化、提供服務、銷燬等。它的各種行為方式通過在web.xml中配置決定。
說明:在實際開發中,servelt一般通過繼承自javax.servlet.http.HttpServlet
來建立(HttpServlet也是Servlet的實現),它可以為開發者提供一些方法的預設實現,而且可以區別不同的請求方法(doGet() doPost())


57:Servlet生命週期
① 載入。Servlet類載入到JVM中,並且例項化。這個過程中,web容器會呼叫Servlet類的無參構造方法。預設下,Servlet是在第一次請求時被載入,但可通過在web.xml中配置<load-on-startup>標籤設定在web容器啟動時載入。
② 初始化。呼叫init()方法,如為Servelt配置的初始化引數是在init()中取得的。
③ 提供服務。當有HTTP請求時,呼叫service()方法。如果是繼承自HttpServlet的話,service()會根據請求型別不同調用doGet()或doPost()方法
④ 銷燬。當重新部署web應用,關閉web容器時被銷燬並呼叫destroy()方法

Servlet執行時的基本原理

1)、當WEB客戶請求Servlet服務或當WEB服務啟動時,容器環境載入一個Java Servlet類。
2)、容器環境也將根據客房請求建立一個Servlet物件例項,或者建立多個Servlet物件例項,並把這些例項加入到Servlet例項池中。
3)、容器環境呼叫Servlet的初始化方法HttpServlet.init()進行Servlet例項化。在呼叫初始化時,要給init()方法傳入一個ServletConfig物件,ServletConfig物件包含了初始化引數和容環境的資訊,並負責向servlet傳遞資訊,如果傳遞失敗,則會發生ServletException。Servlet將不能正常工作。
4)、容器環境利用一個HttpServletRequest和HttpServletResponse物件,封裝從Web客戶接收到的HTTP請求和由Servlet生成的響應。
5)、容器環境把HttpServletRequest和HttpServletResponse物件傳遞給HttpServlet.Service()方法。這樣,一個定製的Java Servlet就可以訪問這種HTTP請求和響應介面。Servlet()方法可被多次呼叫,各呼叫過程執行在不同的執行緒中,互不干擾。
6)、定製的Java Servlet從HttpServletRequest物件讀取HTTP請求資料,訪問來自HttpSession或Cookie物件的狀態資訊,進行特定應用的處理,並且用HttpServletResponse物件生成HTTP響應資料。
7)、當WEB伺服器和容器關閉時,會自動呼叫HttpServlet.destroy()方法關閉任何開啟的資源,並進行一些關閉前的處理。

servlet 的生命週期。

Servlet 執行在 Servlet 容器中,其生命週期由容器來管理。 Servlet 的生命週期通過 Servlet 介面中 init ()、 service ()、 destroy ()方法來表示。

Servlet 的生命週期包含了下面 4 個階段。

(1) 載入和例項化

Servlet 容器負責載入和例項化 Servlet 。當 Servlet 容器啟動時,或者在容器檢查到需要這個 Servlet 來響應一個請求時,建立 Servlet 例項。當 Servlet 容器啟動後,它必須要知道所需的 Servlet 類在什麼位置, Servlet 容器可以從本地檔案系統、遠端檔案系統或者其他網路伺服器中通過類載入器載入 Servlet 類,成功載入後,容器建立 Servlet 例項。因為容器是通過 Java 的反射 API 來建立 Servlet 例項,呼叫的是 Servlet 的預設建構函式,也就是那個不帶引數的建構函式,所以我們在編寫 Servlet 類的時候,不應該提供帶引數的建構函式。——這也就是為什麼 Servlet 類可以不寫建構函式的原因。

(2) 初始化

在 Servlet 例項化之後,容器必須呼叫 Servlet 的 init ()方法初始化這個物件。初始化的目的是為了讓 Servlet 物件在處理客戶請求前完成一些初始化工作,如建立資料庫連線,獲取配置資訊等。對於每一個 Servlet 例項, init ()方法只能被呼叫一次。在初始化期間, Servlet 例項可以使用容器為它準備的 ServletConfit 物件從 web 應用程式的配置資訊(在 web.xml 中配置)中獲取初始化的引數資訊。在初始化期間,如果發生錯誤, Servlet 例項可以丟擲異常來通知容器。

(3) 請求處理

Servlet 容器呼叫 Servlet 的 service ()方法對請求進行處理。要注意的是,在 service ()方法呼叫之前, init ()方法必須成功執行。在 service ()方法中, servlet 例項通過 ServletRequest 物件得到客戶端的相關資訊和請求資訊,在對請求進行處理後,呼叫 servletResponse 物件的方法設定響應資訊。

(4) 服務終止

當容器檢測在一個 Servlet 例項應該從服務中被移除的時候,容器就會呼叫例項的 destroy ()方法,以便讓該例項可以釋放它所使用的資源,儲存資料到持久儲存裝置中。當需要釋放記憶體或者容器關閉時,容器就會呼叫 Servlet 例項的 destroy ()方法。在呼叫 destroy ()方法後,容器會釋放這個 Servlet 例項,該例項隨後會被 java 的垃圾收集器回收。



在整個 Servlet 的生命週期過程中,建立 Servlet 例項、呼叫例項的 init ()和 destroy ()方法都只進行一次,當初始化完成後, Servlet 容器會將該例項儲存在記憶體中,通過呼叫它的 service ()方法,為接收到的請求服務。


58:在web.xml中Servlet的標準配置
<servlet>
<!– Servlet的名字,請求時以名字訪問 –>
<servlet-name>LoginServlet</servlet-name>
<!– 完整類名 –>
<servlet-class>com.abc.LoginServelt</servlet-class>
<!– 初始化引數,這些引數在init()方法中取得 –>
<init-param>
<param-name>myparam</param-name>
<param-value>100</param-value>
</init-param>
<!– 在容器啟動時載入 –>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/*Servlet</url-pattern>
</servlet-mapping>


59:Forward(直接轉發或請求轉發)和Redirect(間接轉發或重定向)的區別
① 直接轉發或請求轉發:
RequestDspatcher rs = request.getRequestDispatcher(“url”);
Rs.forward(request,response);
本質上是一次請求,共享同一個request物件,位址列地址不會改變
② 間接轉發或重定向response.sendRedirect(“url”);
本質上是兩次請求,對應兩個不同request物件,請求資訊不一樣。


forward是伺服器請求資源,伺服器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然後把這些內容再發給瀏覽器,瀏覽器根本不知道伺服器傳送的內容是從哪兒來的,所以它的位址列中還是原來的地址。還有,轉發是在web應用程式之內進行的,可以訪問web應用程式所設定的內部目錄,像是WEB-INF目錄,只能在Web應用程式中進行,不能指定至其它的Web應用程式的地址。
redirect就是服務端根據邏輯,傳送一個狀態碼,告訴瀏覽器重新去請求那個地址,一般來說瀏覽器會用剛才請求的所有引數重新請求,所以session,request引數都可以獲取。web應用程式會要求客戶端瀏覽器重新發出請求地址,客戶端會重新連線至所指定的地址,因此瀏覽器的地址會出現重新導向的資訊,重新導向後的請求由瀏覽器發出,所以不能訪問Web應用程式中的隱藏目錄,像是WEB-INF,重新是由瀏覽器重新要求一個網頁,可以指定至其他的Web應用程式地址。

RequestDispatcher.forward()方法和HttpServletResponse.sendRedirect()方法的區別是:前者僅是容器中控制權的轉向,在客戶端瀏覽器位址列中不會顯示出轉向後的地址,他是不會改變Request的值,如果你需要在下一個頁面中能從中獲取新的資訊的話,你可以Request.setAttribute()來放置一些標誌,這樣從下一個頁面中獲取;後者則是完全的跳轉,瀏覽器將會得到跳轉的地址,並重新發送請求連結。這樣,從瀏覽器的位址列中可以看到跳轉後的連結地址。所以,前者更加高效,在前者可以滿足需要時,儘量使用RequestDispatcher.forward()方法,並且,這樣也有助於隱藏實際的連結。在有些情況下,比如,需要跳轉到一個其它伺服器上的資源,則必須使用 HttpServletResponse.sendRequest()方法。

1、forward與include共亨Request範圍內的物件,而redirect則不行,即:如果一個javabean被宣告為request範圍的話,則被forward到的資源也可以訪
問這個javabean,而redriect則不行。
2、forward與include基本上都是轉發到context內部的資源,而redirect可以重定向到外部的資源,如: req.sendRedriect

1、從位址列顯示來說
forward是伺服器請求資源,伺服器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然後把這些內容再發給瀏覽器.瀏覽器根本不知道伺服器傳送
的內容從哪裡來的,所以它的位址列還是原來的地址.
redirect是服務端根據邏輯,傳送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以位址列顯示的是新的URL.
2、從資料共享來說
forward:轉發頁面和轉發到的頁面可以共享request裡面的資料.
redirect:不能共享資料.
3、從運用地方來說
forward:一般用於使用者登陸的時候,根據角色轉發到相應的模組.
redirect:一般用於使用者登出登陸時返回主頁面和跳轉到其它的網站等.
4、從效率來說
forward:高.
redirect:低.

不要僅僅為了把變數傳到下一個頁面而使用session作用域,那會無故增大變數的作用域,轉發也許可以幫助你解決這個問題。

redirect:以前的request中存放的變數全部失效,並進入一個新的request作用域。
forward:以前的request中存放的變數不會失效,就像把兩個頁面拼到了一起。

他們的呼叫分別如下:
request.getRequestDispatcher("apage.jsp").forward(request, response); //轉發到apage.jsp
response.sendRedirect("apage.jsp"); //重定向到apage.jsp

在jsp頁面中你也會看到通過下面的方式實現轉發:
<jsp:forward page="apage.jsp"/>


60:過濾器的作用和原理
過濾器是處於web容器內的一個元件,它會過濾特定請求和響應.當一個請求到來時,web容器判斷是否有過濾器與該資訊資源關聯,若有,則交給過濾器—處理,然後交給目標資源。響應的時候則以相反的順序交給過濾器,最後返回給使用者。過濾器是一種很重要的設計模式(基於AOP思想)可以再不侵入原有程式碼的基礎上為它們提供一些功能。Struts就是利用過濾器工作的。
過濾器要實現javax.servlet.Filter介面,並實現doFilter()方法。

public class MyFilter implements Filter{
//過濾器的業務邏輯方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//業務程式碼
chain.doFilter(request, response);//將請求轉發給下一個過濾器或目標資源
}

public void init(FilterConfig arg0) throws ServletException {
//初始化程式碼
}
public void destroy() {
//釋放資原始碼
}
}

Web.xml中配置:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.abc.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<!– 凡是以"Servlet"結尾的URL資源,該過濾器都起作用 –>
<url-pattern>/*Servlet</url-pattern>
</filter-mapping>


61:監聽器的作用和工作原理
對於web應用程式來說,監聽器是處於web容器內的一個元件,它會對web容器中的request,session和application 3種物件進行監聽。當這些物件在建立或銷燬時,web容器主動呼叫它們的初始化和銷燬方法。
Request事件監聽介面ServletRequestListener;
Session事件監聽介面HttpSessionListener;
Application事件監聽介面ServletContextListener;
應用:當用戶第一次訪問web應用程式是,程式將線上人數加1


62:JSP的執行機制
JSP本質上是Servlet,但JSP的存在是必要的,因為Servlet在處理靜態內容(如HTML標籤)時非常笨拙,不得不以字串形式進行拼接,而JSP可以很好的實現動態和靜態內容的分離。
當客戶端發出一次對某個JSP的請求,web容器處理該請求過程如下:
① web容器會檢驗JSP的語法是否正確
② 將JSP檔案轉換成Servlet的原始碼檔案
③ 編譯該原始碼檔案稱為class檔案
④ 建立一個該Servlet類的物件例項,為請求提供服務
說明:JSP只會在第一次訪問時才轉換盒編譯。以後的訪問web容器就直接呼叫編譯好的servlet物件例項了,如果JSP被修改過,整個過程重新執行一次。


63:JSP內建物件及用途(見文件)

request   型別 javax.servlet.ServletRequest 作用域 Request

response 型別 javax.servlet.SrvletResponse 作用域 Page

 pageContext 型別 javax.servlet.jsp.PageContext 作用域 Page

 session 型別 javax.servlet.http.HttpSession 作用域 Session

 application 型別 javax.servlet.ServletContext 作用域 Application

 out 型別 javax.servlet.jsp.JspWriter 作用域 Page

 config 型別 javax.servlet.ServletConfig 作用域 Page

 page 型別 javax.lang.Object 作用域 Page

 exception 型別 javax.lang.Throwable 作用域 page

64:JSP作用域
JSP比Servlet多了一種頁面範圍(page),一共四種作用域:page,request,session和application。
Request範圍指的是一次請求,如果請求指向一個單一的JSP檔案,則此時的page和request的生命週期是一樣的,但是如果一次請求經過多次請求轉發(forward),則這個request週期可以為多個page週期之和


65:jsp中使用javabean
JavaBean規範:
① 是一個公開類
② 提供一個無引數的構造方法
③ 提供了公開的setXXX和getXXX

JSP使用javabean兩種方法
① 純JAVA程式碼,如Dog dog = new Dog()
② Jsp動作標籤
<jsp:useBean id=”myBean” class=”javabean.MyBean” scope=”page”/>
<jsp:setProperty name=”myBean” property=”username” param=”lidiansheng”/>
<jsp:getProperty name=”myBean” property=”username”/>

Scope屬性預設為page


66:表示式語言EL和JSTL(詳見文件EL OGNL JSTL)
EL是一種資料表現語言 JSTL是一個標籤庫,而EL是從JSTL誕生出來的
EL用${people.name}方法 區別於OGNL{#person.name}或{person.name}(根物件)


67:Struts框架是如何體現MVC模式的?
Controller由ActionServlet、Action和Struts-config.xml組成。
Model由ActionForm來實現
View主要由JSP實現
一次典型的Struts請求是這樣的:客戶端(瀏覽器)傳送請求,然後ActionServlet接收到請求後,會根據請求的路徑和引數來判斷由哪個Action來處理該次請求。等到Action處理完成以後,通常是Execute方法呼叫完成以後,Struts會根據該方法返回的ActionForward來判斷由哪個JSP來作為最終響應。

68:Hibernate實體存在哪幾種狀態
瞬時態、持久態、脫管態



69:Hibernate繼承關係的對映策略
有三個類 Animal Dog Cat
單表策略:當所有子類都在一張表中,只需建一個Animal.hbm.xml 用<subclass>新增子類
<class name=”Animal” table=”animal”>
<id …></id>
<discriminator column=”type” type=”string”></discriminator>//區別子類的欄位
<subclass name=”Dog” discriminator-vlaue=”dog”>//子類
<property>…</property>
</subclass>
<subclass …>…</subclass>
</class>
多表策略:當每個子類一張表,只需建一個Animal.hbm.xml 用<joined-subclass>新增子類
<class name=”Animal” table=”animal”>
<id …></id>
< joined-subclass name=”Dog” table=”dog”>//子類 需指明表名
<key column=”dog_id”></key>//指明dog表的主鍵 同時為指向animal的外來鍵
<property>…</property>
</subclass>
< joined-subclass …>…</subclass>
</class>
這樣資料庫有三個表animal dog cat 其中dog,cat共同屬性放在animal表中
單表策略:無需表連線,查詢速度快,適合多型查詢(查詢有繼承關係的類時,同時查詢到多個子類),缺點是可能造成表太大的結果(因為每個子類屬性不一樣,造成很多NULL),不利於維護。
多表策略:資料儲存比較緊湊,當查詢某個子類的資料時速度比較快,缺點是可能會有很多的表連線,不太適合多型查詢


69:AOP原理
AOP是一種對OOP有益補充的程式設計技術,它可以解決OOP和過程化方法不能夠很好解決的橫切問題,如事務,安全,日誌等。隨著軟體系統變得越來越複雜,橫切關注點成為一個大問題,AOP可以很輕鬆的解決橫切關注點這個問題,Spring框架對AOP提供了很好的支援。簡單來說,AOP就是一種功能比較複雜的攔截器。在程式碼真正達到目標以前,AOP可以對其進行攔截,提供一些通用的中介軟體的服務,例如,加上事務服務,記錄日誌等。Spring的宣告式事務也就是基於AOP實現的。

70:列印1000以內的迴文數字
for(int i=10;i<10000;i++){
int temp = i;
int reverse = 0;
while(temp>0){//反序
reverse = reverse*10+temp%10;
temp=temp/10;
}
if(i==reverse){
System.out.println(i);
}
}
//判斷一個字串是否迴文
String str = "abababa";
StringBuffer str2 = new StringBuffer(str);
str2 = str2.reverse();
if(str.equals(str2.toString())){
System.out.println("迴文");
}

71:50個人圍城一圈數到3和3的倍數時出圈,問剩下的人是誰,在原來的位置是多少?
public static void main(String []args) {
List<Integer>list = new LinkedList<Integer>();
for(int i=1;i<=50;i++){//模擬50人
list.add(i);
}
int index=-1;//模擬當前數的數字
while(list.size()>1){//多於一個人
//取餘 如果數到最後一個人,迴圈
index = (index+3)%list.size();//
//因為刪除一個元素後,後邊元素自動前移,所以索引減一
list.remove(index–);
}
System.out.println(list.get(0));
}