java解惑——易錯知識點歸納總結 – JAVA編程語言程序開發技術文章

純粹個人觀點,如果有誤,歡迎指正!

一. Switch
1.其能接受的數據類型有四個,char , byte, short, int
2.Default 可放在switch中的任何一個地方,但隻有給定的條件匹配不到時,才會執行
3.Case,default語句如果執行完要跳出,必須用break, 沒的話會向下繼續執行(如果碰到case語句則直接進入執行)
實例1:
[java]
int i = 1, j = 0; 
switch (i) { 
case 2: 
    j += 6; 
case 4: 
    j += 1; 
default: 
    j += 2; 
case 0: 
    j += 4; 
}  

        int i = 1, j = 0;
        switch (i) {
        case 2:
            j += 6;
        case 4:
            j += 1;
        default:
            j += 2;
        case 0:
            j += 4;
        } What is the value of j ?
A.0
B.1
C.2
D.4

E.6

 

 

What is the acceptable type for the variable i?
A.byte
B.long
C.float
D.double
E.object
F.A and B
G.C and D 

二. String 和 StringBuffer
String 定義的是字符串常量,其値一旦定義就不再改變,如下:
String s = “ABC”;
S = s.subString(2); //會重新生成一個字符串對象
以上兩句執行後在內存中會產生“兩”個字符串對象 一個”ABC”,另一個是s指向的”AB”(註意s已不再指向”ABC”)
StringBuffer 定義的是字符串變量,其値可以改變,如下:
StringBuffer s1 = new StringBuffer(“ABC”);
S1 = s1.subString(2);
以上兩句執行後在內存中隻產生“一個”字符串對象: s指向的”AB”;

三. String s = new String(“XYZ”) 產生瞭幾個對象
  該語句會產生2個字符串對象:
一個是通過 ” ” 方式在 編譯期 產生,存放在常量池中
一個是通過new方式在 運行期 產生,存放在堆內存中
但在運行時隻會通過new方式產生一個對象

四. java中的參數隻能“按値”傳遞,且傳遞的是値的 copy
  如是基本類型,則傳遞的是基本類型的副本
 如是引用類型,則傳遞的是引用本身的副本

五. 方法重載和覆蓋的條件
符合重載的條件:
1.在同一個類中
2.有多個同名的方法,
3.方法參數不同(參數的個數不同 或則 參數的類型不同)
實例:
[java]
public class MethodOver { 
     public void setVar (int a, int b, float c) { 
     } 
}  

public class MethodOver {
     public void setVar (int a, int b, float c) {
     }
} Which two overload the setVar method? (Choose Two)
A.private void setVar (int a, float c, int b) { }
B.protected void setVar (int a, int b, float c) { }
C.public int setVar (int a, float c, int b) (return a;)
D.public int setVar (int a, int b, float c) (return a;)
E.protected float setVar (int a, int b, float c) (return c;)

符合覆蓋的條件:
1.在繼承中
2.子類中的方法名和父類相同
3.子類中的方法參數和父類相同
4.子類中的方法返回類型和父類一樣
5.子類的方法不能比父類拋出更多的異常
6.子類的方法訪問范圍大於或等於父類
覆蓋值得註意的是如果子類中有一個方法名稱和父類一樣,但參數不同,那不叫覆蓋,所以也就不受覆蓋的條件限制(註意該方法可以存在)

 

六. java類中的變量初始化相關的知識
6-1.初始化順序分三步:
1. 類加載時,初始化靜態變量和靜態區塊,先父類後子類
2. 運行中當new出一個對象時,開始為對象分配空間並初始化實例變量,先父類後子類
3. 調用構造函數時,先執行父類的構造函數,再執行子類的構造函數,具體過程是調用子類的構造函數時,在第一行處會調用父類的構造函數(顯式或隱式)

6-2. 初始化時各類型的變量初始化的値:
引用類型: null
基本類型:
[java]
boolean : false 
char:\u0000 
byte: 0 
short: 0 
int: 0 
long: 0 
float: 0.0 
double: 0.0  

  boolean : false
  char:\u0000
  byte: 0
  short: 0
  int: 0
  long: 0
  float: 0.0
  double: 0.0 6-3. 數組的初始化
  當我們產生某個存儲對象的數組時,真正產生的其實是個存儲references的數組。此數組建立之後,其中的每一個reference皆會被自動設為某個特殊值。該值以關鍵字null表示。當Java看到null值,便將這個reference視為“不指向任何對象”。使用任何reference之前,你必須先將某個對象指派給它。如果你使用某個reference而其值為null,便會在執行期發生錯誤
  數組在分配空間時就開始瞭初始化,初始化規則,基本類型按照6-2的規則進行初始化,引用類型類型全部初始化為null

 6-4. java中的所有的實例變量都有系統默認初始化,所有的方法變量由方法本身進行初始化,且方法中的變量一定要初始化後才能應用

 

七. java中的構造函數
1. 構造函數不能被繼承
2. 每一個類都至少有一個構造函數,自己不定義,編譯器也會給分配一個默認的不帶參數的構造函數
3. 子類的構造函數一定會調用父類的構造函數,通過super()調用,或顯式或隱式,顯式調用的父類構造函數必須存在; 如果沒有顯式調用則編譯器會自動在子類的構造函數第一行處加上super()這個隱式調用,這時要求父類一定要有不帶參數的構造函數存在(如果父類自己定義瞭構造函數,但帶有參數,編譯時會報錯)
例子:
[java]
class super1{ 
    public int I = 0; 
    public super1 (String text){ 
        I = 1; 
    } 

public class sub1 extends super1{ 
    public sub1(String text){ 
    // super(text);  
    I= 2; 
    //隱式超級構造super1()是未定義的。必須明確援引另一個構造  

public static void main (String args[]){ 
    sub1 sub2 = new sub1("Hello"); 
        System.out.println(sub2.I); 
    } 

class super1{
    public int I = 0;
    public super1 (String text){
        I = 1;
    }
}
public class sub1 extends super1{
    public sub1(String text){
    // super(text);
    I= 2;
    //隱式超級構造super1()是未定義的。必須明確援引另一個構造
}
public static void main (String args[]){
    sub1 sub2 = new sub1("Hello");
        System.out.println(sub2.I);
    }
}

八. java中的異常處理
1. java中的異常分運行時異常 和 非運行時異常, 運行時異常由運行時系統捕獲並處理(編譯正常),非運行時異常必須由處理(拋出或捕獲)

2. 異常機制中try{}後一定要跟catch嗎?
* 不一定,,但必須跟finally.也就是catch和finally必須跟其中一個
* 異常機制中try{}後一定要跟catch嗎?
* 不一定,,但必須跟finally.也就是catch和finally必須跟其中一個
* try {
* }finally {}
* 這樣沒問題,而且,可不是沒有意義哦,因為這樣可以保證即使發生瞭異常,finally裡面的代碼一定會被執行。
* 有時候,這個還是非常有用的。
* 比如可以用來釋放一些自己占用的資源,然後讓調用者處理異常。
3. 異常中的finally一定會執行,哪怕一個方法中有return語句,也是在異常處理後才返回
4. 異常的拋出可以先子類再父類,如果子類捕獲瞭,則父類就不再捕獲;
但是不能先父類再子類,那樣會導致編譯出錯
5. 異常處理後,程序繼續執行
實例:
[java]
/*
* 非運行時異常一旦拋出,要麼用catch塊捕獲處理,要麼聲明拋出
*/ 
import java.io.IOException; 
public class ExceptionTest { 
    // public static void methodA(){  
    public static void methodA() throws IOException { 
        // throw new NullPointerException();  
        // try{  
        throw new IOException(); 
        // System.out.println("method exit");  
        // }catch(IOException e){}  
        // finally{}  
    } 
 
    public static void main(String[] args) { 
        try { 
            methodA(); 
            // throw new IOException();  
        } catch (IOException e) { 
            System.out.println("Caught1 IOException "); 
        } catch (NullPointerException e) { 
            System.out.println("Caught1 NullPointerException"); 
        } catch (Exception e) { 
            System.out.println("Caught Exception"); 
        } 
 
        System.out.println("main exit"); 
    } 

/*
* 非運行時異常一旦拋出,要麼用catch塊捕獲處理,要麼聲明拋出
*/
import java.io.IOException;
public class ExceptionTest {
    // public static void methodA(){
    public static void methodA() throws IOException {
        // throw new NullPointerException();
        // try{
        throw new IOException();
        // System.out.println("method exit");
        // }catch(IOException e){}
        // finally{}
    }

    public static void main(String[] args) {
        try {
            methodA();
            // throw new IOException();
        } catch (IOException e) {
            System.out.println("Caught1 IOException ");
        } catch (NullPointerException e) {
            System.out.println("Caught1 NullPointerException");
        } catch (Exception e) {
            System.out.println("Caught Exception");
        }

        System.out.println("main exit");
    }
}

九. 按位運算和邏輯運算
  按位運算操作符(& ,| )兩邊的都要計算
邏輯運算如果操作符(&&, || )左邊成立則就不在計算右邊瞭

實例:
[java] view plaincopyprint?public class test { 
    private static int j = 0; 
 
    private static boolean methodB(int k) { 
        j += k; 
        return true; 
    } 
 
    public static void methodA(int i) { 
        boolean b; 
        b = i < 10 | methodB(4); 
        b = i < 10 || methodB(8); 
    } 
 
    public static void main(String args[]) { 
        methodA(0); 
        System.out.println(j); 
    } 

public class test {
    private static int j = 0;

    private static boolean methodB(int k) {
        j += k;
        return true;
    }

    public static void methodA(int i) {
        boolean b;
        b = i < 10 | methodB(4);
        b = i < 10 || methodB(8);
    }

    public static void main(String args[]) {
        methodA(0);
        System.out.println(j);
    }
}What is the result?
A.The program prints “0”
B.The program prints “4”
C.The program prints “8”
D.The program prints “12”
E.The code does not complete

 

十. for(;;)意義
相當於while(true), 不知道java為什麼要搞出這個古怪讓人費解的東西?

 

十一. equals, = =
  equals比較兩個對象的內容是否相等
  = = 比較的是兩個引用是否指向同一對象 
  String的存儲特性會對以上的判定規則產生影響(實質上規則不變,表面上改變):
  String 通過“”方式生成的對象會存儲在常量池中,常量池有一個重要的特點就是共享,比如String s = “X”; 在把”X”放常量池之前jvm會檢測常量池中是否存在和“X"相同的對象,如果已經存在則直接把引用指向已存在的對象,不再為”X”分配空間,好處是節約瞭空間 

何時需要重寫equals()

  當一個類有自己特有的“邏輯相等”概念(不同於對象身份的概念)。

  2.設計equals()

      [1]使用instanceof操作符檢查“實參是否為正確的類型”。

      [2]對於類中的每一個“關鍵域”,檢查實參中的域與當前對象中對應的域值。

          [2.1]對於非float和double類型的原語類型域,使用==比較;

          [2.2]對於對象引用域,遞歸調用equals方法;

          [2.3]對於float域,使用Float.floatToIntBits(afloat)轉換為int,再使用==比較;

          [2.4]對於double域,使用Double.doubleToLongBits(adouble) 轉換為int,再使用==比較;

          [2.5]對於數組域,調用Arrays.equals方法。

  3.當改寫equals()的時候,總是要改寫hashCode()

  根據一個類的equals方法(改寫後),兩個截然不同的實例有可能在邏輯上是相等的,但是,根據Object.hashCode方法,它們僅僅是兩個對象。因此,違反瞭“相等的對象必須具有相等的散列碼”。

  4.設計hashCode()

      [1]把某個非零常數值,例如17,保存在int變量result中;

      [2]對於對象中每一個關鍵域f(指equals方法中考慮的每一個域):

          [2.1]boolean型,計算(f ? 0 : 1);

          [2.2]byte,char,short型,計算(int);

          [2.3]long型,計算(int) (f ^ (f>>>32));

          [2.4]float型,計算Float.floatToIntBits(afloat);

          [2.5]double型,計算Double.doubleToLongBits(adouble)得到一個long,再執行[2.3];

          [2.6]對象引用,遞歸調用它的hashCode方法;

          [2.7]數組域,對其中每個元素調用它的hashCode方法。

      [3]將上面計算得到的散列碼保存到int變量c,然後執行 result=37*result+c;

      [4]返回result。

 

十二. 基本類型的變量賦初始値
  Byte的范圍為-128~127
當我們給出一個整數,且該整數後不帶l標示,則編譯器自動把它視為int類型,如
Int i = 1 ; 是成立的
當我們給出一個小數,且該小數後不帶f標示,則編譯器自動把它視為double類型,如
Double d = 1.0; 是成立的

十三. 基本類型的轉化
  規則: 小的可以自動轉化為大的, 大的要強制性才能轉為小的,比如以下
Double d = 1.0f; //正確, 小轉大,自動
Float f = 1.0d(或1.0); //錯誤,大轉小,需強制 float f = (float)1.0d;

 

十四. servlet運行機理
  Servlet是java引入的在B/S架構中用來處理動態網頁的一種技術,其實質是一個繼承瞭HttpServlet的java類,由web容器負責解釋運行,其機理如下:
(第一次被請求)
客戶提出請求 -> web容器解析請求,找出請求的url,根據web.xml配置找到對應的servlet -> 加載servlet -> 實例化 -> 調用init初始化 -> 調用service方法 -> 由service方法自動匹配doXXX方法-> web容器關閉/servlet長時間沒有被請求則調用其destroy方法銷毀servlet實例

不確定的地方: servlet多長時間沒有被調用才會銷毀,可以設置嗎? 不同的web服務器應該是不同的吧

十五. servlet 和 jsp 的區別
都是用來處理動態網頁的技術,jsp被編譯後轉化為servlet, 一個jsp頁面本質上也是一個servlet;jsp在第一次被請求後,先轉化為servlet,再編譯,所以第一次要比servlet慢
Servlet是在java代碼中嵌入HTML, 擅長邏輯控制
Jsp是在HTML中嵌入java代碼, 擅長頁面處理

十六. forward(請求轉發)與redirect(重定向)的區別
( Forward是服務器端請求,是servlet提供的一種技術,服務器根據請求的url找到請求的頁面,對瀏覽器而言,這一過程是不透明的,好像什麼也沒發生一樣,瀏覽器的地址欄不會顯示被請求的url頁面地址(顯示的仍是上次請求的服務器端的url地址),從HTTP協議的角度,隻發生一次的請求響應過程
Redirect是客戶端的請求,客戶端根據服務器傳回的地址,重新向服務器發出請求,瀏覽器的地址欄顯示的是新請求的url地址, 從http協議的角度,發生瞭兩次請求響應的過程 )
——————-
1. 重定向是HTTP協議定義的功能,要經過兩次HTTP通信過程,第一次用於獲取資源的實際地址,第二次用之前得到的地址發出請求, 這個過程對瀏覽器是可見的;
  請求轉發是servlet技術本身的特點,轉發的過程是在服務器內部進行,對瀏覽器是不透明的,它認為它所發送的地址實際上得到的就是這些內容; 從HTTP角度看,隻有一次通訊過程

 2. 重定向隻能轉向新的資源,功能較單一; 請求轉發不但可以轉向新的資源,也可將其它資源和本身的生成的內容結合起來,功能很豐富 

十七. 線程

  16-1.線程的同步
  1.同步的概念: 當多個線程同時使用一個對象時,由於線程本身運行的不確定性,可能會造成操作的不完整性,故而引入同步
  2.Java中同步的方式有兩種, Synchronized 和 Lock
  3.當一個線程進入一個對象的同步方法後,它會把該對象鎖住,其它的線程不能再使用該對象(包括對象的任何方法,屬性),直到該線程釋放掉鎖,其它線程才有機會使用該對象
  4.一個線程釋放同步鎖的條件:
a. 正常運行完(退出synchronized塊)
b. 使用wait()方法
  5.同步中的方法: wait(), notify()/notifyAll(),用於同步中的線程通訊
  Wait(): 釋放持有的同步鎖,本身進入鎖等待狀態,在線程中因為多個線程“同時“運作,可能導致運作的條件不滿足,當條件不滿足時,線程本身就需要進入等待狀態(釋放掉鎖),等其它的線程改變瞭條件,它才能有機會繼續執行
  NotifyAll(): 喚醒鎖等待的線程,當一個持有線程鎖的對象調用該方法後,其它處於鎖等待的線程雖然被喚醒,但其本身不會立刻釋放掉鎖,需要等運行結束後(退出synchronized塊)才釋放掉,其它線程才有機會執行
  16-2.線程中的方法
  6.sleep() ,當前線程休眠一個設定的時間,時間到後進入線程就緒隊列,等待執行
  7.join(),該方法使調用該方法的線程在此之前執行完畢,也就是等待調用該方法的線程執行完畢後再往下繼續執行。註意該方法也要捕獲異常。
實例:
[java]
class A implements runable { 
    int i; 
    public void run() { 
        try { 
            thread.sleep(5000); 
            i = 10; 
        } catch (InterruptedException e) { 
        } 
    } 

 
public class Test { 
    public static void main(string args[]) { 
        try { 
            A a = new A(); 
            Thread t = new Thread(a); 
            t.start(); 
 
            int j = a.i; 
 
        } catch (Exception e) { 
        } 
    } 

class A implements runable {
    int i;
    public void run() {
        try {
            thread.sleep(5000);
            i = 10;
        } catch (InterruptedException e) {
        }
    }
}

public class Test {
    public static void main(string args[]) {
        try {
            A a = new A();
            Thread t = new Thread(a);
            t.start();

            int j = a.i;

        } catch (Exception e) {
        }
    }
}Which statement al line 17 will ensure that j=10 at line 19?
A.a.wait();
B.t.wait();
C.t.join();
D.t.yield();
E.t.notify();
F.a.notify();

G.t.interrupt();

 

  8.yield(),與sleep()類似,隻是不能由用戶指定暫停多長時間,所以一個線程線程執行yield()方法後,也可能立刻執行(jvm還是分配給它執行),yield()方法隻能讓同優先級的線程有執行的機會。

舉幾個例子:
[java]
//例1 線程同步  
class SyncStack{ //同步堆棧類  
   private int index = 0; //堆棧指針初始值為0  
   private char []buffer = new char[6]; //堆棧有6個字符的空間  
   public synchronized void push(char c){ //加上互斥鎖  
     while(index = = buffer.length){ //堆棧已滿,不能壓棧  
     try{ 
        this.wait(); //等待,直到有數據出棧  
       }catch(InterruptedException e){} 
    } 
    this.notify(); //通知其它線程把數據出棧  
    buffer[index] = c; //數據入棧  
    index++; //指針向上移動  
   } 
   public synchronized char pop(){ //加上互斥鎖  
       while(index ==0){ //堆棧無數據,不能出棧  
        try{ 
           this.wait(); //等待其它線程把數據入棧  
        }catch(InterruptedException e){} 
    } 
       this.notify(); //通知其它線程入棧  
       index- -; //指針向下移動  
       return buffer[index]; //數據出棧  
    } 
 } 
    class Producer implements Runnable{ //生產者類  
       SyncStack theStack; 
        //生產者類生成的字母都保存到同步堆棧中  
 
       public Producer(SyncStack s){ 
          theStack = s; 
       } 
       public void run(){ 
          char c; 
          for(int i=0; i<20; i++){ 
            c =(char)(Math.random()*26+'A');  //隨機產生20個字符  
            theStack.push(c); //把字符入棧  
            System.out.println("Produced: "+c); //打印字符  
            try{ 
             Thread.sleep((int)(Math.random()*1000)); /*每產生一個字符線程就睡眠*/ 
            }catch(InterruptedException e){} 
          } 
       } 
     } 
     class Consumer implements Runnable{ //消費者類  
         SyncStack theStack;  //消費者類獲得的字符都來自同步堆棧  
         public Consumer(SyncStack s){ 
             theStack = s; 
         } 
         public void run(){ 
             char c; 
             for(int i=0;i<20;i++){ 
               c = theStack.pop(); //從堆棧中讀取字符  
             System.out.println("Consumed: "+c);  //打印字符  
    try{ 
              Thread.sleep((int)(Math.random()*1000)); 
             }catch(InterruptedException e){} 
         } 
       } 
     } 
     public class SyncTest{ 
       public static void main(String args[]){ 
         SyncStack stack = new SyncStack(); 
   //下面的消費者類對象和生產者類對象所操作的是同一個同步堆棧對象  
         Runnable source=new Producer(stack); 
         Runnable sink = new Consumer(stack); 
         Thread t1 = new Thread(source); //線程實例化  
         Thread t2 = new Thread(sink); //線程實例化  
         t1.start(); //線程啟動  
         t2.start(); //線程啟動  
       } 
     } 
//例2.下面的代碼在絕大部分時間內都運行得很正常,請問在什麼情況下會出現問題?問題的根源在哪裡?  
import java.util.LinkedList; 
public class Stack { 
    LinkedList list = new LinkedList(); 
 
    public synchronized void push(Object x) { 
        synchronized (list) { 
            list.addLast(x); 
            notify(); 
        } 
    } 
 
    public synchronized Object pop() throws Exception { 
        synchronized (list) { 
            if (list.size() <= 0) { 
                wait(); 
            } 
            return list.removeLast(); 
        } 
    } 

//例1 線程同步
class SyncStack{ //同步堆棧類
   private int index = 0; //堆棧指針初始值為0
   private char []buffer = new char[6]; //堆棧有6個字符的空間
   public synchronized void push(char c){ //加上互斥鎖
     while(index = = buffer.length){ //堆棧已滿,不能壓棧
     try{
        this.wait(); //等待,直到有數據出棧
       }catch(InterruptedException e){}
    }
    this.notify(); //通知其它線程把數據出棧
    buffer[index] = c; //數據入棧
    index++; //指針向上移動
   }
   public synchronized char pop(){ //加上互斥鎖
       while(index ==0){ //堆棧無數據,不能出棧
        try{
           this.wait(); //等待其它線程把數據入棧
        }catch(InterruptedException e){}
    }
       this.notify(); //通知其它線程入棧
       index- -; //指針向下移動
       return buffer[index]; //數據出棧
    }
 }
    class Producer implements Runnable{ //生產者類
       SyncStack theStack;
        //生產者類生成的字母都保存到同步堆棧中

       public Producer(SyncStack s){
          theStack = s;
       }
       public void run(){
          char c;
          for(int i=0; i<20; i++){
            c =(char)(Math.random()*26+'A');  //隨機產生20個字符
            theStack.push(c); //把字符入棧
            System.out.println("Produced: "+c); //打印字符
            try{
             Thread.sleep((int)(Math.random()*1000)); /*每產生一個字符線程就睡眠*/
            }catch(InterruptedException e){}
          }
       }
     }
     class Consumer implements Runnable{ //消費者類
         SyncStack theStack;  //消費者類獲得的字符都來自同步堆棧
         public Consumer(SyncStack s){
             theStack = s;
         }
         public void run(){
             char c;
             for(int i=0;i<20;i++){
               c = theStack.pop(); //從堆棧中讀取字符
             System.out.println("Consumed: "+c);  //打印字符
    try{
              Thread.sleep((int)(Math.random()*1000));
             }catch(InterruptedException e){}
         }
       }
     }
     public class SyncTest{
       public static void main(String args[]){
         SyncStack stack = new SyncStack();
   //下面的消費者類對象和生產者類對象所操作的是同一個同步堆棧對象
         Runnable source=new Producer(stack);
         Runnable sink = new Consumer(stack);
         Thread t1 = new Thread(source); //線程實例化
         Thread t2 = new Thread(sink); //線程實例化
         t1.start(); //線程啟動
         t2.start(); //線程啟動
       }
     }
//例2.下面的代碼在絕大部分時間內都運行得很正常,請問在什麼情況下會出現問題?問題的根源在哪裡?
import java.util.LinkedList;
public class Stack {
    LinkedList list = new LinkedList();

    public synchronized void push(Object x) {
        synchronized (list) {
            list.addLast(x);
            notify();
        }
    }

    public synchronized Object pop() throws Exception {
        synchronized (list) {
            if (list.size() <= 0) {
                wait();
            }
            return list.removeLast();
        }
    }
}
對例2的分析【網友】:
當一個線程執行下面方法:
[java] view plaincopyprint?public synchronized void push(Object x) { 
  synchronized(list) {   
    list.addLast( x ); 
    notify(); 
  } 
}  

public synchronized void push(Object x) {
  synchronized(list) { 
    list.addLast( x );
    notify();
  }
}
這個時候他獲得2個鎖,一個是Stack對象的鎖,還有list對象的鎖,而notify,釋放的是stack對象的鎖,沒有釋放list對象的鎖,所以隻要當pop方法中檢測到list的大小為0,則執行pop的線程會一直控制list的鎖,使得push沒法執行。 之所以大部分時間程序運行成功,是因為push總比pop快,list沒有為0.

[java]
//例3.可能死鎖的線程  
public class SyncTest { 
    public static void main(String[] args) { 
        final StringBuffer s1 = new StringBuffer(); 
        final StringBuffer s2 = new StringBuffer(); 
        new Thread() { 
            public void run() { 
                synchronized (s1) { 
                    s2.append("A"); 
                    synchronized (s2) { 
                        s2.append("B"); 
                        System.out.print(s1); 
                        System.out.print(s2); 
                    } 
                } 
            } 
        }.start(); 
 
        new Thread() { 
            public void run() { 
                synchronized (s2) { 
                    s2.append("C"); 
                    synchronized (s1) { 
                        s1.append("D"); 
                        System.out.print(s2); 
                        System.out.print(s1); 
                    } 
                } 
            } 
        }.start(); 
    } 

//例3.可能死鎖的線程
public class SyncTest {
    public static void main(String[] args) {
        final StringBuffer s1 = new StringBuffer();
        final StringBuffer s2 = new StringBuffer();
        new Thread() {
            public void run() {
                synchronized (s1) {
                    s2.append("A");
                    synchronized (s2) {
                        s2.append("B");
                        System.out.print(s1);
                        System.out.print(s2);
                    }
                }
            }
        }.start();

        new Thread() {
            public void run() {
                synchronized (s2) {
                    s2.append("C");
                    synchronized (s1) {
                        s1.append("D");
                        System.out.print(s2);
                        System.out.print(s1);
                    }
                }
            }
        }.start();
    }
}
對例3的分析【網友】
如果沒有出現死鎖,那麼輸出的結果必然是:"ABABCD"; 如果沒有輸出此結果;那麼死鎖
原因:T1啟動在前, T2啟動在後;且T1的第一步操作為:synchronized(s1)
  1. 由於T1執行過慢—> T2要執行第一步:synchronized(s2)—>尋找s1,被T1鎖住等待—>T1尋找說說s2,被T2鎖住等待 ; 出現死鎖
  2. T1執行過快–>s1,s2都被鎖住—>T2執行,等待–>T1執行完:"AB" –>T2執行:"ABCD"

十八.web服務器
  Web服務器說白瞭就是提供web應用的基礎功能:
  1. 它是遵從http協議的一個服務器端程序,按照http提供基本的請求解析、應答處理等
  2. 它提供瞭供web程序運行的最直接的環境,比如tomcat就是一個servlet的容器
  3. 它提供瞭對線程的管理,包括創建,調度,撤銷等
  4. 它提供請求地址與具體地址的對應處理
  。。。
  B/S是在C/S架構基礎上發展起來的一種技術,相比C/S,B/S主要有以下幾點的不同,這裡不比較哪個優越,況且優越與否是和具體環境關聯的,單獨不能說哪個好,
  1. B/S是遵從http協議的,即采用的是標準的協議,
  2. B/S的客戶端已開發好(就遵從http協議的瀏覽器),不需要程序員再開發
  3. B/S的服務器端業界也提供瞭基礎功能的實現(各種web容器)

  一個C/S示例(多線程),希望在它的基礎上能更好的理解web服務器
  1. 客戶端程序:MultiTalkClient.java
[java]
  import java.io.*; 
  import java.net.*; 
  public class MultiTalkClient { 
   public static void main(String args[]) { 
    try{ 
      Socket socket=new Socket("127.0.0.1",4700); 
      //向本機的4700端口發出客戶請求  
      BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); 
      //由系統標準輸入設備構造BufferedReader對象  
      PrintWriter os=new PrintWriter(socket.getOutputStream()); 
      //由Socket對象得到輸出流,並構造PrintWriter對象  
      BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); 
      //由Socket對象得到輸入流,並構造相應的BufferedReader對象  
      String readline; 
      readline=sin.readLine(); //從系統標準輸入讀入一字符串  
      while(!readline.equals("bye")){ 
      //若從標準輸入讀入的字符串為 "bye"則停止循環  
        os.println(readline); 
        //將從系統標準輸入讀入的字符串輸出到Server  
        os.flush(); 
        //刷新輸出流,使Server馬上收到該字符串  
        System.out.println("Client:"+readline); 
        //在系統標準輸出上打印讀入的字符串  
        System.out.println("Server:"+is.readLine()); 
        //從Server讀入一字符串,並打印到標準輸出上  
        readline=sin.readLine(); 
        //從系統標準輸入讀入一字符串  
      } //繼續循環  
      os.close(); //關閉Socket輸出流  
      is.close(); //關閉Socket輸入流  
      socket.close(); //關閉Socket  
    }catch(Exception e) { 
      System.out.println("Error"+e); //出錯,則打印出錯信息  
    } 
  } 

  import java.io.*;
  import java.net.*;
  public class MultiTalkClient {
   public static void main(String args[]) {
    try{
      Socket socket=new Socket("127.0.0.1",4700);
      //向本機的4700端口發出客戶請求
      BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
      //由系統標準輸入設備構造BufferedReader對象
      PrintWriter os=new PrintWriter(socket.getOutputStream());
      //由Socket對象得到輸出流,並構造PrintWriter對象
      BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
      //由Socket對象得到輸入流,並構造相應的BufferedReader對象
      String readline;
      readline=sin.readLine(); //從系統標準輸入讀入一字符串
      while(!readline.equals("bye")){
      //若從標準輸入讀入的字符串為 "bye"則停止循環
        os.println(readline);
        //將從系統標準輸入讀入的字符串輸出到Server
        os.flush();
        //刷新輸出流,使Server馬上收到該字符串
        System.out.println("Client:"+readline);
        //在系統標準輸出上打印讀入的字符串
        System.out.println("Server:"+is.readLine());
        //從Server讀入一字符串,並打印到標準輸出上
        readline=sin.readLine();
        //從系統標準輸入讀入一字符串
      } //繼續循環
      os.close(); //關閉Socket輸出流
      is.close(); //關閉Socket輸入流
      socket.close(); //關閉Socket
    }catch(Exception e) {
      System.out.println("Error"+e); //出錯,則打印出錯信息
    }
  }
}
 2. 服務器端程序: MultiTalkServer.java

[java]
 import java.io.*; 
  import java.net.*; 
  import ServerThread; 
  public class MultiTalkServer{ 
   static int clientnum=0; //靜態成員變量,記錄當前客戶的個數  
   public static void main(String args[]) throws IOException { 
    ServerSocket serverSocket=null; 
    boolean listening=true; 
    try{ 
      serverSocket=new ServerSocket(4700); 
      //創建一個ServerSocket在端口4700監聽客戶請求  
    }catch(IOException e) { 
      System.out.println("Could not listen on port:4700."); 
      //出錯,打印出錯信息  
      System.exit(-1); //退出  
    } 
    while(listening){ //永遠循環監聽  
      new ServerThread(serverSocket.accept(),clientnum).start(); 
      //監聽到客戶請求,根據得到的Socket對象和  
       客戶計數創建服務線程,並啟動之 
      clientnum++; //增加客戶計數  
    } 
    serverSocket.close(); //關閉ServerSocket  
  } 

  import java.io.*;
  import java.net.*;
  import ServerThread;
  public class MultiTalkServer{
   static int clientnum=0; //靜態成員變量,記錄當前客戶的個數
   public static void main(String args[]) throws IOException {
    ServerSocket serverSocket=null;
    boolean listening=true;
    try{
      serverSocket=new ServerSocket(4700);
      //創建一個ServerSocket在端口4700監聽客戶請求
    }catch(IOException e) {
      System.out.println("Could not listen on port:4700.");
      //出錯,打印出錯信息
      System.exit(-1); //退出
    }
    while(listening){ //永遠循環監聽
      new ServerThread(serverSocket.accept(),clientnum).start();
      //監聽到客戶請求,根據得到的Socket對象和
       客戶計數創建服務線程,並啟動之
      clientnum++; //增加客戶計數
    }
    serverSocket.close(); //關閉ServerSocket
  }
}
 3. 程序ServerThread.java

[java]
 import java.io.*; 
  import java.net.*; 
  public class ServerThread extends Thread{ 
   Socket socket=null; //保存與本線程相關的Socket對象  
   int clientnum; //保存本進程的客戶計數  
   public ServerThread(Socket socket,int num) { //構造函數  
    this.socket=socket; //初始化socket變量  
    clientnum=num+1; //初始化clientnum變量  
   } 
   public void run() { //線程主體  
    try{ 
      String line; 
      BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream())); 
  //由Socket對象得到輸入流,並構造相應的BufferedReader對象  
      PrintWriter os=newPrintWriter(socket.getOutputStream()); 
      //由Socket對象得到輸出流,並構造PrintWriter對象  
      BufferedReader sin=new BufferedReader(new InputStreamReader(System.in)); 
      //由系統標準輸入設備構造BufferedReader對象  
      System.out.println("Client:"+ clientnum +is.readLine()); 
      //在標準輸出上打印從客戶端讀入的字符串  
      line=sin.readLine(); 
      //從標準輸入讀入一字符串  
      while(!line.equals("bye")){ 
      //如果該字符串為 "bye",則停止循環  
        os.println(line); 
        //向客戶端輸出該字符串  
        os.flush(); 
        //刷新輸出流,使Client馬上收到該字符串  
        System.out.println("Server:"+line); 
        //在系統標準輸出上打印該字符串  
        System.out.println("Client:"+ clientnum +is.readLine()); 
        //從Client讀入一字符串,並打印到標準輸出上  
        line=sin.readLine(); 
        //從系統標準輸入讀入一字符串  
      } //繼續循環  
      os.close(); //關閉Socket輸出流  
      is.close(); //關閉Socket輸入流  
      socket.close(); //關閉Socket  
      server.close(); //關閉ServerSocket  
     }catch(Exception e){ 
      System.out.println("Error:"+e); 
      //出錯,打印出錯信息  
     } 
   } 
 } 

  import java.io.*;
  import java.net.*;
  public class ServerThread extends Thread{
   Socket socket=null; //保存與本線程相關的Socket對象
   int clientnum; //保存本進程的客戶計數
   public ServerThread(Socket socket,int num) { //構造函數
    this.socket=socket; //初始化socket變量
    clientnum=num+1; //初始化clientnum變量
   }
   public void run() { //線程主體
    try{
      String line;
      BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
  //由Socket對象得到輸入流,並構造相應的BufferedReader對象
      PrintWriter os=newPrintWriter(socket.getOutputStream());
      //由Socket對象得到輸出流,並構造PrintWriter對象
      BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
      //由系統標準輸入設備構造BufferedReader對象
      System.out.println("Client:"+ clientnum +is.readLine());
      //在標準輸出上打印從客戶端讀入的字符串
      line=sin.readLine();
      //從標準輸入讀入一字符串
      while(!line.equals("bye")){
      //如果該字符串為 "bye",則停止循環
        os.println(line);
        //向客戶端輸出該字符串
        os.flush();
        //刷新輸出流,使Client馬上收到該字符串
        System.out.println("Server:"+line);
        //在系統標準輸出上打印該字符串
        System.out.println("Client:"+ clientnum +is.readLine());
        //從Client讀入一字符串,並打印到標準輸出上
        line=sin.readLine();
        //從系統標準輸入讀入一字符串
      } //繼續循環
      os.close(); //關閉Socket輸出流
      is.close(); //關閉Socket輸入流
      socket.close(); //關閉Socket
      server.close(); //關閉ServerSocket
     }catch(Exception e){
      System.out.println("Error:"+e);
      //出錯,打印出錯信息
     }
   }
 }

十九.資源池的理解
  1.資源池引入的目的(好處)
  提高性能
  2.資源池運作機制
  由資源池管理器提供一定數目的目標資源,當有請求該資源時,資源池分配給一個,然後給該資源標識為忙,標示為忙的資源不能再被分配使用,當某一個資源使用完後,資源池把相關的資源的忙標示清除掉,以示該資源可以再被下一個請求使用
  3.資源池常有的參數
  1.初始資源的數目:資源池啟動時,一次建立的資源數目,資源池最少要保證在這個數目上
  2.最大資源的數目:當請求的資源超出這個數目,就等待
  4.常見的資源池
  1.數據庫連接池
  2.web容器中的request,response對象池
  3.web容器中的線程池

發佈留言