銀行業務調度系統篇
我覺得,總的邏輯是要在main方法中體現的,那麼在這個銀行業務調度系統中,main方法要做的事情是什麼呢,我們來將以前的類的功能來分析一下:
NumberManager類:號碼管理器,用來生成來獲取服務的新的客戶,其中有生成號碼的方法,和拿走服務客戶的方法;小技巧是使用remove類似隊列,先到先服務;
NumberMerchine類:來服務的人需要一個機器爲他們來分類,當然設計成單列模式,是因爲只需要一個機器來管理這三個對象,含有這三個號碼管理器對象的實例以及獲取實的方法
WindowService類:具體實現各窗口的服務,那麼各窗口要完成什麼服務,在這個類中要有體現,這是要對面向對象程序設計的理解啊:兩個屬性,窗口的類型;窗口的編號;方法一個執行過程start,執行一個線程,根據窗口類型,接受編號,獲取服務
常量類和枚舉:一個不大不小的工程,涉及了多少變量或者常量,爲了方便查詢和修改,一個必要的常量類是應該設計的;比如說服務時間,固定常量;
關於線程和Synchronized關鍵字:
複習到了線程,我對這方面的知識稍微有點模糊,所以複習了一下以前的知識;
對於兩個獨立的調用線程的方法,方法未加修飾,當在調用兩個線程的start方法時,唯一的區別是這兩個方法的調用順序是不確定的,一旦調用方法就會完全執行完,這是唯一的區別,並不會亂序:
package com.jianjian;
import java.util.concurrent.ThreadFactory;
public class ThreadTest1
{
public static void main(String[] args)
{
new thread1().start();
new thread2().start();
}
}
class thread1extends Thread
{
public void run()
{
for(int i = 0; i< 100; i++)
{
System.out.println("李四吃了"+ i+"個饅頭");
}
}
}
class thread2extends Thread
{
@Override
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println("張三喝了"+i+"杯牛奶");
}
}
}
打印結果只會出現兩個輸出語句先後順序的不同
Synchronized關鍵字,同步的, synchronized關鍵字可以修飾方法,還有靜態代碼塊(static block)
Java中的每一個對象都有一把鎖(lock),只是平時狀態下,鎖是開着的,當被synchronized關鍵字所修飾時,就將這把鎖激活,當一個對象被多個線程所調用時,由於線程的行進度的不確定性,可能會引發方法內很多問題,而synchronized關鍵字就是隻允許方法每一次只能允許一個線程訪問,只有當線程訪問結束,或者拋出異常時,才允許下一個線程的介入:
synchronized修飾方法的對象會上鎖,如果一個類的多個方法都被synchronized修飾時,當一個線程未訪問完畢其中的一個synchronized方法時,其他的線程是不能訪問任何該對象的任何一個synchronized方法的,除非你生成了兩個對象!
下面我們來一步步的分析:
首先 當一個類中的兩個方法都沒有被synchronized修飾時,這兩個方法的打印將是隨機進行的:
package thread;
public class ThreadTest10
{
public static void main(String[]args)
{
Demo demo1 = new Demo();
ThreadTest11test1 = new ThreadTest11(demo1);
ThreadTest12test2 = new ThreadTest12(demo1);
test1.start();
test2.start();
}
}
class Demo
{
public void method1() throwsInterruptedException
{
for(int i = 0 ; i < 15; i ++)
{
Thread.sleep((long)(Math.random()*1000));
System.out.println("hello:"+ i );
}
}
public void method2() throwsInterruptedException
{
for(int i = 0 ; i <15; i ++)
{
Thread.sleep((long)(Math.random()*1000));
System.out.println("world:"+i);
}
}
}
class ThreadTest11extends Thread
{
private Demodemo;
public ThreadTest11(Demo demo)
{
this.demo = demo;
}
@Override
public void run()
{
try
{
demo.method1();
}
catch(InterruptedException e)
{
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
class ThreadTest12extends Thread
{
private Demodemo;
public ThreadTest12(Demo demo)
{
this.demo = demo;
}
@Override
public void run()
{
try
{
demo.method2();
}
catch(InterruptedException e)
{
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
現在我要爲上面的兩個方法加上synchronized關鍵字,使之變得有序,如果只加上一個的話,是沒有意義的,還是亂序,因爲同步方法,可以和非同步方法同時運行;
加上後將會輸出有序;先輸出hello 或者 world;
銀行業務調度系統的NumberManager類用到了單列模式:那麼如何驗證和創建單列模式呢?
創建單列模式的步驟
1. 構造方法私有化,私有化構造方法,則別的類就不能創建他;
2. 既然不能創建對象,那麼也就是無法使用其中的非靜態方法了,只能使用靜態方法獲取這個唯一的實例;
3. 所以應該有一個靜態方法返回自己的對象,而這個對象應該是成員變量,傳入靜態方法中,肯定是私有靜態的了
下面是一個完整的單列模式
package com.jianjian;
public class SingletonTest
{
// 構造方法私有化
private SingletonTest()
{
};
// 生成私有實例對象
private static SingletonTest single =new SingletonTest();
// 構造靜態方法,獲取私有實例對象
public static SingletonTestgetInstance()
{
returnsingle;
}
}
如何驗證單例模式
以前說過,一個對象可以生成多個實例,比如每次使用new來生成相同對象的實例,雖然每個實例都是指向同一個對象,但是各個實例之間是獨立的,也就是生成了兩個對象,這就不是單例模式了;可以通過boolean類型的輸出來驗證
public class BooleanTest
{
public static void main (String [] args)
{
Parent parent1 = new Parent ();
Parent parent2 = new Parent ();
System.out.println (parent1 == parent2);
}
}
class Parent
{
public void method ()
{
}
}
輸出的結果是false;也就是生成的對象不是同一個對象;
使用private關鍵字和static來生成單例模式
面試的時候,寫個單例模式出來;
public class SingletonTest
{
public static void main (String[] args)
{
Parent parent1 = Parent.method ();
Parent parent2 = Parent.method ();
System.out.println (parent1 == parent2);//驗證是否爲同一個對象
}
}
class Parent
{
static Parent parent = new Parent ();//知識點;這裏將實例定義爲static 是爲了使下面的靜態方法可以訪問;
//靜態的方法只能訪問靜態的變量
private Parent()
{
}
public static Parent method ( )//定義一個返回類型爲Parent的方法,將生成的Parent實例返回給主方法,這樣就避免了
//在主方法中生成對象;就完成了單例模式;通過驗證可以知道二者生成的對象是同一個對象,指向同一個對象;
{
return parent;
}
}
static可以不用生成對象,而直接使用類的名字去調用類中的方法;
細節錯誤,一個ArrayList對象儲存Integer類型,
如果定義一個方法返回的是int 類型
比如
publicint method ()
{
return list.remove(0)
}
你覺得這樣做對不對,確實remove返回的是當前的儲存值,如果集合爲空的話,那又會怎麼樣呢,集合爲空則remove(0)返回的就是一個異常,你把異常轉換成了int,你說成嗎。所以寫成Integer,如果是空,就返回Integer = null,也是一個對象
服務窗口類寫完後,感覺要了自己半條命,我覺得其中涉及到太多的知識點了,首先是方法的重構refector,記住,抽取方法的時候Extract Method ,會讓你選擇抽出方法後的位置,destination ,有可能是內部類中,所以選擇外部類或者內部類吧;
三種類型的窗口,應該使用代碼重用,只有部分不同
調試程序的時候出現了異常,什麼異常呢,下標越界,爲什麼會下表越界呢;
Exception in thread"main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
atjava.util.ArrayList.RangeCheck(ArrayList.java:547)
atjava.util.ArrayList.remove(ArrayList.java:387)
atcom.heima.jianjian.Test.main(Test.java:12)
沒生成號之前你就去取號了,或者,取得比生的還快,於是你寫了
public synchronized Integer fetchServiceNumber()
{
return lineQueueNumber.remove(0);//拿走服務號,一舉兩得
}
要考慮到null的情況,應該這樣寫
public synchronized Integer fetchServiceNumber()
{
Integer Null = null;
if(lineQueueNumber.size() >0)
return lineQueueNumber.remove(0);//拿走服務號,一舉兩得
else
returnNull;滿足返回的是一個對象就可以了
}
然後於是,你成功了!!!!!!!!!!!
但是呢,還是有一點不完美,打印輸出有點亂,這其實是涉及到了線程的選擇問題;
被Synchronized關鍵字修飾的方法,每次只能被一個線程調用,(比如說生成號碼,和取號碼,不能三個一起來取和生吧)
由於線程選擇的問題,最好把輸出或者輸入寫在索的後面,也就是帶關鍵字方法的後面,這樣就顯得輸出乾淨一點:
Integer clientNum =NumberMerchine.getMerchineInstance()
.getCommonClient().fetchServiceNumber();
System.out.println(windowNum +"號" +windowType + "正在等待服務;");
爲什麼亂序:cup在執行完生成號碼的線程後,本來該執行打印的語句,但是它有可能發現其他的線程空置,去調用其他符合條件的線程了。所以纔會亂序;
如果你想讓線程和輸出一起出來的話,就要把這兩個都鎖住,但是
有可能死鎖;
交通燈管理系統篇
匿名內部中的方法要想訪問外部類的成員變量,則外部類的成員變量必須是final的,如果名字相同的話,訪問的形式是
OutClass.this.name
void execute(Runnablecommand) 另起一個線程來實現新增車輛功能使用新特性來世先線程調用車輛功能
* 看一下這個方法接受的是一個Runnable接口對象,顯然是要用一個實現接口的匿名內部類;
* 必須重寫其中的run方法,在run方法中實現,增加車輛的操作
*/
// 把任務交給一個線程池,在線程池中找一個線程來執行這個功能,
小技巧,對於枚舉構造枚舉的情況,可以將枚舉中的枚舉變成字符串,在需要變回枚舉時使用方法
Enum.valueof(Stringenum)將會返回枚舉類型
設計一個Lamp類來表示一個交通燈,每個交通燈都維護一個狀態,亮(綠),不亮(紅),每個交通燈要有變亮或者變黑的方法,並且能返回自己的亮黑狀態
總共有12條路線,所以,系統中總共要產生12個交通燈,右拐彎的路線本來不受燈的控制,但是爲了讓程序採用統一的處理方式,故假設出四個右拐彎的燈,只是這些燈永遠爲常亮。
除了右拐彎方向的其他八條路線的燈,他們是兩兩成對的,可以歸爲4組,所以,在編程處理時,只要從這四組中各取一個等,對這四個等一次輪番變亮,與這四個方向燈方向對應的燈則隨之變化,因此Lampl類中要有一個變量來記住自己相反方向的燈,在一個Lamp對象的變亮和變黑方法中,路對應方向的燈也變亮和變黑,每個燈黑時,都伴隨這下一個燈的變亮,Lamp類中還用一個變量來記住自己的下一個燈。
無論在程序的什麼地方去獲得某個方向的燈時,每次獲得的都是同一個實例對象,所以Lamp類改用枚舉來做hi很方便的,永遠都只代表12個方向的燈的實例對象。