




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、單例模式完全剖析概要 單例模式是最簡單的設(shè)計(jì)模式之一,但是對于Java的開發(fā)者來說,它卻有很多缺陷。在本月的專欄中,David Geary探討了單例模式以及在面對多線程(multithreading)、類裝載器(classloaders)和序列化(serialization)時如何處理這些缺陷。 單例模式適合于一個類只有一個實(shí)例的情況,比如窗口管理器,打印緩沖池和文件系統(tǒng),它們都是原型的例子。典型的情況是,那些對象的類型被遍及一個軟件系統(tǒng)的不同對象訪問,因此需要一個全局的訪問指針,這便是眾所周知的單例模式的應(yīng)用。當(dāng)然這只有在你確信你不再需要任何多于一個的實(shí)例的情況下。 單例模式的用意在于前一段
2、中所關(guān)心的。通過單例模式你可以: 確保一個類只有一個實(shí)例被建立 提供了一個對對象的全局訪問指針 在不影響單例類的客戶端的情況下允許將來有多個實(shí)例 盡管單例設(shè)計(jì)模式如在下面的圖中的所顯示的一樣是最簡單的設(shè)計(jì)模式,但對于粗心的Java開發(fā)者來說卻呈現(xiàn)出許多缺陷。這篇文章討論了單例模式并揭示了那些缺陷。 注意:你可以從Resources下載這篇文章的源代碼。 單例模式 在設(shè)計(jì)模式一書中,作者這樣來敘述單例模式的:確保一個類只有一個實(shí)例并提供一個對它的全局訪問指針。 下圖說明了單例模式的類圖。 (圖1) 單例模式的類圖 正如你在上圖中所看到的,這不是單例模式的完整部分。此圖中單例類保持了一個對唯一的單
3、例實(shí)例的靜態(tài)引用,并且會從靜態(tài)getInstance()方法中返回對那個實(shí)例的引用。 例1顯示了一個經(jīng)典的單例模式的實(shí)現(xiàn)。 例1.經(jīng)典的單例模式 Java代碼 1. public class ClassicSingleton 2. private static ClassicSingleton instance = null; 3. 4.
4、60; protected ClassicSingleton() 5. / Exists only to defeat instantiation. 6. 7. public static ClassicSingleton getIn
5、stance() 8. if(instance = null) 9. instance = new ClassicSingleton(); 10.
6、160; 11. return instance; 12. 13. public class ClassicSingleton private static ClassicSingleton instance = null; protected ClassicSingleton() / Exists only to defeat instantiation.
7、public static ClassicSingleton getInstance() if(instance = null) instance = new ClassicSingleton(); return instance; 在例1中的單例模式的實(shí)現(xiàn)很容易理解。ClassicSingleton類保持了一個對單獨(dú)的單例實(shí)例的靜態(tài)引用,并且從靜態(tài)方法getInstance()中返回那個引用。 關(guān)于ClassicSingleton類,有幾個讓我們感興趣的地方。首先,ClassicSingleton使用了一個眾所周知的懶漢式實(shí)例化去創(chuàng)建那個單例類的引用;結(jié)果,這個單例類的實(shí)例直到getInst
8、ance()方法被第一次調(diào)用時才被創(chuàng)建。這種技巧可以確保單例類的實(shí)例只有在需要時才被建立出來。其次,注意ClassicSingleton實(shí)現(xiàn)了一個protected的構(gòu)造方法,這樣客戶端不能直接實(shí)例化一個ClassicSingleton類的實(shí)例。然而,你會驚奇的發(fā)現(xiàn)下面的代碼完全合法: Java代碼 1. public class SingletonInstantiator 2. public SingletonInstantiator()
9、160; 3. ClassicSingleton instance = ClassicSingleton.getInstance(); 4. ClassicSingleton anotherInstance = 5. new ClassicSingleton(); 6. . &
10、#160; 7. 8. public class SingletonInstantiator public SingletonInstantiator() ClassicSingleton instance = ClassicSingleton.getInstance(); ClassicSingleton anotherInstance = new ClassicSingleton(); . 測試單例模式 接下來,我使用與log4j相對應(yīng)的JUnit來測試單例類,它會貫穿在
11、這篇文章余下的部分。如果你對JUnit或log4j不很熟悉,請參考相關(guān)資源。 例2是一個用JUnit測試?yán)?的單例模式的案例: 例2.一個單例模式的案例 Java代碼 . 5. public class SingletonTest extends TestCase 6. private ClassicSingleton sone = null, stwo
12、60;= null; 7. private static Logger logger = Logger.getRootLogger(); 8. 9. public SingletonTest(String name) 10.
13、160; super(name); 11. 12. public void setUp() 13. ("getting singleton."); 14.
14、 sone = ClassicSingleton.getInstance(); 15. (".got singleton: " + sone); 16. 17. ("getting
15、;singleton."); 18. stwo = ClassicSingleton.getInstance(); 19. (".got singleton: " + stwo); 20.
16、; 21. public void testUnique() 22. ("checking singletons for equality"); 23. Assert.assertEquals(true, so
17、ne = stwo); 24. 25. public class SingletonTest extends TestCase private ClassicSingleton sone = null, stwo = null; private static Logger logger = Logger.getRootLogger(); public SingletonTest(String name) super(name);
18、 public void setUp() ("getting singleton."); sone = ClassicSingleton.getInstance(); (".got singleton: " + sone); ("getting singleton."); stwo = ClassicSingleton.getInstance(); (".got singleton: " + stwo); public void
19、 testUnique() ("checking singletons for equality"); Assert.assertEquals(true, sone = stwo); 例2兩次調(diào)用ClassicSingleton.getInstance(),并且把返回的引用存儲在成員變量中。方法testUnique()會檢查這些引用看它們是否相同。例3是這個測試案例的輸出: 例3.是這個測試案例的輸出 Java代碼 1. Buildfile: build.xml 2.
20、 3. init: 4. echo Build 20030414 (14-04-2003 03:08) 5. 6. compile: 7. 8. run-test-text: 9. ja
21、va .INFO main: bgetting singleton./b 10. java INFO main: bcreated singleton:/b Singletone86f41 11. java INFO main: .got singleton: Sin
22、gletone86f41 12. java INFO main: bgetting singleton./b 13. java INFO main: .got singleton: Singletone86f41 14.
23、java INFO main: checking singletons for equality 15. 16. java Time: 0.032 17. 18. java OK (1 test)&
24、#160; Buildfile: build.xml init: echo Build 20030414 (14-04-2003 03:08) compile: run-test-text: java .INFO main: bgetting singleton./b java INFO main: bcreated singleton:/b Singletone86f41 java INFO main: .got singleton: Singletone86f41 java INFO main: bgetting singleton./b java INFO main: .got
25、 singleton: Singletone86f41 java INFO main: checking singletons for equality java Time: 0.032 java OK (1 test)正如前面的清單所示,例2的簡單測試順利通過-通過ClassicSingleton.getInstance()獲得的兩個單例類的引用確實(shí)相同;然而,你要知道這些引用是在單線程中得到的。下面的部分著重于用多線程測試單例類。 多線程因素的考慮 在例1中的ClassicSingleton.getInstance()方法由于下面的代碼而不是線程安全的: Java代碼 1. 1:
26、;if(instance = null) 2. 2: instance = new Singleton(); 3. 3: 1: if(instance = null) 2: instance = new Singleton(); 3: 如果一個線程在第二行的賦值語句發(fā)生之前切換,那么成員變量instance仍然是null,然后另一個線程可能接下來進(jìn)入到if塊中。在這種情況
27、下,兩個不同的單例類實(shí)例就被創(chuàng)建。不幸的是這種假定很少發(fā)生,這樣這種假定也很難在測試期間出現(xiàn)(譯注:在這可能是作者對很少出現(xiàn)這種情況而導(dǎo)致無法測試從而使人們放松警惕而感到嘆惜)。為了演示這個線程輪換,我得重新實(shí)現(xiàn)例1中的那個類。例4就是修訂后的單例類: 例4.人為安排的方式 Java代碼 1.2. 3. public class Singleton 4. private static Singleton singleton&
28、#160;= null; 5. private static Logger logger = Logger.getRootLogger(); 6. private static boolean firstThread = true; 7. 8. protect
29、ed Singleton() 9. / Exists only to defeat instantiation. 10. 11. public static Singleton getInstance() 12.
30、60; if(singleton = null) 13. simulateRandomActivity(); 14. singleton = new Singleton(); 15.
31、0; 16. ("created singleton: " + singleton); 17. return singleton; 18. 19. private
32、0;static void simulateRandomActivity() 20. try 21. if(firstThread) 22. fir
33、stThread = false; 23. ("sleeping."); 24. 25. / This nap shoul
34、d give the second thread enough time 26. / to get by the first thread. 27.
35、60; Thread.currentThread().sleep(50); 28. 29. 30. catch(InterruptedException ex) 31.
36、; logger.warn("Sleep interrupted"); 32. 33. 34. public class Singleton private static Singleton singleton = null; private static Logger log
37、ger = Logger.getRootLogger(); private static boolean firstThread = true; protected Singleton() / Exists only to defeat instantiation. public static Singleton getInstance() if(singleton = null) simulateRandomActivity(); singleton = new Singleton(); ("created singleton: " + single
38、ton); return singleton; private static void simulateRandomActivity() try if(firstThread) firstThread = false; ("sleeping."); / This nap should give the second thread enough time / to get by the first thread. Thread.currentThread().sleep(50); catch(InterruptedException ex) logger
39、.warn("Sleep interrupted"); 除了在這個清單中的單例類強(qiáng)制使用了一個多線程錯誤處理,例4類似于例1中的單例類。在getInstance()方法第一次被調(diào)用時,調(diào)用這個方法的線程會休眠50毫秒以便另外的線程也有時間調(diào)用getInstance()并創(chuàng)建一個新的單例類實(shí)例。當(dāng)休眠的線程覺醒時,它也會創(chuàng)建一個新的單例類實(shí)例,這樣我們就有兩個單例類實(shí)例。盡管例4是人為如此的,但它卻模擬了第一個線程調(diào)用了getInstance()并在沒有完成時被切換的真實(shí)情形。 例5測試了例4的單例類: 例5.失敗的測試 Java代碼 .
40、; 5. public class SingletonTest extends TestCase 6. private static Logger logger = Logger.getRootLogger(); 7. private static Singleton singleton
41、= null; 8. 9. public SingletonTest(String name) 10. super(name); 11. 12. public void
42、160;setUp() 13. singleton = null; 14. 15. public void testUnique() throws InterruptedException 16.
43、160; / Both threads call Singleton.getInstance(). 17. Thread threadOne = new Thread(new SingletonTestRunnable(), 18. &
44、#160; threadTwo = new Thread(new SingletonTestRunnable(); 19. 20. threadOne.start(); 21. threadTwo.start();
45、0;22. 23. threadOne.join(); 24. threadTwo.join(); 25. 26. private static class SingletonTestRunna
46、ble implements Runnable 27. public void run() 28. / Get a reference to the singleton. 29.
47、0; Singleton s = Singleton.getInstance(); 30. 31. / Protect singleton member variable from 32.
48、60; / multithreaded access. 33. synchronized(SingletonTest.class) 34. if(si
49、ngleton = null) / If local reference is null. 35. singleton = s; / .set it to the single
50、ton 36. 37. / Local reference must be equal to the one and 38.
51、; / only instance of Singleton; otherwise, we have two 39. / Singleton instances. 4
52、0. Assert.assertEquals(true, s = singleton); 41. 42. 43. public class SingletonTest extends TestCase p
53、rivate static Logger logger = Logger.getRootLogger(); private static Singleton singleton = null; public SingletonTest(String name) super(name); public void setUp() singleton = null; public void testUnique() throws InterruptedException / Both threads call Singleton.getInstance(). Thread threadOne = n
54、ew Thread(new SingletonTestRunnable(), threadTwo = new Thread(new SingletonTestRunnable(); threadOne.start(); threadTwo.start(); threadOne.join(); threadTwo.join(); private static class SingletonTestRunnable implements Runnable public void run() / Get a reference to the singleton. Singleton s = Sing
55、leton.getInstance(); / Protect singleton member variable from / multithreaded access. synchronized(SingletonTest.class) if(singleton = null) / If local reference is null. singleton = s; / .set it to the singleton / Local reference must be equal to the one and / only instance of Singleton; otherwise,
56、 we have two / Singleton instances. Assert.assertEquals(true, s = singleton); 例5的測試案例創(chuàng)建兩個線程,然后各自啟動,等待完成。這個案例保持了一個對單例類的靜態(tài)引用,每個線程都會調(diào)用Singleton.getInstance()。如果這個靜態(tài)成員變量沒有被設(shè)置,那么第一個線程就會將它設(shè)為通過調(diào)用getInstance()而得到的引用,然后這個靜態(tài)變量會與一個局部變量比較是否相等。 在這個測試案例運(yùn)行時會發(fā)生一系列的事情:第一個線程調(diào)用getInstance(),進(jìn)入if塊,然后休眠;接著,第二個線程也調(diào)用getIn
57、stance()并且創(chuàng)建了一個單例類的實(shí)例。第二個線程會設(shè)置這個靜態(tài)成員變量為它所創(chuàng)建的引用。第二個線程檢查這個靜態(tài)成員變量與一個局部備份的相等性。然后測試通過。當(dāng)?shù)谝粋€線程覺醒時,它也會創(chuàng)建一個單例類的實(shí)例,并且它不會設(shè)置那個靜態(tài)成員變量(因?yàn)榈诙€線程已經(jīng)設(shè)置過了),所以那個靜態(tài)變量與那個局部變量脫離同步,相等性測試即告失敗。例6列出了例5的輸出: 例6.例5的輸出 Java代碼 1. Buildfile: build.xml 2. init: 3.
58、;echo Build 20030414 (14-04-2003 03:06) 4. compile: 5. run-test-text: 6. INFO Thread-1: sleeping. 7. INFO Thread-2: created singleton: Singleton7e5cbd 8. INFO
59、160;Thread-1: created singleton: Singleton704ebb 9.10. 47) 11. 282) 12. 64) 13. 149) 14. 155) 15. at SingletonTest$SingletonTestRunnable.run(Unknown
60、Source) 16. 554) 17. java . 18. java Time: 0.577 19. 20. java OK (1 test)
61、0; Buildfile: build.xml init: echo Build 20030414 (14-04-2003 03:06) compile: run-test-text: INFO Thread-1: sleeping. INFO Thread-2: created singleton: Singleton7e5cbd INFO Thread-1: created singleton: Singleton704ebb at SingletonTest$SingletonTestRunnable.run(Unknown Source) java . java Time:
62、0.577 java OK (1 test) 到現(xiàn)在為止我們已經(jīng)知道例4不是線程安全的,那就讓我們看看如何修正它。 同步 要使例4的單例類為線程安全的很容易-只要像下面一個同步化getInstance()方法: Java代碼 1. public synchronized static Singleton getInstance() 2. if(singleton = null) 3.
63、0; simulateRandomActivity(); 4. singleton = new Singleton(); 5. 6. ("created singleton: " + si
64、ngleton); 7. return singleton; 8. public synchronized static Singleton getInstance() if(singleton = null) simulateRandomActivity(); singleton = new Singleton(); ("created singleton: " + singleton); retu
65、rn singleton; 在同步化getInstance()方法后,我們就可以得到例5的測試案例返回的下面的結(jié)果: Java代碼 1. Buildfile: build.xml 2. 3. init: 4. echo Build 20030414 (14-04-2003 03:15) 5.
66、 6. compile: 7. javac Compiling 2 source files 8. 9. run-test-text: 10. INFO Thread-1: sleeping. 11. INFO Thread-1: created singl
67、eton: Singletonef577d 12. INFO Thread-2: created singleton: Singletonef577d 13. java . 14. java Time: 0.513 15.
68、60; 16. java OK (1 test) Buildfile: build.xml init: echo Build 20030414 (14-04-2003 03:15) compile: javac Compiling 2 source files run-test-text: INFO Thread-1: sleeping. INFO Thread-1: created singleton: Singletonef577d INFO Thread-
69、2: created singleton: Singletonef577d java . java Time: 0.513 java OK (1 test) 這此,這個測試案例工作正常,并且多線程的煩惱也被解決;然而,機(jī)敏的讀者可能會認(rèn)識到getInstance()方法只需要在第一次被調(diào)用時同步。因?yàn)橥降男阅荛_銷很昂貴(同步方法比非同步方法能降低到100次左右),或許我們可以引入一種性能改進(jìn)方法,它只同步單例類的getInstance()方法中的賦值語句。 一種性能改進(jìn)的方法 尋找一種性能改進(jìn)方法時,你可能會選擇像下面這樣重寫getInstance()方法: Java代碼 1. public
70、 static Singleton getInstance() 2. if(singleton = null) 3. synchronized(Singleton.class) 4.
71、60;singleton = new Singleton(); 5. 6. 7. return singleton; 8. public static Singleton getInstance() if(singleton = n
72、ull) synchronized(Singleton.class) singleton = new Singleton(); return singleton; 這個代碼片段只同步了關(guān)鍵的代碼,而不是同步整個方法。然而這段代碼卻不是線程安全的。考慮一下下面的假定:線程1進(jìn)入同步塊,并且在它給singleton成員變量賦值之前線程1被切換。接著另一個線程進(jìn)入if塊。第二個線程將等待直到第一個線程完成,并且仍然會得到兩個不同的單例類實(shí)例。有修復(fù)這個問題的方法嗎?請讀下去。 雙重加鎖檢查 初看上去,雙重加鎖檢查似乎是一種使懶漢式實(shí)例化為線程安全的技術(shù)。下面的代碼片段展示了這種技術(shù): Java代碼
73、1. public static Singleton getInstance() 2. if(singleton = null) 3. synchronized(Singleton.class) 4. if(singleton
74、= null) 5. singleton = new Singleton(); 6. 7. 8.
75、9. return singleton; 10. public static Singleton getInstance() if(singleton = null) synchronized(Singleton.class) if(singleton = null) singleton = new Singleton(); return singleton; 如果兩個線程同時訪問getInstance()方法會發(fā)生什么?想像一下線程1進(jìn)行同步塊馬上又被切換。接著,第二個線程進(jìn)入if 塊。
76、當(dāng)線程1退出同步塊時,線程2會重新檢查看是否singleton實(shí)例仍然為null。因?yàn)榫€程1設(shè)置了singleton成員變量,所以線程2的第二次檢查會失敗,第二個單例類實(shí)例也就不會被創(chuàng)建。似乎就是如此。 不幸的是,雙重加鎖檢查不會保證正常工作,因?yàn)榫幾g器會在Singleton的構(gòu)造方法被調(diào)用之前隨意給singleton賦一個值。如果在singleton引用被賦值之后而被初始化之前線程1被切換,線程2就會被返回一個對未初始化的單例類實(shí)例的引用。 一個改進(jìn)的線程安全的單例模式實(shí)現(xiàn) 例7列出了一個簡單、快速而又是線程安全的單例模式實(shí)現(xiàn): 例7.一個簡單的單例類 Java代碼 1. public
77、60;class Singleton 2. public final static Singleton INSTANCE = new Singleton(); 3. private Singleton() 4.
78、; / Exists only to defeat instantiation. 5. 6. public class Singleton public final static Singleton INSTANCE = new Singleton(); private Singleton() / Exists only to defeat instant
79、iation. 這段代碼是線程安全的是因?yàn)殪o態(tài)成員變量一定會在類被第一次訪問時被創(chuàng)建。你得到了一個自動使用了懶漢式實(shí)例化的線程安全的實(shí)現(xiàn);你應(yīng)該這樣使用它: Java代碼 1. Singleton singleton = Singleton.INSTANCE; 2. singleton.dothis(); 3. singleton.dothat(); 4. . Singleton singleton = Singleton
80、.INSTANCE; singleton.dothis(); singleton.dothat(); . 當(dāng)然萬事并不完美,前面的Singleton只是一個折衷的方案;如果你使用那個實(shí)現(xiàn),你就無法改變它以便后來你可能想要允許多個單例類的實(shí)例。用一種更折哀的單例模式實(shí)現(xiàn)(通過一個getInstance()方法獲得實(shí)例)你可以改變這個方法以便返回一個唯一的實(shí)例或者是數(shù)百個實(shí)例中的一個你不能用一個公開且是靜態(tài)的(public static)成員變量這樣做 你可以安全的使用例的單例模式實(shí)現(xiàn)或者是例的帶一個同步的getInstance()方法的實(shí)現(xiàn)然而,我們必須要研究另一個問題:你必須在編譯期指定這個單
81、例類,這樣就不是很靈活一個單例類的注冊表會讓我們在運(yùn)行期指定一個單例類 使用注冊表 使用一個單例類注冊表可以: 在運(yùn)行期指定單例類 防止產(chǎn)生多個單例類子類的實(shí)例 在例8的單例類中,保持了一個通過類名進(jìn)行注冊的單例類注冊表: 例8 帶注冊表的單例類 Java代碼 1.2.3. 4. public class Singleton 5. private static HashMap map = ne
82、w HashMap(); 6. private static Logger logger = Logger.getRootLogger(); 7. 8. protected Singleton() 9. /
83、;Exists only to thwart instantiation 10. 11. public static synchronized Singleton getInstance(String classname) 12. if(cla
84、ssname = null) throw new IllegalArgumentException("Illegal classname"); 13. Singleton singleton = (Singleton)map.get(classname); 14. 15. if(singleton != null) 16. ("got singleton from&
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 摩托車照明設(shè)備升級考核試卷
- 自動扶梯智能化改造的技術(shù)方案與經(jīng)濟(jì)效益分析考核試卷
- 2023年部編版三年級語文下冊期末試卷【參考答案】
- 外貿(mào)英語函電unit4課件
- 遼寧省丹東二十四中學(xué)2025屆初三第三次調(diào)研考化學(xué)試題文試題含解析
- 江西省南昌市安義中學(xué)2025年高三下學(xué)期聯(lián)考期末試卷數(shù)學(xué)試題含解析
- 江蘇省無錫市宜興中學(xué)2024-2025學(xué)年初三4月(四區(qū))聯(lián)考數(shù)學(xué)試題試卷含解析
- 四川護(hù)理職業(yè)學(xué)院《有機(jī)化學(xué)(1)(上)》2023-2024學(xué)年第二學(xué)期期末試卷
- 內(nèi)蒙古自治區(qū)赤峰市第二中學(xué)2024-2025學(xué)年高三下學(xué)期期中聯(lián)考化學(xué)試題試卷含解析
- 陜西省榆林市一中學(xué)分校2024-2025學(xué)年初三第二學(xué)期聯(lián)合教學(xué)質(zhì)量調(diào)研數(shù)學(xué)試題試卷含解析
- 環(huán)境藝術(shù)設(shè)計(jì)職業(yè)生涯規(guī)劃書
- 郵政社招筆試試題及答案
- 2025年java開發(fā)面試題及答案
- (完整版)公司的代賬協(xié)議模板合同7篇
- 全過程工程咨詢投標(biāo)方案(技術(shù)方案)
- 2024中國合同能源管理行業(yè)發(fā)展前景預(yù)測及投資戰(zhàn)略咨詢報(bào)告
- 風(fēng)力發(fā)電項(xiàng)目實(shí)習(xí)報(bào)告范文
- 自然辯證法概論(視頻課)知到課后答案智慧樹章節(jié)測試答案2025年春安徽農(nóng)業(yè)大學(xué)
- 海南省臨高縣2022-2023學(xué)年小升初語文試卷(有答案)
- 第六單元“保護(hù)環(huán)境”(主題閱讀)-六年級語文上冊閱讀理解(統(tǒng)編版)
- 名著《紅巖》三年中考真題及典型模擬題訓(xùn)練(原卷版)
評論
0/150
提交評論