------- android培訓、java培訓、期待與您交流! ----------
24.01 JDK5之后的Lock鎖的概述和使用雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock
public interface Lock:Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition 對象。
例:
1 public class SellTicket implements Runnable 2 { 3 // 定義票 4 PRivate int tickets = 100; 5 6 // 定義鎖對象 7 private Lock lock = new ReentrantLock(); 8 9 @Override10 public void run() 11 {12 while (true) 13 {14 try 15 {16 // 加鎖17 lock.lock();18 if (tickets > 0) 19 {20 try 21 {22 Thread.sleep(100);23 } 24 catch (InterruptedException e) 25 {26 e.printStackTrace();27 }28 System.out.println(Thread.currentThread().getName()29 + "正在出售第" + (tickets--) + "張票");30 }31 } 32 finally 33 {34 // 確保鎖一定釋放35 lock.unlock();36 }37 }38 }39 }24.02 死鎖問題概述和使用
同步弊端:效率低,如果出現了同步嵌套,就容易產生死鎖問題
死鎖問題:是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象
同步代碼塊的嵌套案例
1 public class DieLock extends Thread 2 { 3 private boolean flag; 4 5 // 創建兩把鎖對象 6 public static final Object objA = new Object(); 7 public static final Object objB = new Object(); 8 9 public DieLock(boolean flag) 10 {11 this.flag = flag;12 }13 14 @Override15 public void run() 16 {17 if (flag) 18 {19 synchronized (objA) 20 {21 System.out.println("if objA");22 synchronized (objB) 23 {24 System.out.println("if objB");25 }26 }27 } 28 else 29 {30 synchronized (objB) 31 {32 System.out.println("else objB");33 synchronized (objA) 34 {35 System.out.println("else objA");36 }37 }38 }39 }40 }理想狀態下運行結果:
if objAif objBelse objBelse objA
死鎖運行結果:
if objAelse objB
24.03 生產者消費者問題代碼1
線程間通訊:多個線程在處理同一資源,但是任務卻不同
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 //創建資源 6 Student s = new Student(); 7 8 //設置和獲取的類 9 SetThread st = new SetThread(s);10 GetThread gt = new GetThread(s);11 12 //線程類13 Thread t1 = new Thread(st);14 Thread t2 = new Thread(gt);15 16 //啟動線程17 t2.start();18 t1.start();19 }20 }問題:t1線程是用來設置學生的信息,而t2線程是用來獲取學生的信息,理想狀態下t1先運行t2后運行沒有問題,但如果t2先運行則獲取的結果是null---0
24.04 生產者消費者題代碼2并解決線程安全問題問題:加入了循環和判斷,給出不同的值,這個時候產生了新的問題
1:同一個數據出現多次
2:姓名和年齡不匹配
原因:
1:同一個數據出現多次,因為CPU的一點點時間片的執行權,就足夠執行很多次
2:姓名和年齡不匹配,線程運行的隨機性
解決方案:加鎖
1:不同種類的線程都要加鎖
2:不同種類的線程加的鎖必須是同一把
例:
1 public class SetThread implements Runnable 2 { 3 private Student s; 4 private int i = 0; 5 public SetThread(Student s) 6 { 7 this.s = s; 8 } 9 10 @Override11 public void run() 12 {13 while(true)14 {15 synchronized (s) 16 {17 if(i % 2 == 0)18 {19 s.name = "旺財";20 s.age = 7;21 }22 else23 {24 s.name = "小強強強";25 s.age = 3;26 }27 i++;28 }29 }30 }31 32 }33 34 35 public class GetThread implements Runnable36 {37 private Student s;38 39 public GetThread(Student s) 40 {41 this.s = s;42 }43 44 @Override45 public void run() 46 {47 while(true)48 {49 synchronized (s) 50 {51 System.out.println(s.name + "---" + s.age);52 }53 }54 }55 }24.05 生產者消費者之等待喚醒機制思路
思路:
生產者:先看是否有數據,有就等待,沒有就生產,生產完之后通知消費者消費
消費者:先看是否有數據,有就消費,沒有就等待,等待時通知生產者生產
Java提供了等待喚醒機制
24.06 生產者消費者之等待喚醒機制代碼實現通過Java提供的等待喚醒機制來實現一次一個輸出
等待喚醒:
Object類中提供了三個方法:
wait():等待
notify():喚醒單個線程
notifyAll():喚醒所有線程
為什么操作線程的方法wait notify notifyAll定義在Object類中?
因為這些方法是監視器的方法,監視器其實就是鎖。鎖可以是任意的對象,任意的對象調用的方法一定定義在Object類中。
1 public class SetThread implements Runnable 2 { 3 private Student s; 4 private int x = 0; 5 6 public SetThread(Student s) 7 { 8 this.s = s; 9 }10 11 @Override12 public void run() 13 {14 while (true) 15 {16 synchronized (s) 17 {18 //判斷有沒有19 if(s.flag)20 {21 try 22 {23 //有就等待24 s.wait();//等待立即釋放鎖25 } catch (InterruptedException e)26 {27 e.printStackTrace();28 }29 }30 31 if (x % 2 == 0) 32 {33 s.name = "小明";34 s.age = 17;35 } 36 else 37 {38 s.name = "旺財";39 s.age = 3;40 }41 x++;42 43 //修改標記44 s.flag = true;45 //喚醒線程46 s.notify(); 47 }48 }49 }50 }51 52 53 public class GetThread implements Runnable54 {55 private Student s;56 57 public GetThread(Student s)58 {59 this.s = s;60 }61 62 @Override63 public void run() 64 {65 while (true) 66 {67 synchronized (s) 68 {69 if(!s.flag)70 {71 try 72 {73 s.wait(); //等待立即釋放鎖74 } 75 catch (InterruptedException e) 76 {77 e.printStackTrace();78 }79 }80 81 System.out.println(s.name + "---" + s.age);82 //修改標記83 s.flag = false;84 //喚醒線程85 s.notify();86 }87 }88 }89 }24.07 線程的狀態轉換圖及常見執行情況

Java中使用ThreadGroup來表示線程組,它可以對一批線程進行分類管理,Java允許程序直接對線程組進行控制。
方法:public final ThreadGroup getThreadGroup()
返回該線程所屬的線程組。如果該線程已經終止(停止運行),該方法則返回 null。
構造方法:public Thread(ThreadGroup group,Runnable target,String name)
分配新的 Thread 對象,以便將 target 作為其運行對象,將指定的 name 作為其名稱,并作為 group 所引用的線程組的一員。
默認情況下,所有的線程都屬于主線程組。
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 method2(); 6 } 7 private static void method1() 8 { 9 MyRunnable my = new MyRunnable();10 Thread t1 = new Thread(my);11 Thread t2 = new Thread(my);12 // 線程類里面的方法:public final ThreadGroup getThreadGroup()13 ThreadGroup tg1 = t1.getThreadGroup();14 ThreadGroup tg2 = t2.getThreadGroup();15 // 線程組類里面的方法:public final String getName()16 //獲取線程組名稱17 String name1 = tg1.getName();//main18 String name2 = tg2.getName();//main19 System.out.println(name1);20 System.out.println(name2);21 // 線程默認情況下屬于main線程組,所有的線程都屬于同一個組22 System.out.println(Thread.currentThread().getThreadGroup().getName());//main23 }24 private static void method2() 25 {26 // ThreadGroup(String name)27 ThreadGroup tg = new ThreadGroup("新的線程組");28 29 MyRunnable my = new MyRunnable();30 // Thread(ThreadGroup group, Runnable target, String name)31 Thread t1 = new Thread(tg, my);32 Thread t2 = new Thread(tg, my);33 34 System.out.println(t1.getThreadGroup().getName());//新的線程組35 System.out.println(t2.getThreadGroup().getName());//新的線程組36 37 //通過組名稱設置后臺線程,表示該組的線程都是后臺線程38 tg.setDaemon(true);39 }40 }24.09 生產者消費者之等待喚醒機制代碼優化
1 public class Student 2 { 3 private String name; 4 private int age; 5 // 默認情況是沒有數據,如果是true,說明有數據 6 private boolean flag; 7 8 public synchronized void set(String name, int age) 9 { 10 // 如果有數據,就等待 11 if (this.flag) 12 { 13 try 14 { 15 this.wait(); 16 } 17 catch (InterruptedException e) 18 { 19 e.printStackTrace(); 20 } 21 } 22 23 // 設置數據 24 this.name = name; 25 this.age = age; 26 27 // 修改標記 28 this.flag = true; 29 this.notify(); 30 } 31 32 public synchronized void get() 33 { 34 // 如果沒有數據,就等待 35 if (!this.flag) 36 { 37 try 38 { 39 this.wait(); 40 } 41 catch (InterruptedException e) 42 { 43 e.printStackTrace(); 44 } 45 } 46 47 // 獲取數據 48 System.out.println(this.name + "---" + this.age); 49 50 // 修改標記 51 this.flag = false; 52 this.notify(); 53 } 54 } 55 -------------------------------------------------------------------- 56 public class SetThread implements Runnable 57 { 58 private Student s; 59 private int x = 0; 60 61 public SetThread(Student s) 62 { 63 this.s = s; 64 } 65 66 @Override 67 public void run() 68 { 69 while (true) 70 { 71 if (x % 2 == 0) 72 { 73 s.set("xiaoming", 17); 74 } 75 else 76 { 77 s.set("旺財", 7); 78 } 79 x++; 80 } 81 } 82 } 83 -------------------------------------------------------------------- 84 public class GetThread implements Runnable 85 { 86 private Student s; 87 88 public GetThread(Student s) 89 { 90 this.s = s; 91 } 92 93 @Override 94 public void run() 95 { 96 while (true) 97 { 98 s.get(); 99 }100 }101 }102 --------------------------------------------------------------------103 public class Practice 104 {105 public static void main(String[] args)106 {107 //創建資源108 Student s = new Student();109 110 //設置和獲取的類111 SetThread st = new SetThread(s);112 GetThread gt = new GetThread(s);113 114 //線程類115 Thread t1 = new Thread(st);116 Thread t2 = new Thread(gt);117 118 //啟動線程119 t1.start();120 t2.start();121 }122 }24.10 線程池的概述和使用
程序啟動一個新線程成本是比較高的,因為它涉及到要與操作系統進行交互。而使用線程池可以很好的提高性能,尤其是當程序中要創建大量生存期很短的線程時,更應該考慮使用線程池。
線程池里的每一個線程代碼結束后,并不會死亡,而是再次回到線程池中成為空閑狀態,等待下一個對象來使用。
在JDK5之前,我們必須手動實現自己的線程池,從JDK5開始,Java內置支持線程池
JDK5新增了一個Executors工廠類來產生線程池,有如下幾個方法
1.public static ExecutorService newCachedThreadPool():創建一個具有緩存功能的線程池
創建一個可根據需要創建新線程的線程池,但是在以前構造的線程可用時將重用它們。
2.public static ExecutorService newFixedThreadPool(int nThreads):創建一個可重用的,具有固定線程數的線程池
創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。
3.public static ExecutorService newSingleThreadExecutor():創建一個只有單線程的線程池,相當于上個方法的參數是1
創建一個使用單個 worker 線程的 Executor,以無界隊列方式來運行該線程。
這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者Callable對象代表的線程。
接口 ExecutorService提供了如下方法
1.Future<?> submit(Runnable task)
提交一個 Runnable 任務用于執行,并返回一個表示該任務的 Future。該 Future 的 get 方法在成功完成時將會返回 null。
2.<T> Future<T> submit(Callable<T> task)
提交一個返回值的任務用于執行,返回一個表示任務的未決結果的 Future。
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // 創建一個線程池對象,控制要創建幾個線程對象。 6 // public static ExecutorService newFixedThreadPool(int nThreads) 7 ExecutorService pool = Executors.newFixedThreadPool(2); 8 9 // 可以執行Runnable對象或者Callable對象代表的線程10 pool.submit(new MyRunnable());11 pool.submit(new MyRunnable());12 13 //結束線程池14 pool.shutdown();15 }16 }24.11 多線程方式3的代碼實現
1 //這里指定的泛型其實是call()方法的返回值類型 2 public class MyCallable implements Callable 3 { 4 @Override 5 public Object call() throws Exception 6 { 7 for (int x = 0; x < 100; x++) 8 { 9 System.out.println(Thread.currentThread().getName() +":"+ x);10 }11 return null;12 }13 }14 15 16 public class Practice 17 {18 public static void main(String[] args)19 {20 //創建線程池對象21 ExecutorService pool = Executors.newFixedThreadPool(2);22 23 //可以執行Runnable對象或者Callable對象代表的線程24 pool.submit(new MyCallable());25 pool.submit(new MyCallable());26 27 //結束28 pool.shutdown();29 }30 }好處:可以有返回值、可以拋出異常
弊端:代碼比較復雜,所以一般不用
24.12 多線程方式3的求和案例 1 public class MyCallable implements Callable<Integer> 2 { 3 private int number; 4 public MyCallable(int number) 5 { 6 this.number = number; 7 } 8 @Override 9 public Integer call() throws Exception 10 {11 int sum = 0;12 for (int x = 1; x <= number; x++) 13 {14 sum += x;15 }16 return sum;17 }18 }19 20 public class Practice 21 {22 public static void main(String[] args) throws InterruptedException, ExecutionException23 {24 //創建線程池對象25 ExecutorService pool = Executors.newFixedThreadPool(2);26 27 //可以執行Runnable對象或者Callable對象代表的線程28 Future<Integer> f1 = pool.submit(new MyCallable(6));29 Future<Integer> f2 = pool.submit(new MyCallable(100));30 31 Integer i1 = f1.get();32 Integer i2 = f2.get();33 System.out.println(i1);34 System.out.println(i2);35 //結束36 pool.shutdown();37 }38 }24.13 匿名內部類的方式實現多線程程序
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // 繼承Thread類來實現多線程 6 new Thread() 7 { 8 public void run() 9 {10 for (int x = 0; x < 100; x++) 11 {12 System.out.println(Thread.currentThread().getName() + ":"+ x);13 }14 }15 }.start();16 17 // 實現Runnable接口來實現多線程18 new Thread(new Runnable() 19 {20 @Override21 public void run() 22 {23 for (int x = 0; x < 100; x++) 24 {25 System.out.println(Thread.currentThread().getName() + ":"+ x);26 }27 }28 }) 29 {}.start();30 31 // 更有難度的32 new Thread(new Runnable() 33 {34 @Override35 public void run() 36 {37 for (int x = 0; x < 100; x++) 38 {39 System.out.println("hello" + ":" + x);40 }41 }42 }) 43 {44 public void run() 45 {46 for (int x = 0; x < 100; x++) 47 {48 //子類對象運行49 System.out.println("world" + ":" + x);50 }51 }52 }.start();53 }54 }24.14 定時器的概述和使用
定時器是一個應用十分廣泛的線程工具,可用于調度多個定時任務以后臺線程的方式執行。在Java中,可以通過Timer和TimerTask類來實現定義調度的功能
類 Timer:一種工具,線程用其安排以后在后臺線程中執行的任務。可安排任務執行一次,或者定期重復執行。
構造方法:
public Timer()
創建一個新計時器。相關的線程不作為守護程序運行。
方法:
1.public void schedule(TimerTask task,long delay)
安排在指定延遲后執行指定的任務。
2.public void schedule(TimerTask task,long delay,long period)
安排指定的任務從指定的延遲后開始進行重復的固定延遲執行。
類 TimerTask:由 Timer 安排為一次執行或重復執行的任務。
1.public abstract void run()
此計時器任務要執行的操作。
2.public boolean cancel()
取消此計時器任務。如果任務安排為一次執行且還未運行,或者尚未安排,則永遠不會運行。
開發中:Quartz是一個完全由java編寫的開源調度框架。
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // 創建定時器對象 6 Timer t = new Timer(); 7 //結束任務 8 t.schedule(new MyTask(t), 3000); 9 }10 }11 //做一個任務12 class MyTask extends TimerTask 13 {14 15 private Timer t;16 17 public MyTask(){}18 19 public MyTask(Timer t)20 {21 this.t = t;22 }23 24 @Override25 public void run() 26 {27 System.out.println("任務執行");28 t.cancel();29 }30 }24.15 定時任務的多次執行代碼體現
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // 創建定時器對象 6 Timer t = new Timer(); 7 t.schedule(new MyTask(), 3000, 2000); 8 } 9 }10 //做一個任務11 class MyTask extends TimerTask 12 {13 @Override14 public void run() 15 {16 System.out.println("任務執行");17 }18 }24.16 定時刪除指定的帶內容目錄
1 public class Practice 2 { 3 public static void main(String[] args) throws ParseException 4 { 5 // 創建定時器對象 6 Timer t = new Timer(); 7 8 String s = "2014-11-27 15:45:00"; 9 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");10 Date d = sdf.parse(s);11 12 t.schedule(new DeleteFolder(), d);13 }14 }15 //做一個任務16 class DeleteFolder extends TimerTask17 {18 @Override19 public void run() 20 {21 File srcFolder = new File("demo");22 deleteFolder(srcFolder);23 }24 25 // 遞歸刪除目錄26 public void deleteFolder(File srcFolder) 27 {28 File[] fileArray = srcFolder.listFiles();29 if (fileArray != null) 30 {31 for (File file : fileArray) 32 {33 if (file.isDirectory()) 34 {35 deleteFolder(file);36 } 37 else 38 {39 System.out.println(file.getName() + ":" + file.delete());40 }41 }42 System.out.println(srcFolder.getName() + ":" + srcFolder.delete());43 }44 }45 }24.17 多線程常見的面試題
1:多線程有幾種實現方案,分別是哪幾種?
兩種。繼承Thread類、實現Runnable接口
擴展一種:實現Callable接口。這個得和線程池結合。
2:同步有幾種方式,分別是什么?
兩種。同步代碼塊、同步方法
3:啟動一個線程是run()還是start()?它們的區別?
start();
run():封裝了被線程執行的代碼,直接調用僅僅是普通方法的調用
start():啟動線程,并由JVM自動調用run()方法
4:sleep()和wait()方法的區別
sleep():必須指時間;不釋放鎖。
wait():可以不指定時間,也可以指定時間;釋放鎖。
5:為什么wait(),notify(),notifyAll()等方法都定義在Object類中
因為這些方法的調用是依賴于鎖對象的,而同步代碼塊的鎖對象是任意鎖。而Object代表任意的對象,所以,定義在這里面。
6:線程的生命周期圖
新建 -- 就緒 -- 運行 -- 死亡
新建 -- 就緒 -- 運行 -- 阻塞 -- 就緒 -- 運行 -- 死亡
畫圖解釋
24.18 面向對象的常見設計原則面向對象思想的設計原則:
1.單一職責原則:
其實就是開發人員經常說的”高內聚,低耦合”
也就是說,每個類應該只有一個職責,對外只能提供一種功能,而引起類變化的原因應該只有一個。在設計模式中,所有的設計模式都遵循這一原則。
2.開閉原則
核心思想是:一個對象對擴展開放,對修改關閉。
其實開閉原則的意思就是:對類的改動是通過增加代碼進行的,而不是修改現有代碼。
3.里氏替換原則
核心思想:在任何父類出現的地方都可以用它的子類來替代。
其實就是說:同一個繼承體系中的對象應該有共同的行為特征。
4.依賴注入原則
核心思想:要依賴于抽象,不要依賴于具體實現。
其實就是說:在應用程序中,所有的類如果使用或依賴于其他的類,則應該依賴這些其他類的抽象類,而不是這些其他類的具體類。為了實現這一原則,就要求我們在編程的時候針對抽象類或者接口編程,而不是針對具體實現編程。
5.接口分離原則
核心思想:不應該強迫程序依賴它們不需要使用的方法。
其實就是說:一個接口不需要提供太多的行為,一個接口應該只提供一種對外的功能,不應該把所有的操作都封裝到一個接口中。
6.迪米特原則
核心思想:一個對象應當對其他對象盡可能少的了解
其實就是說:降低各個對象之間的耦合,提高系統的可維護性。在模塊之間應該只通過接口編程,而不理會模塊的內部工作原理,它可以使各個模塊耦合度降到最低,促進軟件的復用
24.19 設計模式的概述和分類設計模式概述:設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
設計模式不是一種方法和技術,而是一種思想
設計模式和具體的語言無關,學習設計模式就是要建立面向對象的思想,盡可能的面向接口編程,低耦合,高內聚,使設計的程序可復用,學習設計模式能夠促進對面向對象思想的理解,反之亦然。它們相輔相成
設計模式的幾個要素:
名字必須有一個簡單,有意義的名字
問題描述在何時使用模式
解決方案描述設計的組成部分以及如何解決問題
效果描述模式的效果以及優缺點
設計模式的分類
創建型模式對象的創建
結構型模式對象的組成(結構)
行為型模式對象的行為
創建型模式:簡單工廠模式、工廠方法模式、抽象工廠模式、建造者模式、原型模式、單例模式。(6個)
結構型模式:外觀模式、適配器模式、代理模式、裝飾模式、橋接模式、組合模式、享元模式。(7個)
行為型模式:模版方法模式、觀察者模式、狀態模式、職責鏈模式、命令模式、訪問者模式、策略模式、備忘錄模式、迭代器模式、解釋器模式。(10個)
24.20 簡單工廠模式概述和使用簡單工廠模式概述:又叫靜態工廠方法模式,它定義一個具體的工廠類負責創建一些類的實例
優點:客戶端不需要在負責對象的創建,從而明確了各個類的職責
缺點:這個靜態工廠類負責所有對象的創建,如果有新的對象增加,或者某些對象的創建方式不同,就需要不斷的修改工廠類,不利于后期的維護
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // // 具體類調用 6 // Dog d = new Dog(); 7 // d.eat(); 8 // Cat c = new Cat(); 9 // c.eat();10 // System.out.println("------------");11 12 // //使用工廠創建對象13 // Dog dd = AnimalFactory.createDog();14 // Cat cc = AnimalFactory.createCat();15 // dd.eat();16 // cc.eat();17 // System.out.println("------------");18 19 // 工廠改進后20 Animal a = AnimalFactory.createAnimal("dog");21 a.eat();22 a = AnimalFactory.createAnimal("cat");23 a.eat();24 25 // NullPointerException26 a = AnimalFactory.createAnimal("pig");27 if (a != null) 28 {29 a.eat();30 } 31 else 32 {33 System.out.println("沒有這種動物");34 }35 }36 }37 abstract class Animal 38 {39 public abstract void eat();40 }41 42 class Dog extends Animal 43 {44 @Override45 public void eat() 46 {47 System.out.println("狗吃肉");48 }49 }50 class Cat extends Animal 51 {52 53 @Override54 public void eat() 55 {56 System.out.println("貓吃魚");57 }58 }59 60 //工廠類61 class AnimalFactory62 {63 private AnimalFactory()64 {}65 66 // public static Dog createDog() {67 // return new Dog();68 // }69 //70 // public static Cat createCat() {71 // return new Cat();72 // }73 74 public static Animal createAnimal(String type) 75 {76 if ("dog".equals(type)) 77 {78 return new Dog();79 } 80 else if ("cat".equals(type)) 81 {82 return new Cat();83 } 84 else 85 {86 return null;87 }88 }89 }24.21 工廠方法模式的概述和使用
工廠方法模式概述:工廠方法模式中抽象工廠類負責定義創建對象的接口,具體對象的創建工作由繼承抽象工廠的具體類實現。
優點:客戶端不需要在負責對象的創建,從而明確了各個類的職責,如果有新的對象增加,只需要增加一個具體的類和具體的工廠類即可,不影響已有的代碼,后期維護容易,增強了系統的擴展性
缺點:需要額外的編寫代碼,增加了工作量
例:
1 public class Practice 2 { 3 public static void main(String[] args) 4 { 5 // 創建狗 6 Factory f = new DogFactory(); 7 Animal a = f.createAnimal(); 8 a.eat(); 9 System.out.println("-------");10 11 //創建貓12 f = new CatFactory();13 a = f.createAnimal();14 a.eat();15 }16 }17 //動物類18 abstract class Animal 19 {20 public abstract void eat();21 }22 //狗類23 class Dog extends Animal 24 {25 @Override26 public void eat() 27 {28 System.out.println("狗吃肉");29 }30 }31 //貓類32 class Cat extends Animal 33 {34 35 @Override36 public void eat() 37 {38 System.out.println("貓吃魚");39 }40 }41 //工廠42 interface Factory 43 {44 public abstract Animal createAnimal();45 }46 //創建Cat的工廠47 class CatFactory implements Factory 48 {49 @Override50 public Animal createAnimal() 51 {52 return new Cat();53 }54 55 }56 //創建Dog的工廠57 class DogFactory implements Factory 58 {59 @Override60 public Animal createAnimal() 61 {62 return new Dog();63 }64 }24.22 單例模式之餓漢式
單例設計模式概述:單例模式就是要確保類在內存中只有一個對象,該實例必須自動創建,并且對外提供。
優點:在系統內存中只存在一個對象,因此可以節約系統資源,對于一些需要頻繁創建和銷毀的對象單例模式無疑可以提高系統的性能。
缺點:沒有抽象層,因此擴展很難。職責過重,在一定程序上違背了單一職責
保證對象唯一性的條件:
1.不允許其他程序用new創建該類對象
2.在該類創建一個本類實例
3.對外提供一個方法讓其他程序可以獲取該對象
保證對象唯一性的步驟:
1.私有化該類的構造函數
2.通過new在本類中創建一個本類的對象
3.定義一個公有的方法,將創建的對象返回
例:
1 public class Student 2 { 3 // 構造私有 4 private Student() 5 {} 6 7 // 靜態方法只能訪問靜態成員變量,加靜態 8 // 為了不讓外界直接訪問修改這個值,加private 9 private static Student s = new Student();10 11 // 提供公共的訪問方式12 // 為了保證外界能夠直接使用該方法,加靜態13 public static Student getStudent() 14 {15 return s;16 }17 }24.23 單例模式之懶漢式
例:
1 class Teacher 2 { 3 private Teacher() 4 {} 5 6 private static Teacher t = null; 7 8 public static Teacher getTeacher() 9 {10 if (t == null) //該語句在被多線程操作時存在安全隱患11 {12 t = new Teacher();13 }14 return t;15 }16 }上面的單例模式在被多線程操作時是有問題的
解決單例模式延遲加載涉及的多線程安全問題
方案1:使用同步方法(多線程操作時每次都需要判斷鎖,效率低)
1 class Teacher 2 { 3 private Teacher() 4 {} 5 6 private static Teacher t = null; 7 //同步方法 8 public synchronized static Teacher getTeacher() 9 {10 if (t == null) 11 {12 t = new Teacher();13 }14 return t;15 }16 }方案2:使用同步代碼塊
1 class Teacher 2 { 3 private Teacher() 4 {} 5 6 private static Teacher t = null; 7 8 public static Teacher getTeacher() 9 {10 if (t == null) //提高效率11 {12 //同步代碼塊13 synchronized (t) //解決安全問題14 {15 if(t == null)16 t = new Teacher();17 }18 }19 return t;20 }21 }24.24 單例模式的Java代碼體現Runtime類
Runtime類概述:每個Java應用程序都有一個Runtime類實例,使應用程序能夠與其運行的環境相連接。
可以通過 getRuntime 方法獲取當前運行時。應用程序不能創建自己的 Runtime 類實例。(單例設計模式餓漢式)
Runtime類使用
public Process exec(String command)throws IOException
在單獨的進程中執行指定的字符串命令。
例:
Runtime r = Runtime.getRuntime();r.exec("notepad");//打開記事本新聞熱點
疑難解答