posts - 51, comments - 17, trackbacks - 0, articles - 9
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          synchronized的一個簡單例子

          public class TextThread
          {

           /**
            * @param args
            */
           public static void main(String[] args)
           {
            // TODO 自動生成方法存根
                  TxtThread tt = new TxtThread();
                  new Thread(tt).start();
                  new Thread(tt).start();
                  new Thread(tt).start();
                  new Thread(tt).start();
           }

          }
          class TxtThread implements Runnable
          {
           int num = 100;
           String str = new String();
           public void run()
           {
            while (true)
            {
             synchronized(str)
             {
             if (num>0)
             {
              try
              {
               Thread.sleep(10);
              }
              catch(Exception e)
              {
               e.getMessage();
              }
              System.out.println(Thread.currentThread().getName()+ "this is "+ num--);
             }
             }
            }
           }
          }

          上面的例子中為了制造一個時間差,也就是出錯的機會,使用了Thread.sleep(10)


          Java對多線程的支持與同步機制深受大家的喜愛,似乎看起來使用了synchronized關鍵字就可以輕松地解決多線程共享數據同步問題。到底如何?――還得對synchronized關鍵字的作用進行深入了解才可定論。

          總的說來,synchronized關鍵字可以作為函數的修飾符,也可作為函數內的語句,也就是平時說的同步方法和同步語句塊。如果再細的分類,synchronized可作用于instance變量、object reference(對象引用)、static函數和class literals(類名稱字面常量)身上。

          在進一步闡述之前,我們需要明確幾點:

          A.無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其他線程的對象訪問。

          B.每個對象只有一個鎖(lock)與之相關聯。

          C.實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。

          接著來討論synchronized用到不同地方對代碼產生的影響:

           

          假設P1、P2是同一個類的不同對象,這個類中定義了以下幾種情況的同步塊或同步方法,P1、P2就都可以調用它們。

           

          1.  把synchronized當作函數修飾符時,示例代碼如下:

          Public synchronized void methodAAA()

          {

          //….

          }

          這也就是同步方法,那這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不同的線程中執行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的Class所產生的另一對象P2卻可以任意調用這個被加了synchronized關鍵字的方法。

          上邊的示例代碼等同于如下代碼:

          public void methodAAA()

          {

          synchronized (this)      //  (1)

          {

                 //…..

          }

          }

           (1)處的this指的是什么呢?它指的就是調用這個方法的對象,如P1。可見同步方法實質是將synchronized作用于object reference。――那個拿到了P1對象鎖的線程,才可以調用P1的同步方法,而對P2而言,P1這個鎖與它毫不相干,程序也可能在這種情形下擺脫同步機制的控制,造成數據混亂:(

          2.同步塊,示例代碼如下:

                      public void method3(SomeObject so)

                        {

                               synchronized(so)

          {

                 //…..

          }

          }

          這時,鎖就是so這個對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的instance變量(它得是一個對象)來充當鎖:

          class Foo implements Runnable

          {

                 private byte[] lock = new byte[0];  // 特殊的instance變量

              Public void methodA()

          {

                 synchronized(lock) { //… }

          }

          //…..

          }

          注:零長度的byte數組對象創建起來將比任何對象都經濟――查看編譯后的字節碼:生成零長度的byte[]對象只需3條操作碼,而Object lock = new Object()則需要7行操作碼。

          3.將synchronized作用于static 函數,示例代碼如下:

                Class Foo

          {

          public synchronized static void methodAAA()   // 同步的static 函數

          {

          //….

          }

          public void methodBBB()

          {

                 synchronized(Foo.class)   //  class literal(類名稱字面常量)

          }

                 }

             代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數產生的效果是一樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而不再是由這個Class產生的某個具體對象了)。

          記得在《Effective Java》一書中看到過將 Foo.class和 P1.getClass()用于作同步鎖還不一樣,不能用P1.getClass()來達到鎖這個Class的目的。P1指的是由Foo類產生的對象。

          可以推斷:如果一個類中定義了一個synchronized的static函數A,也定義了一個synchronized 的instance函數B,那么這個類的同一對象Obj在多線程中分別訪問A和B兩個方法時,不會構成同步,因為它們的鎖都不一樣。A方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class。

           

          小結如下:

          搞清楚synchronized鎖定的是哪個對象,就能幫助我們設計更安全的多線程程序。


           

          還有一些技巧可以讓我們對共享資源的同步訪問更加安全:

          1.  定義private 的instance變量+它的 get方法,而不要定義public/protected的instance變量。如果將變量定義為public,對象在外界可以繞過同步方法的控制而直接取得它,并改動它。這也是JavaBean的標準實現方式之一。

          2.  如果instance變量是一個對象,如數組或ArrayList什么的,那上述方法仍然不安全蛭蓖飩綞韻笸ü齡et方法拿到這個instance對象的引用后,又將其指向另一個對象,那么這個private變量也就變了,豈不是很危險。 這個時候就需要將get方法也加上synchronized同步,并且,只返回這個private對象的clone()――這樣,調用端得到的就是對象副本的引用了。

          synchronized的一個簡單例子

          public class TextThread
          {

           /**
            * @param args
            */
           public static void main(String[] args)
           {
            // TODO 自動生成方法存根
                  TxtThread tt = new TxtThread();
                  new Thread(tt).start();
                  new Thread(tt).start();
                  new Thread(tt).start();
                  new Thread(tt).start();
           }

          }
          class TxtThread implements Runnable
          {
           int num = 100;
           String str = new String();
           public void run()
           {
            while (true)
            {
             synchronized(str)
             {
             if (num>0)
             {
              try
              {
               Thread.sleep(10);
              }
              catch(Exception e)
              {
               e.getMessage();
              }
              System.out.println(Thread.currentThread().getName()+ "this is "+ num--);
             }
             }
            }
           }
          }

          上面的例子中為了制造一個時間差,也就是出錯的機會,使用了Thread.sleep(10)


          Java對多線程的支持與同步機制深受大家的喜愛,似乎看起來使用了synchronized關鍵字就可以輕松地解決多線程共享數據同步問題。到底如何?――還得對synchronized關鍵字的作用進行深入了解才可定論。

          總的說來,synchronized關鍵字可以作為函數的修飾符,也可作為函數內的語句,也就是平時說的同步方法和同步語句塊。如果再細的分類,synchronized可作用于instance變量、object reference(對象引用)、static函數和class literals(類名稱字面常量)身上。

          在進一步闡述之前,我們需要明確幾點:

          A.無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其他線程的對象訪問。

          B.每個對象只有一個鎖(lock)與之相關聯。

          C.實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。

          接著來討論synchronized用到不同地方對代碼產生的影響:

           

          假設P1、P2是同一個類的不同對象,這個類中定義了以下幾種情況的同步塊或同步方法,P1、P2就都可以調用它們。

           

          1.  把synchronized當作函數修飾符時,示例代碼如下:

          Public synchronized void methodAAA()

          {

          //….

          }

          這也就是同步方法,那這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不同的線程中執行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的Class所產生的另一對象P2卻可以任意調用這個被加了synchronized關鍵字的方法。

          上邊的示例代碼等同于如下代碼:

          public void methodAAA()

          {

          synchronized (this)      //  (1)

          {

                 //…..

          }

          }

           (1)處的this指的是什么呢?它指的就是調用這個方法的對象,如P1。可見同步方法實質是將synchronized作用于object reference。――那個拿到了P1對象鎖的線程,才可以調用P1的同步方法,而對P2而言,P1這個鎖與它毫不相干,程序也可能在這種情形下擺脫同步機制的控制,造成數據混亂:(

          2.同步塊,示例代碼如下:

                      public void method3(SomeObject so)

                        {

                               synchronized(so)

          {

                 //…..

          }

          }

          這時,鎖就是so這個對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的instance變量(它得是一個對象)來充當鎖:

          class Foo implements Runnable

          {

                 private byte[] lock = new byte[0];  // 特殊的instance變量

              Public void methodA()

          {

                 synchronized(lock) { //… }

          }

          //…..

          }

          注:零長度的byte數組對象創建起來將比任何對象都經濟――查看編譯后的字節碼:生成零長度的byte[]對象只需3條操作碼,而Object lock = new Object()則需要7行操作碼。

          3.將synchronized作用于static 函數,示例代碼如下:

                Class Foo

          {

          public synchronized static void methodAAA()   // 同步的static 函數

          {

          //….

          }

          public void methodBBB()

          {

                 synchronized(Foo.class)   //  class literal(類名稱字面常量)

          }

                 }

             代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數產生的效果是一樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而不再是由這個Class產生的某個具體對象了)。

          記得在《Effective Java》一書中看到過將 Foo.class和 P1.getClass()用于作同步鎖還不一樣,不能用P1.getClass()來達到鎖這個Class的目的。P1指的是由Foo類產生的對象。

          可以推斷:如果一個類中定義了一個synchronized的static函數A,也定義了一個synchronized 的instance函數B,那么這個類的同一對象Obj在多線程中分別訪問A和B兩個方法時,不會構成同步,因為它們的鎖都不一樣。A方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class。

           

          小結如下:

          搞清楚synchronized鎖定的是哪個對象,就能幫助我們設計更安全的多線程程序。


           

          還有一些技巧可以讓我們對共享資源的同步訪問更加安全:

          1.  定義private 的instance變量+它的 get方法,而不要定義public/protected的instance變量。如果將變量定義為public,對象在外界可以繞過同步方法的控制而直接取得它,并改動它。這也是JavaBean的標準實現方式之一。

          2.  如果instance變量是一個對象,如數組或ArrayList什么的,那上述方法仍然不安全蛭蓖飩綞韻笸ü齡et方法拿到這個instance對象的引用后,又將其指向另一個對象,那么這個private變量也就變了,豈不是很危險。 這個時候就需要將get方法也加上synchronized同步,并且,只返回這個private對象的clone()――這樣,調用端得到的就是對象副本的引用了。




          1.
          public class Thread1 implements Runnable{

           //當兩個并發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,
           //一個時間內只能有一個線程得到執行。
           //另一個線程必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊
           public void run(){
            synchronized (this){
             for(int i=0; i<5; i++){
              System.out.println(Thread.currentThread().getName() + " synchronize loop " + i);
             }
            }
           }
           
           public static void main(String[] args) {

            Thread1 thread1 = new Thread1();
            Thread thread2 = new Thread(thread1, "A");
            Thread thread3 = new Thread(thread1, "B");
            thread2.start();
            thread3.start();//thead3必須等待thread2的執行完畢就可立即執行
           }

          }


          2.

          public class Thread2 {

           public void method1(){
            synchronized(this){
             int i = 5;
             while(i-- > 0){
              System.out.println(Thread.currentThread().getName() + " : " + i);
             
              try{
               Thread.sleep(500);
               }
              catch(InterruptedException  ie){
               ie.printStackTrace();
               }
             }
            }
           }
           
           //當一個線程訪問object的一個synchronized(this)同步代碼塊時,
           //另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊
           public void method2(){
            int i = 5;
            while(i-- > 0){
             System.out.println(Thread.currentThread().getName() + " : " + i);
             
             try{
              Thread.sleep(500);
             }catch(InterruptedException ie){
              ie.printStackTrace();
             }
            }
           }
           
           public static void main(String[] args) {

            final Thread2 thread2 = new Thread2();
            Thread thread1 = new Thread(new Runnable(){
             public void run(){
              thread2.method1();
             }
            }, "Thread1");
            
            Thread thread3 = new Thread(new Runnable(){
             public void run(){
              thread2.method2();
             }
            }, "Thread2");
            
            thread1.start();
            thread3.start();//thead3不用等待thread1的執行完畢就可立即執行
           }

          3.

          public class Thread3 {

           class Inner{
            private void method1(){
             int i = 5;
             while(i-- > 0){
              System.out.println(Thread.currentThread().getName() + " : Inner.method1() = " + i);
              try{
               Thread.sleep(500);
              }catch(InterruptedException ie){
               ie.printStackTrace();
              }
             }
            }
            
            //盡管線程thread1獲得了對Inner的對象鎖,但由于線程thread2訪問的是同一個Inner中的非同步部分。
            //所以兩個線程互不干擾。
            private void method2(){
             int i = 5;
             while(i-- > 0){
              System.out.println(Thread.currentThread().getName() + " : Inner.method2() = " + i);
              try{
               Thread.sleep(500);
              }catch(InterruptedException ie){
               ie.printStackTrace();
              }
             }
            }
           }

           
           public void method1(Inner inner){
            synchronized (inner){//使用對象鎖
             inner.method1();
            }
           }
           
           public void method2(Inner inner){
            inner.method2();
           }
           
           
           public static void main(String[] args){
            final Thread3 thread3 = new Thread3();
            final Inner inner = thread3.new Inner();
            
            Thread thread1 = new Thread(new Runnable(){
             public void run(){
              thread3.method1(inner);
             }
            }, "Thread1");
            
            Thread thread2 = new Thread(new Runnable(){
             public void run(){
              thread3.method2(inner);
             }
            }, "Thread2");
            
            thread1.start();
            thread2.start();//thread2無需等待thread1運行完畢,就可執行,
           }
          }

          4.

          public class Thread4 {

           class Inner{
            private void method1(){
             int i = 5;
             while(i-- > 0){
              System.out.println(Thread.currentThread().getName() + " : Inner.method1() = " + i);
              try{
               Thread.sleep(500);
              }catch(InterruptedException ie){
               ie.printStackTrace();
              }
             }
            }
            
            //盡管線程thread1與thread2訪問了同一個Inner對象中兩個毫不相關的部分,但因為thread1先獲得了對Inner的對象鎖,
            //所以thread2對Inner.mmethod2()的訪問也被阻塞,因為method2()是Inner中的一個同步方法。
            private synchronized void method2(){
             int i = 5;
             while(i-- > 0){
              System.out.println(Thread.currentThread().getName() + " : Inner.method2() = " + i);
              try{
               Thread.sleep(500);
              }catch(InterruptedException ie){
               ie.printStackTrace();
              }
             }
            }
           }

           
           public void method1(Inner inner){
            synchronized (inner){//使用對象鎖
             inner.method1();
            }
           }
           
           public void method2(Inner inner){
            inner.method2();
           }
           
           
           public static void main(String[] args){
            final Thread4 thread4 = new Thread4();
            final Inner inner = thread4.new Inner();
            
            Thread thread1 = new Thread(new Runnable(){
             public void run(){
              thread4.method1(inner);
             }
            }, "Thread1");
            
            Thread thread2 = new Thread(new Runnable(){
             public void run(){
              thread4.method2(inner);
             }
            }, "Thread2");
            
            thread1.start();
            thread2.start();//thread2要等待thread1運行完才能執行,因為method2是一個同步方法
           }
          }

          5.

          import java.text.SimpleDateFormat;
          import java.util.Date;

          public class TestThread extends Thread{

           private static Integer threadCounterLock;//用于同步,防止數據被寫亂,可以是任意的對象
           private static int threadCount;//靜態變量用于本類的線程計數器
           
           static{
            threadCount = 0;
            threadCounterLock = new Integer(0);
           }
           
           public TestThread(){
            super();
           }
           
           public synchronized static void incThreadCount(){
            threadCount++;
            System.out.println("thread count after enter: " + threadCount);
           }
           
           public synchronized static void decThreadCount(){
            threadCount--;
            System.out.println("thread count after leave: " + threadCount);
           }
           
           public void run(){
          //  synchronized(threadCounterLock){//同步
          //   threadCount++;
          //   System.out.println("thread count after enter: " + threadCount);
          //  }
            
            incThreadCount();//此語句作用同上面注釋掉的同步代碼塊
            
            final long nSleepMilliSecs = 1000; //循環中的休眠時間

            
            long nCurrentTime = System.currentTimeMillis();
            long nEndTime = nCurrentTime + 10000;//運行截止時間

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            
            try{
             while(nCurrentTime < nEndTime){
              nCurrentTime = System.currentTimeMillis();
              System.out.println("Thread " + this.hashCode() + ", current time: " + simpleDateFormat.format(new Date(nCurrentTime)));
              
              try{
               sleep(nSleepMilliSecs);
              }catch(InterruptedException ie){
               ie.printStackTrace();
              }
             }
            }finally{
          //   synchronized(threadCounterLock){
          //    threadCount--;
          //    System.out.println("thread count after leave: " + threadCount);
          //   }
             
             decThreadCount();//此語句作用同上面注釋掉的同步代碼塊
            }
            
           }
           
           public static void main(String[] args){
            TestThread[] testThread = new TestThread[2];
            for(int i=0; i<2; i++){
             testThread[i] = new TestThread();
             testThread[i].start();
            }
           }
          }



          posted @ 2007-04-09 20:54 chenweicai 閱讀(1091) | 評論 (0)編輯 收藏


          1。類中定義內部類

          public class Outer {

           private int outer_i = 100;//內部類可以訪問外部類的變量
           
           void test(){
            Inner inner = new Inner();
            inner.display();
           }
           
           //當一個類中的程序代碼要用到另外一個類的實例對象,
           //而另外一個類的程序代碼又要訪問第一個類的成員時
           // 將另外一個類作成第一個類的內部類。
           class Inner{
            
            private int inner_i;
            //內部類的成員只有在內部類的范圍之內是可知的,
            //并不能被外部類使用
            
            void display(){
             System.out.println("display: outer_i = " + outer_i);
            }
           }
          }


           public static void main(String[] args) {

            Outer outer = new Outer();
            outer.test();
           }

          2.

          public class Outer {

           private int size = 1;
           
           public void print(){
            Inner inner = new Inner();
            inner.method(11);
           }
           
           public class Inner{
            private int size = 3;
            
            public void method(int size){
             size++;//method的形參
             this.size++;//Inner類中的成員變量
             Outer.this.size++;//Outer類中的成員變量
             
             System.out.println("method size is :" +size);
             System.out.println("Inner size is :" + this.size);
             System.out.println("Outer size is :" + Outer.this.size);
            }
           }
           
           public static void main(String[] args){
            Outer outer = new Outer();
            outer.print();
           }
          }

          3.內部類被外部引用

          public class Outer {

           private int size = 10;
           
           //內部類聲明為public 可從外部類之外被調用
           public class Inner{
            public void print(){
             System.out.println(++size);
            }
           }
          }

          public class test {


           public static void main(String[] args) {

            Outer outer = new Outer();
            Outer.Inner inner = outer.new Inner();
            inner.print();
           }
          }

          4.方法中定義內部類

          public class Outer {

           int outer_i = 100;
           String str = new String("Between");
           
           void test(){
            for(int i=0; i<5; i++){
             
             class Inner{
              void display(){
               System.out.println("display: outer_i = " + outer_i);
              }
             }
             
             Inner inner = new Inner();
             inner.display();
            }
           }
           
           //在方法中定義的內部類只能訪問方法中的final類型的局部變量
           //因為用final定義的局部變量相當于一個常量,她的生命周期超出了方法運行的生命周期
           public void amethod(final int iArgs){
            int it315;//此變量內部類不能訪問
            
            class Bicycle{
             public void sayhello(){
              System.out.println(str);
              System.out.println(iArgs);
             }
            }
            Bicycle bicycle = new Bicycle();
            bicycle.sayhello();
           }
          }


           public static void main(String[] args) {

            Outer outer = new Outer();
            outer.test();
            outer.amethod(18);
            
           }

          posted @ 2007-04-08 22:13 chenweicai 閱讀(126) | 評論 (0)編輯 收藏

          1.靜態變量

          public class Chinese {

           static String country = "中國";
           
           String name;
           int age;
           
           void singOurCountry(){
            System.out.println("啊, 親愛的" + country);
           }
          }

           public static void main(String[] args) {

            System.out.println("Chinese country is :" + Chinese.country);//類.成員
            Chinese ch1 = new Chinese();
            System.out.println("Chinese country is :" + ch1.country);//對象.成員
                  ch1.singOurCountry();
           }


          注:1. 不能把任何方法內的變量聲明為static類型
                  2. 靜態成員變量為該類的各個實例所共享,所以在內存中該變量只有一份,
                      經常用來計數統計
          如:
          class A{
               private static int  count = 0;

                public A(){
                    count = count + 1;  
              }

          public void finalize(){//在垃圾回收時,finalize方法被調用
                 count = count - 1;
              }
          }


          2.靜態方法
          public class Chinese {

           //在靜態方法里,只能直接調用同類中的其他靜態成員,不能直接訪問類中的非靜態成員和方法
           //因為對如非靜態成員和方法,需先創建類的實例對象后才可使用。
           //靜態方法不能使用關鍵字this和super,道理同上
           //main方法是靜態方法,不能直接訪問該類中的非靜態成員,必須先創建該類的一個實例
           static void sing(){
            System.out.println("啊...");
           }
           
           void singOurCountry(){
            sing();//類中的成員方法可以直接訪問靜態成員方法
            System.out.println("啊...祖國...");
           }
          }

           public static void main(String[] args) {

            Chinese.sing();//類.方法
            
            Chinese ch1 = new Chinese();
            ch1.sing();//對象名.方法
            ch1.singOurCountry();
           }


          3. 靜態代碼塊
          public class StaticCode {

           static String country;
           
           static{
            country = "Chine";
            System.out.println("Static code is loading...");
           }
          }

          public class TestStatic {

           static{
            System.out.println("TestStatic code is loading..."); 
           }
           
           public static void main(String[] args){
            System.out.println("begin executing main method...");
            //類中的靜態代碼將會自動執行,盡管產生了類StaticCode的兩個實例對象,
            //但其中的靜態代碼塊只被執行了一次。同時也反過來說明:類是在第一次被使用時
            //才被加載,而不是在程序啟動時就加載程序中所有可能要用到的類
            new StaticCode();
            new StaticCode();
           }
           
          }



          posted @ 2007-04-08 20:12 chenweicai 閱讀(133) | 評論 (0)編輯 收藏

          java實現多線程有兩種方法:
          一種是繼承Thread類

          如下面例子:
          public class TestThread extends Thread {

           //實現多線程方法一:繼承Thread類,并重載run方法
           public void run(){
            //要將一段代碼在一個新的線程上運行,該代碼應寫在run函數中
            while(true){
             System.out.println(Thread.currentThread().getName() + "is running...");
            }
           }
          }



          public class ThreadDemo2 {

           public static void main(String[] args) {
            
            //啟動一個新的線程,不是直接調用Thread類子類對象的run方法,
            //而是調用Thread類子類對象的start方法,Thread類對象的start方法
            //將產生新的線程,并在該線程上運行該Thread類對象中的run方法,根據
            //多態性 實際上運行的是Thread子類的run方法
            TestThread testThread = new TestThread();
            testThread.start();
            
            while(true){
             System.out.println("main thread is running...");
            }
           }

          }



          方法二是:實現接口Runnable中的run函數

          如下列所示

          //實現多線程方法二:實現Runnable接口,重載run函數,
          //大多數情況下,如果只想重寫 run() 方法,而不重寫其他 Thread 方法,
          //那么應使用 Runnable 接口。這很重要,
          //因為除非程序員打算修改或增強類的基本行為,否則不應為該類創建子類。
          public class TestThread implements Runnable {

           public void run() {
            // TODO Auto-generated method stub
            System.out.println(Thread.currentThread().getName() + "is running......");
           }
           
          }


           public static void main(String[] args) {
            // TODO Auto-generated method stub
            TestThread testThread = new TestThread();//創建TestThread類的一個實例
            Thread thread = new Thread(testThread);//創建一個Thread類的實例
            thread.start();//使線程進入runnable狀態
            
            while(true){
             System.out.println("main thread is running...");
            }
           }


          2.這兩種方法的區別:實現Runnable接口適合多個相同的程序代碼訪問處理同一資源


          如下列所示:
          //四個售票窗口售同一中車票,總共有100張,要求這四個售票多線程進行
          public class TicketRunnable implements Runnable {

           private int tickets = 100;
           public void run() {
             while(true){
              if(tickets > 0){
               System.out.println(Thread.currentThread().getName() +
                 " is saling ticket " + tickets--);
              }
             }
           }

          }

          public class TicketRunnableDemo {

           public static void main(String[] args){
            //創建了四個線程,每個線程調用了同一各TicketRunnable對象中run方法,
            //訪問的是同一個對象中的變量tickets,從而滿足了我們的要求。
            //實現Runnable接口適合多個相同的程序代碼訪問處理同一資源
            TicketRunnable ticketrunnable = new TicketRunnable();
            new Thread(ticketrunnable).start();
            new Thread(ticketrunnable).start();
            new Thread(ticketrunnable).start();
            new Thread(ticketrunnable).start();
           }
          }

          而繼承繼承Thread類,并重載run方法不能達到此目的
          如下所示:
          public class TicketThread extends Thread {

           private int tickets = 100;
           
           public void run(){
            while(true){
             if(tickets>0){
              System.out.println(Thread.currentThread().getName() +
                "is saling ticket " + tickets--);
             }
            }
           }
          }

          public class TicketThreadDemo {

           public static void main(String[] args){
            //我們希望的是四個線程共售100,我們創建四個線程
            //但結果與我們希望的相反,各個線程各售100張票
            new TicketThread().start();
            new TicketThread().start();
            new TicketThread().start();
            new TicketThread().start();
            
          //  如果我們象下面這樣寫的話,只有一個線程在售票,沒有實現四個線程并發售票
          //  TicketThread ticketThread = new TicketThread();
          //  ticketThread.start();
          //  ticketThread.start();
          //  ticketThread.start();
          //  ticketThread.start();
           }
          }

          3.后臺線程

          public class DaemonTest {
           public static void main(String[] args){
            TicketRunnable ticketRunnable = new TicketRunnable();
            Thread thread = new Thread(ticketRunnable);
            thread.setDaemon(true);//將此線程設置為后臺線程,當進程中只有后臺線程時,進程就會結束
            thread.start();
           }
          }

          4.聯合線程
          (還沒搞懂)

          5. 線程同步

          在上面的售票程序中,線程可能是不安全的,

          比如我們修改一下:

          public class ThreadRunnable implements Runnable{
           
           private int tickets = 100;
           public void run() {
             while(true){
              if(tickets > 0){
               try{
                Thread.sleep(10);//使該線程暫停,
               }catch(Exception e){
                System.out.println(e.getMessage());
               }
               System.out.println(Thread.currentThread().getName() +
                 " is saling ticket " + tickets--);
              }
             }
           }
          }

          再次測試時,發現可能會打印-1 -2 這樣的ticket

          所以我們要修改一下,使該程序是線程安全的,

          public class TicketSynch implements Runnable{
           
           private int tickets = 100;
           String str = new String("");//此對象放在run函數之外
           
           public void run(){
            while(true){
             synchronized(str){//同步代碼塊
              if(tickets > 0){
               try{
                Thread.sleep(10);
               }
               catch(Exception e){
                System.out.println(e.getMessage());
               }
               System.out.println(Thread.currentThread().getName()
                 +" is saling ticket " + tickets--);
              }
             }
            }
           }

          }

          但是 同步以犧牲程序的性能為代價

          5. 同步函數

          public class SynchMethod implements Runnable{

           private int tickets = 100;
           public void run(){
            while(true){
             sale();
            }
           }
           
           public synchronized void sale(){
            if(tickets > 0){
             try{
              Thread.sleep(10);
             }catch(Exception e){
              System.out.println(e.getMessage());
             }
             System.out.println(Thread.currentThread().getName()
               + "is saling ticket " + tickets--);
            }
           }
          }

           public static void main(String[] args){
            //創建了四個線程,每個線程調用了同一各TicketRunnable對象中run方法,
            //訪問的是同一個對象中的變量tickets,從而滿足了我們的要求。
            //實現Runnable接口適合多個相同的程序代碼訪問處理同一資源
            SynchMethod synchMethod = new SynchMethod();
            new Thread(synchMethod).start();
            new Thread(synchMethod).start();
            new Thread(synchMethod).start();
            new Thread(synchMethod).start();
           }

          6.代碼塊和函數間的同步
          public class MethodAndBlockSyn implements Runnable{

           private int tickets = 100;
           String str = new String("");
           
           public void run(){
            if(str.equals("method")){
             while(true){
              sale();
             }
            }
            else{
             synchronized(str){//同步代碼塊
              if(tickets > 0){
               try{
                Thread.sleep(10);
               }
               catch(Exception e){
                System.out.println(e.getMessage());
               }
               System.out.println(Thread.currentThread().getName()
                 +" is saling ticket " + tickets--);
              }
             }
            }
           }
           
           
           public synchronized void sale(){
            if(tickets > 0){
             try{
              Thread.sleep(10);
             }catch(Exception e){
              System.out.println(e.getMessage());
             }
             System.out.println(Thread.currentThread().getName()
               + "is saling ticket " + tickets--);
            }
           }
          }


           public static void main(String[] args){
            MethodAndBlockSyn test = new MethodAndBlockSyn();
            new Thread(test).start();// 這個線程調用同步代碼塊
            
            try{
             Thread.sleep(1);//讓線程等待100ms 是為了讓該線程得到CPU
             //避免該線程未得到CPU就將str設置為method,從而使此線程也是調用同步函數
            }catch(Exception e)
            {
            }
            test.str = new String("method");
            new Thread(test).start();//這個線程調用同步函數
           }

           

          7. 線程間通訊

          wait: 告訴當前線程放棄監視器并進入睡眠狀態,知道有其他線程進入同一監視器并調用notify為止。

          notify:喚醒同一對象監視器中調用wait的第一個線程。類似飯館有一個空位置后通知所有等待就餐顧客中的第一位可以入座的情況

          notifyAll:喚醒同一對象監視器中調用wait的所有線程,具有最高優先級的程序先被喚醒并執行。

          以上三個方法只能在synchronized方法中調用

          public class Record {

           private String name = "陳世美";
           private String sex = "女";
           boolean bFull = false;
           
           public synchronized void put(String name, String sex)
           {
            if(bFull)
             try {
              wait();
             } catch (InterruptedException e1) {
              // TODO Auto-generated catch block
              e1.printStackTrace();
             }
            
            this.name = name;
            
            try{
             Thread.sleep(10);
            }
            catch(Exception e){
             System.out.println(e.getMessage());
            }
            
            this.sex = sex;
            
            bFull = true;
            notify();
           }
           
           public synchronized void get(){
            if(!bFull)
             try {
              wait();
             } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
             }
            System.out.println(name + "--->" + sex);
            
            bFull = false;
            notify();
           }
          }




          public class Productor implements Runnable {

           Record record = null;
           
           public Productor(Record record){
            this.record = record;
           }
           
           public void run(){
            int i = 0;
            
            while(true){
             if(i==0)
              record.put("陳陳陳", "男");
             else
              record.put("陳世美", "女");
             i = (i+1)%2;
            }
           }
          }

          public class Consumer implements Runnable {

           Record record = null;
           
           public Consumer(Record record){
            this.record = record;
           }
           
           public void run(){
            while(true){
             record.get();
            }
           }
          }

           public static void main(String[] args) {
            // TODO Auto-generated method stub

            Record record = new Record();
            new Thread(new Productor(record)).start();
            new Thread(new Consumer(record)).start();
           }



           

           

          posted @ 2007-04-08 17:14 chenweicai 閱讀(189) | 評論 (0)編輯 收藏

           我們知道,Java利用ClassLoader將類載入內存,并且在同一應用中,可以有很多個ClassLoader,通過委派機制,把裝載的任務傳遞給上級的裝載器的,依次類推,直到啟動類裝載器(沒有上級類裝載器)。如果啟動類裝載器能夠裝載這個類,那么它會首先裝載。如果不能,則往下傳遞。當父類為null時,JVM內置的類(稱為:bootstrap class loader)就會充當父類。想想眼下的越來越多用XML文件做配置文件或者是描述符、部署符。其實這些通過XML文檔描述的配置信息最終都要變成Java類,基實都是通過ClassLoader來完成的。URLClassLoader是ClassLoader的子類,它用于從指向 JAR 文件和目錄的 URL 的搜索路徑加載類和資源。也就是說,通過URLClassLoader就可以加載指定jar中的class到內存中。

          下面來看一個例子,在該例子中,我們要完成的工作是利用URLClassLoader加載jar并運行其中的類的某個方法。

          首先我們定義一個接口,使所有繼承它的類都必須實現action方法,如下:

          public   interface  ActionInterface  {
              
          public  String action();
          }

          完成后將其打包為testInterface.jar文件。

          接下來新建一工程,為了編譯通過,引入之前打好的testInterface.jar包。并創建TestAction類,使它實現ActionInterface接口。如下:

           

          public   class  TestAction  implements  ActionInterface  {
              
          public  String action()  {
                  
          return   " com.mxjava.TestAction.action " ;
              }

          }

          完成后將其打包為test.jar,放在c盤根目錄下。下面要做的就是利用URLClassLoader加載并運行TestAction的action方法,并將返回的值打印在控制臺上。

          新建一工程,引入testInterface.jar包。并創建一可執行類(main方法),在其中加入如下代碼:

          URL url  =   new  URL(“file:C: / test.jar”);
          URLClassLoader myClassLoader 
          =   new  URLClassLoader( new  URL[]  { url } );
          Class myClass 
          =  myClassLoader.loadClass(“com.mxjava.TestAction”);
          ActionInterface action 
          =  (ActionInterface)myClass.newInstance();
          System.out.println(action.action());

            在上面的例子中,首先利用URLClassLoader加載了C:\test.jar包,將其中的com.mxjava.TestAction類載入內存,將其強制轉型為testInterface包中的ActionInterface類型,最后調用其action方法,并打印到控制臺中。

            執行程序后,在控制臺上如期打印出我們想要的內容。但是,事情并沒有那么簡單,當我們將該代碼移動web應用中時,就會拋出異常。原來,Java為我們提供了三種可選擇的ClassLoader:
          1. 系統類加載器或叫作應用類加載器 (system classloader or application classloader)
          2. 當前類加載器
          3. 當前線程類加載器

            在上例中我們使用javac命令來運行該程序,這時候使用的是系統類加載器 (system classloader)。這個類加載器處理 -classpath下的類加載工作,可以通過ClassLoader.getSystemClassLoader()方法調用。 ClassLoader 下所有的 getSystemXXX()的靜態方法都是通過這個方法定義的。在代碼中,應該盡量少地調用這個方法,以其它的類加載器作為代理。否則代碼將只能工作在簡單的命令行應用中。當在web應用中時,服務器也是利用ClassLoader來加載class的,由于ClassLoader的不同,所以在強制轉型時JVM認定不是同一類型。(在JAVA中,一個類用其完全匹配類名(fully qualified class name)作為標識,這里指的完全匹配類名包括包名和類名。但在JVM中一個類用其全名和一個加載類ClassLoader的實例作為唯一標識。因此,如果一個名為Pg的包中,有一個名為Cl的類,被類加載器KlassLoader的一個實例kl1加載,Cl的實例,即C1.class在JVM中表示為(Cl, Pg, kl1)。這意味著兩個類加載器的實例(Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它們所加載的類也因此完全不同,互不兼容的。)為了能夠使程序正確運行,我們首要解決的問題就是,如何將URLClassLoader加載的類,同當前ClassLoader保持在同一類加載器中。解決方法很簡單,利用java提供的第三種ClassLoader—當前線程類加載器即可。jdk api文檔就會發現,URLClassLoader提供了三種構造方式:

          // 使用默認的委托父 ClassLoader 為指定的 URL 構造一個新 URLClassLoader。 
          URLClassLoader(URL[] urls) 
          // 為給定的 URL 構造新 URLClassLoader。 
          URLClassLoader(URL[] urls, ClassLoader parent) 
          // 為指定的 URL、父類加載器和 URLStreamHandlerFactory 創建新 URLClassLoader。
          URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) 

          接下來要做的就是,在構造URLClassLoader時,將當前線程類加載器置入即可。如下:

          URLClassLoader myClassLoader  =   new  URLClassLoader( new  URL[]  { url } , Thread.currentThread().getContextClassLoader());

          總結:
            
          Java是利用ClassLoader來加載類到內存的,ClassLoader本身是用java語言寫的,所以我們可以擴展自己的ClassLoader。利用URLClassLoader可以加載指定jar包中的類到內存。在命行上利用URLClassLoader加載jar時,是使用系統類加載器來加載class的,所以在web環境下,就會出錯。這是因為JVM中一個類用其全名和一個加載類ClassLoader的實例作為唯一標識的。我們只要利用URLClassLoader的第二種構造方法并傳入當前線程類加載器即可解決。

          posted @ 2007-04-06 16:55 chenweicai 閱讀(310) | 評論 (0)編輯 收藏

          ?

          ?不變模式(Immutable Pattern)顧名思義,它的狀態在它的生命周期內是永恒的(暈,永恒的日月星晨,對象如人,
          太渺小,談不上永恒!
          ),不會改變的.對于其中的不變類(Immutable Class),它的實例可以在運行期間保持狀態永遠不會被
          改變,所以不需要采取共享互斥機制來保護,如果運用得當可以節省大量的時間成本.

          ?請注意上面這段話,不變模式其中的不變類,說明不變類只是不變模式中一個組成部分,不變類和與之相輔的可變
          類,以及它們之間的關系才共同構成不變模式!所以在涉及不變模式的時候一定要研究一個類是不變的還是可變的(Mutable).
          在jdk中的String類和StringBuffer類就組成了一個不變模式.

          還是先看具體的例子:

          final class Dog{
          ??? private final String name;
          ??? private final int age;
          ??? public Dog(String name,int age){
          ??????? this.name = name;
          ??????? this.age = age;
          ??? }
          ???
          ??? public String getName(){return this.name;}
          ??? public int getAge(){return this.age;}
          ???
          ??? public String toString(){
          ??????? return "Dog's name = " + this.name + ",age = " + this.age;
          ??? }
          }

          1.Dog類本身被聲明為final,可以保證它本身的狀態不會被子類擴展方法所改變.
          2.Dog類的所有成員變量都是final的,保證它在構造后不會被重新賦值.而且Dog類所有屬性是private的,只提供getter訪問.
          3.Dog類的能傳入的參數本身是Immutable的.這一點非常重要將在下面具體說明.
          以上條件都是必要條件,而不是充要條件.

          class DisplayDog extends Thread{
          ??? private Dog dog;
          ??? public DisplayDog(Dog dog){
          ??????? this.dog = dog;
          ??? }
          ???
          ??? public void run(){
          ??????? while(true){
          ??????????? System.out.println(this.getName() + " display: " + dog);
          ??????? }
          ??? }
          }

          DisplayDog類是把一個Dog對象傳入后,不斷顯示這個dog的屬性.我們會同時用多個線程來顯示同一dog對象,看看它們在共享
          同一對象時對象的狀態:
          public class Test {
          ??? public static void main(String[] args) throws Exception {
          ??????? Dog dog = new Dog("Sager",100);
          ??????? new DisplayDog(dog).start();
          ??????? new DisplayDog(dog).start();
          ??????? new DisplayDog(dog).start();
          ??? }
          }
          運行這個例子你可以等上一個月,雖然運行一年都正常并不能說明第366天不出現異常,但我們可以把這樣的結果認為是一種
          說明.多個線程共享一個不變類的實例時,這個實例的狀態不會發生改變.事實上它沒有地方讓你去改變.
          在臨界區模式中有些操作必須只允許有一個線程操作,而這個類本身以及對它的訪問類中并不需要進行臨界區保護,這就讓多
          個線程不必等待從而提高了性能.

          既然有這么好的優勢,那我們在需要臨界區保護的對象為什么不都設計成不變類呢?

          1.不變類設計起來有一定難度.對于上面這個用來示例的Dog,由于其本身的屬性,方法都很簡單,我們還可以充分地考慮到可
          以改變它狀態的各種情況.但對于復雜的類,要保證它的不變性,是一個非常吃力的工作.
          ?
          不變類中,任何一個必要都件都不是充要條件,雖然連老骨灰都沒有這么說過,但我還是要真誠地目光深邃語氣凝重地告訴你.
          沒有任何條件是充要條件的意思就是如果任何一個必要條件你沒考慮到,那它就會無法保證類的不可變性.沒有規范,沒有模
          板,完全看一人設計人員的經驗和水平.也許你自以為考慮很全面的一個"不變類"在其他高手面前輕而易舉地就"可變"了.

          2.不變類的種種必要條件限制了類設計的全面性,靈活性.這點不用多說,簡單說因為是不變類,所以你不能A,因為是不變類,你
          不能B.

          當然,如果你是一人很有經驗的設計者,你能成功地設計一個不變類,但因為它的限制而失去一些功能,你就要以使用與之相輔
          的可變類.并且它們之間可以相互轉換,在需要不變性操作的時候以不變類提供給用戶,在需要可變性操作的時候以可變類提供
          給用戶.

          在jdk中String被設計為不可變類,一旦生成一個String對象,它的所有屬性就不會被變,任何方法要么返回這個對象本身的原
          始狀態,要么拋棄原來的字符串返回一個新字符串,而絕對不會返回被修改了的字符串對象.
          但是很多時候返回新字符串拋棄原來的字符串對象這樣的操作太浪費資源了.特別是在循環地操作的時候:

          ?String s = "Axman";
          ?for(int i=0;i<1000*1000;i++) s += "x";這樣的操作是致命的.
          那么這種時候需要將原始的不變的s包裝成可變的StringBuffer來操作,性能的改變可能是成千上萬倍:

          ??????? StringBuffer sb = new StringBuffer(s); //將不變的String包裝成可變的String;
          ??????? for(int i=0;i<1000*1000;i++)
          ??????????? sb.append("x");
          ??????? s = new String(sb); //將可變類封裝成不變類.雖然可以調用toString(),但那不是可變到不變的轉換.

          在將可變類封裝到不變類的時候要特別小心.因為你傳入的引用在外面是可以被修改的.所以即使你不變類本身不能去改變屬
          性但屬性有一個外部引用.可以在外面修改:

          final class MutableDog{
          ??? private String name;
          ??? private int age;
          ??? public MutableDog(String name,int age){
          ??????? this.name = name;
          ??????? this.age = age;
          ??? }
          ??? public synchronized void setDog(String name,int age){
          ??????? this.name = name;
          ??????? this.age = age;
          ??? }
          ??? public String getName(){return this.name;}
          ??? public int getAge(){return this.age;}

          ??? public synchronized String toString(){
          ??????? return "Dog's name = " + this.name + ",age = " + this.age;
          ??? }
          ???
          ???? public synchronized ImmatableDog getImmatableDog(){
          ???????? return new ImmatableDog(this);
          ???? }
          }

          final class ImmatableDog{
          ??? private final String name;
          ??? private final int age;
          ??? public ImmatableDog(String name,int age){
          ??????? this.name = name;
          ??????? this.age = age;
          ??? }
          ???
          ??? public ImmatableDog(MutableDog dog){
          ??????? this.name = dog.getName();
          ??????? this.age = dog.getAge();
          ??? }???
          ???
          ??? public String getName(){return this.name;}
          ??? public int getAge(){return this.age;}
          ???
          ??? public String toString(){
          ??????? return "Dog's name = " + this.name + ",age = " + this.age;
          ??? }
          }


          MutableDog類是可變的,可以滿足我們利用對象的緩沖來讓對象成為表示另一個實體的功能.但它們之間
          隨時可以根據需要相互轉換,但是我們發現:
          ??? public ImmatableDog(MutableDog dog){
          ??????? this.name = dog.getName();
          ??????? this.age = dog.getAge();
          ??? }
          這個方法是不安全的.當一個屬性為"Sager",100的dog被傳進來后,執行this.name = dog.getName();后,
          如果線程切換到其它線程執行,那么dog的屬性就可能是"p4",80,這時再執行this.age = dog.getAge();
          我們就會得到一個屬性為"Sager",80的這樣一個錯誤的不可變對象.這是一個非常危險的陷井.在這里我們
          可以通過同上來解決:
          ??? public ImmatableDog(MutableDog dog){
          ??????? synchronized(dog){
          ??????????? this.name = dog.getName();
          ??????????? this.age = dog.getAge();
          ??????? }
          ??? }
          注意這里同步的MutableDog,它將會和MutableDog的setDog產生互斥.它們都需要獲取同一MutableDog對象的
          鎖,如果MutableDog的setDog不是方法同步(synchronized(this)),即使ImmatableDog(MutableDog dog)中同步
          了dog,也不能保證安全,它們需要在同一對象上互斥.

          posted @ 2007-03-29 15:58 chenweicai 閱讀(121) | 評論 (0)編輯 收藏

          簡單工廠模式(缺點:每增加一個具體產品時 ,就要修改工廠方法,工廠方法負責了所有具體產品的創建)

          舉個例子:
          ------------------
          public interface Fruit {

          ?void grow();
          ?
          ?void harvest();
          ?
          ?void plant();
          }
          -------------------
          public class Apple implements Fruit {

          ?private int treeAge;
          ?
          ?public void grow() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Apple is growing...");
          ??
          ?}

          ?public void harvest() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Apple has been harvested.");
          ?}

          ?public void plant() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Apple has been planted.");
          ??
          ?}

          ?public int getTreeAge() {
          ??return treeAge;
          ?}

          ?public void setTreeAge(int treeAge) {
          ??this.treeAge = treeAge;
          ?}
          ?
          }
          -------------------
          public class Grape implements Fruit {

          ?private boolean seedless;
          ?
          ?
          ?
          ?public boolean isSeedless() {
          ??return seedless;
          ?}

          ?public void setSeedless(boolean seedless) {
          ??this.seedless = seedless;
          ?}

          ?public void grow() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Grape is growing...");
          ?}

          ?public void harvest() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Grape has been harvested.");
          ?}

          ?public void plant() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Grape has been planted.");
          ??
          ?}

          ?
          }


          ---------------------------
          public class Strawberry implements Fruit {

          ?public void grow() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Strawberry is growing...");
          ?}

          ?public void harvest() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Strawberry has been harvested.");
          ?}

          ?public void plant() {
          ??// TODO Auto-generated method stub
          ??System.out.println("Strawberry has been planted.");
          ?}
          }

          -------------------------
          public class FruitGardener {

          ?//靜態工廠方法
          ?public static Fruit factory(String which) throws BadFruitException{
          ??if(which.equalsIgnoreCase("apple")){
          ???return new Apple();
          ??}
          ??else if(which.equalsIgnoreCase("strawberry")){
          ???return new Strawberry();
          ??}
          ??else if(which.equalsIgnoreCase("grape")){
          ???return new Grape();
          ??}
          ??else{
          ???throw new BadFruitException("Bad fruit request");
          ??}
          ?}
          }

          ---------------------------
          public class BadFruitException extends Exception {

          ?public BadFruitException(String msg){
          ??super(msg);
          ?}
          }


          --------------------------
          public class Client {

          ?public static void main(String[] args){
          ??
          ??try{
          ???Fruit apple = (Fruit)FruitGardener.factory("Apple");
          ???System.out.println("apple is class: " + apple.getClass().getName());
          ???apple.plant();
          ???apple.grow();
          ???apple.harvest();
          ???System.out.println();
          ???
          ???Fruit grape = (Fruit)FruitGardener.factory("grape");
          ???System.out.println("grape is class: " + grape.getClass().getName());
          ???grape.plant();
          ???grape.grow();
          ???grape.harvest();
          ???System.out.println();
          ???
          ???Fruit strawberry = (Fruit)FruitGardener.factory("strawberry");
          ???System.out.println("strawberry is class: " + strawberry.getClass().getName());
          ???strawberry.plant();
          ???strawberry.grow();
          ???strawberry.harvest();
          ???
          ??}catch(BadFruitException e){
          ???e.printStackTrace();
          ??}
          ?}
          }


          2 工廠方法模式:解決了簡單工廠模式的缺點, 將每一個具體產品的創建工作交給對應的具體工廠角色去解決

          舉個例子:
          ---------------------
          public interface FruitGardener {

          ?public Fruit factory();
          }


          ---------------------

          public class AppleGardener implements FruitGardener{

          ?public Fruit factory() {
          ??return new Apple();
          ?}
          }

          -----------------

          public class GrapeGardener implements FruitGardener{

          ?public Fruit factory() {
          ??return new Grape();
          ?}
          }


          ----------------------

          public class StrawGardener implements FruitGardener{

          ?public Fruit factory() {
          ??return new Strawberry();
          ?}
          }

          -----------------
          public class Client {

          ?private static FruitGardener applegardener, grapegardener, strawgardener;
          ?private static Fruit apple, grape, strawberry;
          ?
          ?public static void main(String[] args){
          ??applegardener = new AppleGardener();
          ??apple = applegardener.factory();
          ??System.out.println("apple is class: " + apple.getClass().getName());
          ??apple.plant();
          ??apple.grow();
          ??apple.harvest();
          ??System.out.println();
          ??
          ??grapegardener = new GrapeGardener();
          ??grape = grapegardener.factory();
          ??System.out.println("grape is class: " + grape.getClass().getName());
          ??grape.plant();
          ??grape.grow();
          ??grape.harvest();
          ??System.out.println();

          ??strawgardener = new StrawGardener();
          ??strawberry = strawgardener.factory();
          ??System.out.println("strawberry is class: " + strawberry.getClass().getName());
          ??strawberry.plant();
          ??strawberry.grow();
          ??strawberry.harvest();
          ?}
          }


          3 抽象工廠模式:解決多產品簇問題

          舉個例子:

          -------------------
          public interface Fruit {

          //?public String getName();
          }

          public class NorthernFruit implements Fruit{

          ?private String name;

          ?public String getName() {
          ??return name;
          ?}

          ?public void setName(String name) {
          ??this.name = name;
          ?}

          ?public NorthernFruit(String name) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.name = name;
          ?}
          ?
          ?
          }



          public class TropicalFruit implements Fruit{

          ?private String name;

          ?public String getName() {
          ??return name;
          ?}

          ?public void setName(String name) {
          ??this.name = name;
          ?}

          ?public TropicalFruit(String name) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.name = name;
          ?}
          ?
          ?
          }



          public interface Veggie {

          }


          public class TropicalVeggie implements Veggie{
          ?
          ?private String name;

          ?public String getName() {
          ??return name;
          ?}

          ?public void setName(String name) {
          ??this.name = name;
          ?}

          ?public TropicalVeggie(String name) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.name = name;
          ?}
          ?
          ?

          }


          public class NorthernVeggie implements Veggie{

          ?private String name;

          ?public String getName() {
          ??return name;
          ?}

          ?public void setName(String name) {
          ??this.name = name;
          ?}

          ?public NorthernVeggie(String name) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.name = name;
          ?}
          ?
          ?
          }

          -------------------------

          public interface Gardener {

          ?public Fruit createFruit(String name);
          ?
          ?public Veggie createVeggie(String name);
          }

          public class NorthernGardener implements Gardener {

          ?public Fruit createFruit(String name) {
          ??return new NorthernFruit(name);
          ?}

          ?public Veggie createVeggie(String name) {
          ??return new NorthernVeggie(name);
          ?}

          ?
          }



          public class TropicalGardener implements Gardener {

          ?public Fruit createFruit(String name) {
          ??return new TropicalFruit(name);
          ?}

          ?public Veggie createVeggie(String name) {
          ??return new TropicalVeggie(name);
          ?}

          ?
          }


          public class Client {

          ?private static Gardener tropicalgardener ,northerngardener;
          ?private static Fruit northernfruit, tropicalfruit;
          ?private static Veggie northernveggie, tropicalveggie;
          ?
          ?public static void main(String[] args){
          ??tropicalgardener = new TropicalGardener();
          ??tropicalfruit = tropicalgardener.createFruit("tropicalfruit");
          ??//System.out.println(tropicalfruit.getName());
          ??tropicalveggie = tropicalgardener.createVeggie("tropicalveggie");
          ??
          ??northerngardener = new NorthernGardener();
          ??northernfruit = northerngardener.createFruit("northernfruit");
          ??northernveggie = northerngardener.createVeggie("northernveggie");
          ?}
          ?
          }

          posted @ 2007-03-29 13:18 chenweicai 閱讀(290) | 評論 (0)編輯 收藏

          假設在你的應用中使用一些對象,你如何拷貝你的對象呢?最明顯的方法是講一個對象簡單的賦值給另一個,就像這樣:

          ??? obj2 = obj1;
          ??? 但是這個方法實際上沒有拷貝對象而僅僅是拷貝了一個對象引用,換換言之,在你執行這個操作后仍然只有一個對象,但是多出了一個對該對象的引用。

          ??? 如果這個看似明顯的方法不能正常工作,那么如何實際的拷貝一個對象呢?為什么不試試Object.clone呢?這個方法對Object的所有子類都是可用的。例如:

          package clone.clone1;

          public class ClassA implements Cloneable{//要繼承Cloneable接口,否則會拋出異常
          ?private int x;

          ?public ClassA(int x) {
          ??super();
          ??// TODO Auto-generated constructor stub
          ??this.x = x;
          ?}

          ?//要覆蓋clone方法,因為Object.clone()是protect類型
          ??? //Object.clone完成的是對象的“淺”拷貝,即簡單的成員到成員的拷貝。
          ?//它不做“深度”拷貝,即成員或者數組指向的對象的遞歸拷貝
          ?public Object clone(){
          ??// TODO Auto-generated method stub
          ??try{
          ???return super.clone();
          ??}catch(CloneNotSupportedException e){
          ???throw new InternalError(e.toString());
          ??}
          ?}

          ?public int getX() {
          ??return x;
          ?}

          ?public void setX(int x) {
          ??this.x = x;
          ?}
          ?
          ?
          }


          package clone.clone1;

          public class Test {

          ?/**
          ? * @param args
          ? */
          ?public static void main(String[] args) {
          ??// TODO Auto-generated method stub
          ???ClassA a = new ClassA(12);
          ???ClassA b = (ClassA)a.clone();
          ???ClassA c = a;//此處多出了一個對該對象的引用,并沒有實現克隆原對象
          ???System.out.println("Object A's X is :" + a.getX());
          ???System.out.println("Object B's X is :" + b.getX());
          ???System.out.println("Object C's X is :" + c.getX());
          ???a.setX(16);
          ???b.setX(18);
          ???System.out.println("After set,Object A's X is :" + a.getX());
          ???System.out.println("After set,Object B's X is :" + b.getX());
          ???System.out.println("After set,Object C's X is :" + c.getX());
          ?}
          }


          Object.clone完成的是對象的“淺”拷貝,即簡單的成員到成員的拷貝。它不做“深度”拷貝,即成員或者數組指向的對象的遞歸拷貝。
          package clone.clone2;

          import java.util.HashMap;

          public class ClassA implements Cloneable{
          ?
          ?public HashMap hashmap;//成員對象不是簡單的數據,而是復雜的數據類型

          ?public ClassA() {
          ??hashmap = new HashMap();
          ??hashmap.put("key1", "value1");
          ??hashmap.put("key2", "value2");
          ?}
          ?
          ?public Object clone(){//不能簡單的調用super.clone方法,
          ??//否則在新的對象中的復雜數據成員只是原對象的一個引用
          ??try{
          ???ClassA obj = (ClassA)super.clone();
          ???obj.hashmap = (HashMap)hashmap.clone();
          ???return obj;
          ??}catch(CloneNotSupportedException e){
          ??throw new InternalError(e.toString());
          ??}
          ?}

          }


          package clone.clone2;

          public class test {

          ?public static void main(String[] args) {
          ??// TODO Auto-generated method stub

          ??ClassA a = new ClassA();
          ??ClassA b = (ClassA)a.clone();
          ??a.hashmap.remove("key1");
          ??System.out.println(b.hashmap.get("key1"));
          ??System.out.println(a.hashmap.get("key1"));
          ?}

          }

          posted @ 2007-03-27 19:37 chenweicai 閱讀(457) | 評論 (1)編輯 收藏

           觀察者模式是對象的行為模式,顧名思義,即存在觀察者和被觀察者。?觀察者模式可以讓多個觀察者同時監聽同一個被觀察對象,當被觀察對象發生變化時,并通知所有觀察者,使各個觀察者能作出相應的響應。適當地運用觀察者模式,能提高自身代碼的設計水平。

            觀察者模式理解和編碼都比較簡單,通常包括以下步驟:

            1. 設計觀察者接口類;
            2. 觀察者類實現該接口;
            3. 設計被觀察者抽象類,該類中提供一些方法,如:添加觀察者對象,刪除觀察者對象,把事件通知給各個觀察者對象;
            4. 設計被觀察者類,繼承被觀察者抽象類,在該類中,可以根據需要在該類中,可以定義方法:被觀察者是否發生變化

            以上四步,即完成了觀察者模式的設計。下面代碼分類進行描述以上步驟:

          package Observer;

          import java.util.Enumeration;
          import java.util.Vector;

          abstract public class Subject {

          ?private Vector observersVector = new java.util.Vector();
          ?
          ?public void attach(Observer observer){
          ??observersVector.addElement(observer);
          ??System.out.println("Attached an observer.");
          ?}
          ?
          ?public void detach(Observer observer){
          ??observersVector.removeElement(observer);
          ?}
          ?
          ?public void notifyObservers(){
          ??java.util.Enumeration enumeration = observers();
          ??while(enumeration.hasMoreElements()){
          ???System.out.println("Before notifying");
          ???((Observer)enumeration.nextElement()).update();
          ??}
          ?}
          ?
          ?public Enumeration observers(){
          ??return ((java.util.Vector)observersVector.clone()).elements();
          ?}
          }


          package Observer;

          public class ConcreteSubject extends Subject{

          ?private String state;
          ?
          ?public void change(String newState){
          ??state = newState;
          ??this.notifyObservers();
          ?}
          }


          package Observer;

          public interface Observer {

          ?void update();
          }


          package Observer;

          public class ConcreteObserver implements Observer{
          ?
          ?public void update(){
          ??System.out.println("I am notified.");
          ?}

          }


          package Observer;

          public class Client {

          ?private static ConcreteSubject subject;
          ?private static Observer observer1;
          ?private static Observer observer2;
          ?
          ?public static void main(String[] args){
          ??subject = new ConcreteSubject();
          ??observer1 = new ConcreteObserver();
          ??observer2 = new ConcreteObserver();
          ??
          ??subject.attach(observer1);
          ??subject.attach(observer2);
          ??
          ??subject.change("new state");
          ?}
          }


          運行結果是:

          Attached an observer.
          Attached an observer.
          Before notifying
          I am notified.
          Before notifying
          I am notified.


          在java中提供了Observerable類和Observer接口來實現觀察者模式

          public class Observable
          extends Object

          此類表示模型視圖范例中的 observable 對象,或者說“數據”。可將其子類化,表示應用程序想要觀察的對象(即被觀察者)。

          一個 observable 對象可以有一個或多個觀察者。觀察者可以是實現了 Observer 接口的任意對象。一個 observable 實例改變后,調用 ObservablenotifyObservers 方法的應用程序會通過調用觀察者的 update 方法來通知觀察者該實例發生了改變。

          未指定發送通知的順序。Observable 類中所提供的默認實現將按照其注冊的重要性順序來通知 Observers,但是子類可能改變此順序,從而使用非固定順序在單獨的線程上發送通知,或者也可能保證其子類遵從其所選擇的順序。

          注意,此通知機制與線程無關,并且與 Object 類的 waitnotify 機制完全獨立。

          新創建一個 observable 對象時,其觀察者集合是空的。當且僅當 equals 方法為兩個觀察者返回 true 時,才認為它們是相同的。

          public interface Observer

          一個可在觀察者要得到 observable 對象更改通知時可實現 Observer 接口的類。?
          ?給個用java機制來實現觀察者模式的例子
          在java機制中Observable類相當于抽象主題角色,我們定義具體主題角色來繼承該類
          Observer 接口相當于抽象觀察者角色,我們定義具體觀察者角色來實現該接口
          所以這兩個抽象角色我們不用編寫了 只需編寫兩個具體角色

          //----具體觀察對象角色
          package Observer.javaObserver;

          import java.util.Observable;

          public class Product extends Observable{

          ?private String name;
          ?private float price;
          ?
          ?public String getName(){
          ??return name;
          ?}
          ?
          ?public void setName(String name){
          ??this.name = name;
          ??// 設置變化點
          ??setChanged();
          ??notifyObservers(name);//通知觀察者
          ?}

          ?public float getPrice() {
          ??return price;
          ?}

          ?public void setPrice(float price) {
          ??this.price = price;
          ??//設置變化點
          ??setChanged();
          ??notifyObservers(new Float(price));
          ?}
          ?
          ?public void saveToDb(){
          ??System.out.println("saveToDb");
          ?}
          }

          //------------ 兩個具體觀察者角色
          package Observer.javaObserver;

          import java.util.Observable;
          import java.util.Observer;

          public class NameObserver implements Observer {

          ?private String name = null;
          ?
          ?public void update(Observable obj, Object arg){
          ??if(arg instanceof String){
          ???name = (String)arg;
          ???System.out.println("NameObserver:name changed to " + name);
          ??}
          ?}
          }

          package Observer.javaObserver;

          import java.util.Observable;
          import java.util.Observer;

          public class PriceObserver implements Observer{

          ?private float price=0;
          ?
          ?public void update(Observable obj, Object arg){
          ??if(arg instanceof Float){
          ???price = ((Float)arg).floatValue();
          ???System.out.println("PriceObserver: price change to" + price);
          ??}
          ?}
          }

          //----測試程序

          package Observer.javaObserver;

          import java.util.Observer;

          public class Test {

          ?public static void main(String[] args){
          ??Product product = new Product();
          ??Observer nameObs = new NameObserver();
          ??Observer priceObs = new PriceObserver();
          ??
          ??product.addObserver(nameObs);
          ??product.addObserver(priceObs);
          ??
          ??product.setName("apple");
          ??product.setPrice(9.22f);
          ??
          ??product.setName("Apple");
          ??product.setPrice(9.88f);
          ??}
          }


          //---運行結果

          NameObserver:name changed to apple
          PriceObserver: price change to9.22
          NameObserver:name changed to Apple
          PriceObserver: price change to9.88



          posted @ 2007-03-24 17:03 chenweicai 閱讀(879) | 評論 (0)編輯 收藏

          對于這個系列里的問題,每個學Java的人都應該搞懂。當然,如果只是學Java玩玩就無所謂了。如果你認為自己已經超越初學者了,卻不很懂這些問題,請將你自己重歸初學者行列。內容均來自于CSDN的經典老貼。

          問題一:我聲明了什么!

          String s = "Hello world!";

          許多人都做過這樣的事情,但是,我們到底聲明了什么?回答通常是:一個String,內容是“Hello world!”。這樣模糊的回答通常是概念不清的根源。如果要準確的回答,一半的人大概會回答錯誤。
          這個語句聲明的是一個指向對象的引用,名為“s”,可以指向類型為String的任何對象,目前指向"Hello world!"這個String類型的對象。這就是真正發生的事情。我們并沒有聲明一個String對象,我們只是聲明了一個只能指向String對象的引用變量。所以,如果在剛才那句語句后面,如果再運行一句:

          String string = s;

          我們是聲明了另外一個只能指向String對象的引用,名為string,并沒有第二個對象產生,string還是指向原來那個對象,也就是,和s指向同一個對象。

          問題二:"=="和equals方法究竟有什么區別?

          ==操作符專門用來比較變量的值是否相等。比較好理解的一點是:
          int a=10;
          int b=10;
          則a==b將是true。
          但不好理解的地方是:
          String a=new String("foo");
          String b=new String("foo");
          則a==b將返回false。

          根據前一帖說過,對象變量其實是一個引用,它們的值是指向對象所在的內存地址,而不是對象本身。a和b都使用了new操作符,意味著將在內存中產生兩個內容為"foo"的字符串,既然是“兩個”,它們自然位于不同的內存地址。a和b的值其實是兩個不同的內存地址的值,所以使用"=="操作符,結果會是false。誠然,a和b所指的對象,它們的內容都是"foo",應該是“相等”,但是==操作符并不涉及到對象內容的比較。
          對象內容的比較,正是equals方法做的事。

          看一下Object對象的equals方法是如何實現的:
          boolean equals(Object o){

          return this==o;

          }
          Object對象默認使用了==操作符。所以如果你自創的類沒有覆蓋equals方法,那你的類使用equals和使用==會得到同樣的結果。同樣也可以看出,Object的equals方法沒有達到equals方法應該達到的目標:比較兩個對象內容是否相等。因為答案應該由類的創建者決定,所以Object把這個任務留給了類的創建者。

          看一下一個極端的類:
          Class Monster{
          private String content;
          ...
          boolean equals(Object another){ return true;}

          }
          我覆蓋了equals方法。這個實現會導致無論Monster實例內容如何,它們之間的比較永遠返回true。

          所以當你是用equals方法判斷對象的內容是否相等,請不要想當然。因為可能你認為相等,而這個類的作者不這樣認為,而類的equals方法的實現是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列碼的集合(HashSet,HashMap,HashTable),請察看一下java doc以確認這個類的equals邏輯是如何實現的。

          問題三:String到底變了沒有?

          沒有。因為String被設計成不可變(immutable)類,所以它的所有對象都是不可變對象。請看下列代碼:

          String s = "Hello";
          s = s + " world!";

          s所指向的對象是否改變了呢?從本系列第一篇的結論很容易導出這個結論。我們來看看發生了什么事情。在這段代碼中,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。

          問題四:final關鍵字到底修飾了什么?

          final使得被修飾的變量"不變",但是由于對象型變量的本質是“引用”,使得“不變”也有了兩種含義:引用本身的不變,和引用指向的對象不變。

          引用本身的不變:
          final StringBuffer a=new StringBuffer("immutable");
          final StringBuffer b=new StringBuffer("not immutable");
          a=b;//編譯期錯誤

          引用指向的對象不變:
          final StringBuffer a=new StringBuffer("immutable");
          a.append(" broken!"); //編譯通過

          可見,final只對引用的“值”(也即它所指向的那個對象的內存地址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至于它所指向的對象的變化,final是不負責的。這很類似==操作符:==操作符只負責引用的“值”相等,至于這個地址所指向的對象內容是否相等,==操作符是不管的。

          理解final問題有很重要的含義。許多程序漏洞都基于此----final只能保證引用永遠指向固定對象,不能保證那個對象的狀態不變。在多線程的操作中,一個對象會被多個線程共享或修改,一個線程對對象無意識的修改可能會導致另一個使用此對象的線程崩潰。一個錯誤的解決方法就是在此對象新建的時候把它聲明為final,意圖使得它“永遠不變”。其實那是徒勞的。

          問題五:到底要怎么樣初始化!

          本問題討論變量的初始化,所以先來看一下Java中有哪些種類的變量。
          1. 類的屬性,或者叫值域
          2. 方法里的局部變量
          3. 方法的參數

          對于第一種變量,Java虛擬機會自動進行初始化。如果給出了初始值,則初始化為該初始值。如果沒有給出,則把它初始化為該類型變量的默認初始值。

          int類型變量默認初始值為0
          float類型變量默認初始值為0.0f
          double類型變量默認初始值為0.0
          boolean類型變量默認初始值為false
          char類型變量默認初始值為0(ASCII碼)
          long類型變量默認初始值為0
          所有對象引用類型變量默認初始值為null,即不指向任何對象。注意數組本身也是對象,所以沒有初始化的數組引用在自動初始化后其值也是null

          對于兩種不同的類屬性,static屬性與instance屬性,初始化的時機是不同的。instance屬性在創建實例的時候初始化,static屬性在類加載,也就是第一次用到這個類的時候初始化,對于后來的實例的創建,不再次進行初始化。這個問題會在以后的系列中進行詳細討論。

          對于第二種變量,必須明確地進行初始化。如果再沒有初始化之前就試圖使用它,編譯器會抗議。如果初始化的語句在try塊中或if塊中,也必須要讓它在第一次使用前一定能夠得到賦值。也就是說,把初始化語句放在只有if塊的條件判斷語句中編譯器也會抗議,因為執行的時候可能不符合if后面的判斷條件,如此一來初始化語句就不會被執行了,這就違反了局部變量使用前必須初始化的規定。但如果在else塊中也有初始化語句,就可以通過編譯,因為無論如何,總有至少一條初始化語句會被執行,不會發生使用前未被初始化的事情。對于try-catch也是一樣,如果只有在try塊里才有初始化語句,編譯部通過。如果在catch或finally里也有,則可以通過編譯。總之,要保證局部變量在使用之前一定被初始化了。所以,一個好的做法是在聲明他們的時候就初始化他們,如果不知道要出事化成什么值好,就用上面的默認值吧!

          其實第三種變量和第二種本質上是一樣的,都是方法中的局部變量。只不過作為參數,肯定是被初始化過的,傳入的值就是初始值,所以不需要初始化。

          問題六:instanceof是什么東東?

          instanceof是Java的一個二元操作符,和==,>,<是同一類東東。由于它是由字母組成的,所以也是Java的保留關鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回boolean類型的數據。舉個例子:

          String s = "I AM an Object!";
          boolean isObject = s instanceof Object;

          我們聲明了一個String對象引用,指向一個String對象,然后用instancof來測試它所指向的對象是否是Object類的一個實例,顯然,這是真的,所以返回true,也就是isObject的值為True。
          instanceof有一些用處。比如我們寫了一個處理賬單的系統,其中有這樣三個類:

          public class Bill {//省略細節}
          public class PhoneBill extends Bill {//省略細節}
          public class GasBill extends Bill {//省略細節}

          在處理程序里有一個方法,接受一個Bill類型的對象,計算金額。假設兩種賬單計算方法不同,而傳入的Bill對象可能是兩種中的任何一種,所以要用instanceof來判斷:

          public double calculate(Bill bill) {
          if (bill instanceof PhoneBill) {
          //計算電話賬單
          }
          if (bill instanceof GasBill) {
          //計算燃氣賬單
          }
          ...
          }
          這樣就可以用一個方法處理兩種子類。

          然而,這種做法通常被認為是沒有好好利用面向對象中的多態性。其實上面的功能要求用方法重載完全可以實現,這是面向對象變成應有的做法,避免回到結構化編程模式。只要提供兩個名字和返回值都相同,接受參數類型不同的方法就可以了:

          public double calculate(PhoneBill bill) {
          //計算電話賬單
          }

          public double calculate(GasBill bill) {
          //計算燃氣賬單
          }

          所以,使用instanceof在絕大多數情況下并不是推薦的做法,應當好好利用多態。

          ?

          posted @ 2007-03-23 20:17 chenweicai 閱讀(146) | 評論 (0)編輯 收藏

          僅列出標題
          共6頁: 上一頁 1 2 3 4 5 6 下一頁 
          主站蜘蛛池模板: 高邑县| 化州市| 滨州市| 根河市| 荔波县| 毕节市| 商水县| 板桥市| 朝阳市| 永清县| 三门县| 吴江市| 蕲春县| 云霄县| 祁门县| 石景山区| 藁城市| 宁陕县| 梓潼县| 青州市| 大安市| 岐山县| 广宁县| 东乌珠穆沁旗| 句容市| 绍兴市| 琼结县| 武邑县| 临朐县| 祥云县| 榆树市| 蓝山县| 安塞县| 本溪| 汤阴县| 砚山县| 毕节市| 马山县| 叙永县| 光山县| 临泉县|