『壹』 關於java的監聽器
1、public void addWindowListener(WindowListener l)添加指定的窗口偵聽器,以從此窗口接收窗口事件。如果 l 為 null,則不拋出任何異常,且不執行任何操作。
這個是API中的方法定義,此方法參數為介面WindowListener,任何實現該介面的類都可以作為參數。
2、public abstract class WindowAdapterimplements WindowListener, WindowStateListener, WindowFocusListener
接收窗口事件的抽象適配器類。此類中的方法為空。此類存在的目的是方便創建偵聽器對象。
擴展此類可創建 WindowEvent 偵聽器並為所需事件重寫該方法。(如果要實現
WindowListener 介面,則必須定義該介面內的所有方法。此抽象類將所有方法都定義為
null,所以只需針對關心的事件定義方法。)
使用擴展的類可以創建偵聽器對象,然後使用窗口的 addWindowListener
方法向該窗口注冊偵聽器。當通過打開、關閉、激活或停用、圖標化或取消圖標化而改變了窗口狀態時,將調用該偵聽器對象中的相關方法,並將
WindowEvent 傳遞給該方法。
3、如果我想在代碼中一次性使用某個類(抽象類或具體類)或介面,可以使用匿名類的方式,這樣不需自己定義一個My***類,然後再使用,比較方便。用法就是直接在new WindowAdapter()後面加入類定義,在其中實現或覆蓋方法就可以了。
匿名類不是返回值,而是相當於new String(「hello」)這種的擴展形式。我覺得匿名類的最多用處就是加監聽器時。
附上WindowAdapter源代碼:
implementsWindowListener,WindowStateListener,WindowFocusListener
{
publicvoidwindowOpened(WindowEvente){}
publicvoidwindowClosing(WindowEvente){}
publicvoidwindowClosed(WindowEvente){}
publicvoidwindowIconified(WindowEvente){}
publicvoidwindowDeiconified(WindowEvente){}
publicvoidwindowActivated(WindowEvente){}
publicvoidwindowDeactivated(WindowEvente){}
publicvoidwindowStateChanged(WindowEvente){}
publicvoidwindowGainedFocus(WindowEvente){}
publicvoidwindowLostFocus(WindowEvente){}
}
『貳』 java中為什麼要設置監聽器,有什麼用
豬哥解答:
1、private JButton jb=new JButton("按鈕");這句話聲明了一個按鈕,名字叫jb。
2、jb.addActionListener(this);這里給jb那個按鈕設置了監聽,默認為點擊觸發,當然你寫的這個監聽有點怪異~
3、點擊按鈕jb觸發監聽處理方法actionPerformed,在這里可以做你想要的操作,你代碼實現的是改變lab這個label標簽的內容。
4、至於java中為什麼要用監聽,這就像銀行裝監控一樣,監視你的一舉一動,銀行裝監控是為了捕捉每個進銀行的人的動作,預防危險的發生。
java中做監聽同樣是為了監視某個客戶端動作用的,萬一你給我搞破壞怎麼辦(監聽的作用遠不止如此),當然也像平時生活中不是所有的地方都要放監控,要不就沒法過了,java中也不是所有的地方都要放監聽,具體哪裡要放監聽,不該是在課本里學的,應該根據實際工廠、公司的需求來定。
『叄』 Java實現監聽文件變化的三種方法,推薦第三種
背景在研究規則引擎時,如果規則以文件的形式存儲,那麼就需要監聽指定的目錄或文件來感知規則是否變化,進而進行載入。當然,在其他業務場景下,比如想實現配置文件的動態載入、日誌文件的監聽、FTP文件變動監聽等都會遇到類似的場景。
本文給大家提供三種解決方案,並分析其中的利弊,建議收藏,以備不時之需。
方案一:定時任務 + File#lastModified這個方案是最簡單,最能直接想到的解決方案。通過定時任務,輪訓查詢文件的最後修改時間,與上一次進行對比。如果發生變化,則說明文件已經修改,進行重新載入或對應的業務邏輯處理。
在上篇文章《JDK的一個Bug,監聽文件變更要小心了》中已經編寫了具體的實例,並且也提出了其中的不足。
這里再把實例代碼貼出來:
public class FileWatchDemo { /*** 上次更新時間*/ public static long LAST_TIME = 0L; public static void main(String[] args) throws IOException {String fileName = "/Users/zzs/temp/1.txt";// 創建文件,僅為實例,實踐中由其他程序觸發文件的變更createFile(fileName);// 執行2次for (int i = 0; i < 2; i++) { long timestamp = readLastModified(fileName); if (timestamp != LAST_TIME) {System.out.println("文件已被更新:" + timestamp);LAST_TIME = timestamp;// 重新載入,文件內容 } else {System.out.println("文件未更新"); }} } public static void createFile(String fileName) throws IOException {File file = new File(fileName);if (!file.exists()) { boolean result = file.createNewFile(); System.out.println("創建文件:" + result);} } public static long readLastModified(String fileName) {File file = new File(fileName);return file.lastModified(); }}對於文件低頻變動的場景,這種方案實現簡單,基本上可以滿足需求。不過像上篇文章中提到的那樣,需要注意Java 8和Java 9中File#lastModified的Bug問題。
但該方案如果用在文件目錄的變化上,缺點就有些明顯了,比如:操作頻繁,效率都損耗在遍歷、保存狀態、對比狀態上了,無法充分利用OS的功能。
方案二:WatchService在Java 7中新增了java.nio.file.WatchService,通過它可以實現文件變動的監聽。WatchService是基於操作系統的文件系統監控器,可以監控系統所有文件的變化,無需遍歷、無需比較,是一種基於信號收發的監控,效率高。
public class WatchServiceDemo {public static void main(String[] args) throws IOException {// 這里的監聽必須是目錄Path path = Paths.get("/Users/zzs/temp/");// 創建WatchService,它是對操作系統的文件監視器的封裝,相對之前,不需要遍歷文件目錄,效率要高很多WatchService watcher = FileSystems.getDefault().newWatchService();// 注冊指定目錄使用的監聽器,監視目錄下文件的變化;// PS:Path必須是目錄,不能是文件;// StandardWatchEventKinds.ENTRY_MODIFY,表示監視文件的修改事件path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);// 創建一個線程,等待目錄下的文件發生變化try {while (true) {// 獲取目錄的變化:// take()是一個阻塞方法,會等待監視器發出的信號才返回。// 還可以使用watcher.poll()方法,非阻塞方法,會立即返回當時監視器中是否有信號。// 返回結果WatchKey,是一個單例對象,與前面的register方法返回的實例是同一個;WatchKey key = watcher.take();// 處理文件變化事件:// key.pollEvents()用於獲取文件變化事件,只能獲取一次,不能重復獲取,類似隊列的形式。for (WatchEvent<?> event : key.pollEvents()) {// event.kind():事件類型if (event.kind() == StandardWatchEventKinds.OVERFLOW) {//事件可能lost or discardedcontinue;}// 返回觸發事件的文件或目錄的路徑(相對路徑)Path fileName = (Path) event.context();System.out.println("文件更新: " + fileName);}// 每次調用WatchService的take()或poll()方法時需要通過本方法重置if (!key.reset()) {break;}}} catch (Exception e) {e.printStackTrace();}}}上述demo展示了WatchService的基本使用方式,註解部分也說明了每個API的具體作用。
通過WatchService監聽文件的類型也變得更加豐富:
ENTRY_CREATE 目標被創建
ENTRY_DELETE 目標被刪除
ENTRY_MODIFY 目標被修改
OVERFLOW 一個特殊的Event,表示Event被放棄或者丟失
如果查看WatchService實現類(PollingWatchService)的源碼,會發現,本質上就是開啟了一個獨立的線程來監控文件的變化:
PollingWatchService() {// TBD: Make the number of threads configurablescheledExecutor = Executors.(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(null, r, "FileSystemWatcher", 0, false); t.setDaemon(true); return t; }});}也就是說,本來需要我們手動實現的部分,也由WatchService內部幫我們完成了。
如果你編寫一個demo,進行驗證時,會很明顯的感覺到WatchService監控文件的變化並不是實時的,有時候要等幾秒才監聽到文件的變化。以實現類PollingWatchService為例,查看源碼,可以看到如下代碼:
void enable(Set<? extends Kind<?>> var1, long var2) {synchronized(this) {this.events = var1;Runnable var5 = new Runnable() {public void run() {PollingWatchKey.this.poll();}};this.poller = PollingWatchService.this.scheledExecutor.scheleAtFixedRate(var5, var2, var2, TimeUnit.SECONDS);}}也就是說監聽器由按照固定時間間隔的調度器來控制的,而這個時間間隔在SensitivityWatchEventModifier類中定義:
public enum SensitivityWatchEventModifier implements Modifier {HIGH(2),MEDIUM(10),LOW(30);// ...}該類提供了3個級別的時間間隔,分別為2秒、10秒、30秒,默認值為10秒。這個時間間隔可以在path#register時進行傳遞:
path.register(watcher, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY},SensitivityWatchEventModifier.HIGH);相對於方案一,實現起來簡單,效率高。不足的地方也很明顯,只能監聽當前目錄下的文件和目錄,不能監視子目錄,而且我們也看到監聽只能算是准實時的,而且監聽時間只能取API默認提供的三個值。
該API在Stack Overflow上也有人提出Java 7在Mac OS下有延遲的問題,甚至涉及到Windows和Linux系統,筆者沒有進行其他操作系統的驗證,如果你遇到類似的問題,可參考對應的文章,尋求解決方案:https://blog.csdn.net/claram/article/details/97919664 。
方案三:Apache Commons-IO方案一我們自己來實現,方案二藉助於JDK的API來實現,方案三便是藉助於開源的框架來實現,這就是幾乎每個項目都會引入的commons-io類庫。
引入相應依賴:
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.7</version></dependency>注意,不同的版本需要不同的JDK支持,2.7需要Java 8及以上版本。
commons-io對實現文件監聽的實現位於org.apache.commons.io.monitor包下,基本使用流程如下:
自定義文件監聽類並繼承 FileAlterationListenerAdaptor 實現對文件與目錄的創建、修改、刪除事件的處理;
自定義文件監控類,通過指定目錄創建一個觀察者 FileAlterationObserver;
向監視器添加文件系統觀察器,並添加文件監聽器;
調用並執行。
第一步:創建文件監聽器。根據需要在不同的方法內實現對應的業務邏輯處理。
public class FileListener extends FileAlterationListenerAdaptor {@Overridepublic void onStart(FileAlterationObserver observer) {super.onStart(observer);System.out.println("onStart");}@Overridepublic void onDirectoryCreate(File directory) {System.out.println("新建:" + directory.getAbsolutePath());}@Overridepublic void onDirectoryChange(File directory) {System.out.println("修改:" + directory.getAbsolutePath());}@Overridepublic void onDirectoryDelete(File directory) {System.out.println("刪除:" + directory.getAbsolutePath());}@Overridepublic void onFileCreate(File file) {String compressedPath = file.getAbsolutePath();System.out.println("新建:" + compressedPath);if (file.canRead()) {// TODO 讀取或重新載入文件內容System.out.println("文件變更,進行處理");}}@Overridepublic void onFileChange(File file) {String compressedPath = file.getAbsolutePath();System.out.println("修改:" + compressedPath);}@Overridepublic void onFileDelete(File file) {System.out.println("刪除:" + file.getAbsolutePath());}@Overridepublic void onStop(FileAlterationObserver observer) {super.onStop(observer);System.out.println("onStop");}}第二步:封裝一個文件監控的工具類,核心就是創建一個觀察者FileAlterationObserver,將文件路徑Path和監聽器FileAlterationListener進行封裝,然後交給FileAlterationMonitor。
public class FileMonitor {private FileAlterationMonitor monitor;public FileMonitor(long interval) {monitor = new FileAlterationMonitor(interval);}/** * 給文件添加監聽 * * @param path 文件路徑 * @param listener 文件監聽器 */public void monitor(String path, FileAlterationListener listener) {FileAlterationObserver observer = new FileAlterationObserver(new File(path));monitor.addObserver(observer);observer.addListener(listener);}public void stop() throws Exception {monitor.stop();}public void start() throws Exception {monitor.start();}}第三步:調用並執行:
public class FileRunner {public static void main(String[] args) throws Exception {FileMonitor fileMonitor = new FileMonitor(1000);fileMonitor.monitor("/Users/zzs/temp/", new FileListener());fileMonitor.start();}}執行程序,會發現每隔1秒輸入一次日誌。當文件發生變更時,也會列印出對應的日誌:
public class WatchServiceDemo {public static void main(String[] args) throws IOException {// 這里的監聽必須是目錄Path path = Paths.get("/Users/zzs/temp/");// 創建WatchService,它是對操作系統的文件監視器的封裝,相對之前,不需要遍歷文件目錄,效率要高很多WatchService watcher = FileSystems.getDefault().newWatchService();// 注冊指定目錄使用的監聽器,監視目錄下文件的變化;// PS:Path必須是目錄,不能是文件;// StandardWatchEventKinds.ENTRY_MODIFY,表示監視文件的修改事件path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);// 創建一個線程,等待目錄下的文件發生變化try {while (true) {// 獲取目錄的變化:// take()是一個阻塞方法,會等待監視器發出的信號才返回。// 還可以使用watcher.poll()方法,非阻塞方法,會立即返回當時監視器中是否有信號。// 返回結果WatchKey,是一個單例對象,與前面的register方法返回的實例是同一個;WatchKey key = watcher.take();// 處理文件變化事件:// key.pollEvents()用於獲取文件變化事件,只能獲取一次,不能重復獲取,類似隊列的形式。for (WatchEvent<?> event : key.pollEvents()) {// event.kind():事件類型if (event.kind() == StandardWatchEventKinds.OVERFLOW) {//事件可能lost or discardedcontinue;}// 返回觸發事件的文件或目錄的路徑(相對路徑)Path fileName = (Path) event.context();System.out.println("文件更新: " + fileName);}// 每次調用WatchService的take()或poll()方法時需要通過本方法重置if (!key.reset()) {break;}}} catch (Exception e) {e.printStackTrace();}}}0當然,對應的監聽時間間隔,可以通過在創建FileMonitor時進行修改。
該方案中監聽器本身會啟動一個線程定時處理。在每次運行時,都會先調用事件監聽處理類的onStart方法,然後檢查是否有變動,並調用對應事件的方法;比如,onChange文件內容改變,檢查完後,再調用onStop方法,釋放當前線程佔用的CPU資源,等待下次間隔時間到了被再次喚醒運行。
監聽器是基於文件目錄為根源的,也可以可以設置過濾器,來實現對應文件變動的監聽。過濾器的設置可查看FileAlterationObserver的構造方法:
public class WatchServiceDemo {public static void main(String[] args) throws IOException {// 這里的監聽必須是目錄Path path = Paths.get("/Users/zzs/temp/");// 創建WatchService,它是對操作系統的文件監視器的封裝,相對之前,不需要遍歷文件目錄,效率要高很多WatchService watcher = FileSystems.getDefault().newWatchService();// 注冊指定目錄使用的監聽器,監視目錄下文件的變化;// PS:Path必須是目錄,不能是文件;// StandardWatchEventKinds.ENTRY_MODIFY,表示監視文件的修改事件path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);// 創建一個線程,等待目錄下的文件發生變化try {while (true) {// 獲取目錄的變化:// take()是一個阻塞方法,會等待監視器發出的信號才返回。// 還可以使用watcher.poll()方法,非阻塞方法,會立即返回當時監視器中是否有信號。// 返回結果WatchKey,是一個單例對象,與前面的register方法返回的實例是同一個;WatchKey key = watcher.take();// 處理文件變化事件:// key.pollEvents()用於獲取文件變化事件,只能獲取一次,不能重復獲取,類似隊列的形式。for (WatchEvent<?> event : key.pollEvents()) {// event.kind():事件類型if (event.kind() == StandardWatchEventKinds.OVERFLOW) {//事件可能lost or discardedcontinue;}// 返回觸發事件的文件或目錄的路徑(相對路徑)Path fileName = (Path) event.context();System.out.println("文件更新: " + fileName);}// 每次調用WatchService的take()或poll()方法時需要通過本方法重置if (!key.reset()) {break;}}} catch (Exception e) {e.printStackTrace();}}}1小結至此,基於Java實現監聽文件變化的三種方案便介紹完畢。經過上述分析及實例,大家已經看到,並沒有完美的解決方案,根據自己的業務情況及系統的容忍度可選擇最適合的方案。而且,在此基礎上可以新增一些其他的輔助措施,來避免具體方案中的不足之處。
博主簡介:《SpringBoot技術內幕》技術圖書作者,酷愛鑽研技術,寫技術干貨文章。
公眾號:「程序新視界」,博主的公眾號,歡迎關注~
技術交流:請聯系博主微信號:zhuan2quan
原文:https://juejin.cn/post/7103318602748526628『肆』 有趣的 WatchService
歡迎來到本文,我們將深入探討有趣且實用的 Java WatchService 介面。首先,讓我們了解它的基本概念和用途,以及如何在開發中充分利用它。
WatchService 是 JDK 7 引入的一個介面,其核心功能在於監視注冊對象的改變事件。例如,一個文件管理系統可以利用 WatchService 監視目錄,以便實時更新文件列表,當文件被創建或刪除時。
理解 WatchService 的基礎概念後,接下來我們來學習幾個關鍵的 API:
WatchService
WatchService 實現了對對象變化的監視,允許監聽者注冊感興趣的路徑和事件類型。
WatchKey
當注冊的路徑發生改變時,WatchService 會生成 WatchKey 對象,用於通知監聽者。
WatchEvent
WatchEvent 代表路徑上的一個事件,如目錄下文件的創建、刪除等。
StandardWatchEventKinds
定義了常見的事件類型,如文件創建、修改、刪除等。
在實際應用中,WatchService 通過注冊監聽器與特定目錄關聯,監聽器將收到 WatchKey 對象,通過它可以獲取到最新的 WatchEvent。這種實時更新機制使得文件管理系統、日誌系統等在處理實時數據時更為高效。
了解 WatchService 的工作原理後,我們可以通過以下步驟來應用它:
使用 WatchService 可以簡化文件系統操作,提高應用的響應速度。它適用於各種需要實時監控文件系統變化的應用場景,如實時文件監控、日誌分析等。
總結來說,WatchService 是一個強大的 Java 介面,通過它,開發者能夠實現對文件系統變更的實時監控,為應用帶來更高的性能和便利性。