欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
關(guān)于 Java 常用工具您不知道的 5 件事

您覺(jué)得自己懂 Java 編程?事實(shí)是,大多數開(kāi)發(fā)人員都只領(lǐng)會(huì )到了 Java 平臺的皮毛,所學(xué)也只夠應付工作。在本 系列 中,Ted Neward 深度挖掘 Java 平臺的核心功能,揭示一些鮮為人知的事實(shí),幫助您解決最棘手的編程困難。

很多年前,當我還是高中生的時(shí)候,我曾考慮以小說(shuō)作家作為我的職業(yè)追求,我訂閱了一本 Writer's Digest 雜志。我記得其中有篇專(zhuān)欄文章,是關(guān)于 “太小而難以保存的線(xiàn)頭”,專(zhuān)欄作者描述廚房?jì)ξ锍閷现蟹艥M(mǎn)了無(wú)法分類(lèi)的玩意兒。這句話(huà)我一直銘記在心,它正好用來(lái)描述本文的內容,本系列的最后一篇(至少目前是這樣)。

Java 平臺就充滿(mǎn)了這樣的 “線(xiàn)頭” — 有用的命令行工具和庫,大多數 Java 開(kāi)發(fā)人員甚至都不知道,更別提使用了。其中很多無(wú)法劃分到之前的 5 件事 系列 的編程分類(lèi)中,但不管怎樣,嘗試一下:有些說(shuō)不定會(huì )在您編程的廚房抽屜中占得一席之地。

1. StAX

在千禧年左右,當 XML 第一次出現在很多 Java 開(kāi)發(fā)人員面前時(shí),有兩種基本的解析 XML 文件的方法。SAX 解析器實(shí)際是由程序員對事件調用一系列回調方法的大型狀態(tài)機。DOM 解析器將整個(gè) XML 文檔加入內存,并切割成離散的對象,它們連接在一起形成一個(gè)樹(shù)。該樹(shù)描述了文檔的整個(gè) XML Infoset 表示法。這兩個(gè)解析器都有缺點(diǎn):SAX 太低級,無(wú)法使用,DOM 代價(jià)太大,尤其對于大的 XML 文件 — 整個(gè)樹(shù)成了一個(gè)龐然大物。

幸運的是,Java 開(kāi)發(fā)人員找到第三種方法來(lái)解析 XML 文件,通過(guò)對文檔建模成 “節點(diǎn)”,它們可以從文檔流中一次取出一個(gè),檢查,然后處理或丟棄。這些 “節點(diǎn)” 的 “流” 提供了 SAX 和 DOM 的中間地帶,名為 “Streaming API for XML”,或者叫做StAX。(此縮寫(xiě)用于區分新的 API 與原來(lái)的 SAX 解析器,它與此同名。)StAX 解析器后來(lái)包裝到了 JDK 中,在 javax.xml.stream 包。

使用 StAX 相當簡(jiǎn)單:實(shí)例化 XMLEventReader,將它指向一個(gè)格式良好的 XML 文件,然后一次 “拉出” 一個(gè)節點(diǎn)(通常用 while 循環(huán)),查看。例如,在清單 1 中,列舉出了 Ant 構造腳本中的所有目標:


清單 1. 只是讓 StAX 指向目標
            import java.io.*;
            import javax.xml.namespace.QName;
            import javax.xml.stream.*;
            import javax.xml.stream.events.*;
            import javax.xml.stream.util.*;
            public class Targets
            {
            public static void main(String[] args)
            throws Exception
            {
            for (String arg : args)
            {
            XMLEventReader xsr =
            XMLInputFactory.newInstance()
            .createXMLEventReader(new FileReader(arg));
            while (xsr.hasNext())
            {
            XMLEvent evt = xsr.nextEvent();
            switch (evt.getEventType())
            {
            case XMLEvent.START_ELEMENT:
            {
            StartElement se = evt.asStartElement();
            if (se.getName().getLocalPart().equals("target"))
            {
            Attribute targetName =
            se.getAttributeByName(new QName("name"));
            // Found a target!
            System.out.println(targetName.getValue());
            }
            break;
            }
            // Ignore everything else
            }
            }
            }
            }
            }

StAX 解析器不會(huì )替換所有的 SAX 和 DOM 代碼。但肯定會(huì )讓某些任務(wù)容易些。尤其對完成不需要知道 XML 文檔整個(gè)樹(shù)結構的任務(wù)相當方便。

請注意,如果事件對象級別太高,無(wú)法使用,StAX 也有一個(gè)低級 API 在 XMLStreamReader 中。盡管也許沒(méi)有閱讀器有用,StAX 還有一個(gè) XMLEventWriter,同樣,還有一個(gè) XMLStreamWriter 類(lèi)用于 XML 輸出。


2. ServiceLoader

Java 開(kāi)發(fā)人員經(jīng)常希望將使用和創(chuàng )建組件的內容區分開(kāi)來(lái)。這通常是通過(guò)創(chuàng )建一個(gè)描述組件動(dòng)作的接口,并使用某種中介創(chuàng )建組件實(shí)例來(lái)完成的。很多開(kāi)發(fā)人員使用 Spring 框架來(lái)完成,但還有其他的方法,它比 Spring 容器更輕量級。

java.utilServiceLoader 類(lèi)能讀取隱藏在 JAR 文件中的配置文件,并找到接口的實(shí)現,然后使這些實(shí)現成為可選擇的列表。例如,如果您需要一個(gè)私仆(personal-servant)組件來(lái)完成任務(wù),您可以使用清單 2 中的代碼來(lái)實(shí)現:


清單 2. IPersonalServant
            public interface IPersonalServant
            {
            // Process a file of commands to the servant
            public void process(java.io.File f)
            throws java.io.IOException;
            public boolean can(String command);
            }

can() 方法可讓您確定所提供的私仆實(shí)現是否滿(mǎn)足需求。清單 3 中的 ServiceLoaderIPersonalServant 列表基本上滿(mǎn)足需求:


清單 3. IPersonalServant 行嗎?
            import java.io.*;
            import java.util.*;
            public class Servant
            {
            public static void main(String[] args)
            throws IOException
            {
            ServiceLoader<IPersonalServant> servantLoader =
            ServiceLoader.load(IPersonalServant.class);
            IPersonalServant i = null;
            for (IPersonalServant ii : servantLoader)
            if (ii.can("fetch tea"))
            i = ii;
            if (i == null)
            throw new IllegalArgumentException("No suitable servant found");
            for (String arg : args)
            {
            i.process(new File(arg));
            }
            }
            }

假設有此接口的實(shí)現,如清單 4:


清單 4. Jeeves 實(shí)現了 IPersonalServant
            import java.io.*;
            public class Jeeves
            implements IPersonalServant
            {
            public void process(File f)
            {
            System.out.println("Very good, sir.");
            }
            public boolean can(String cmd)
            {
            if (cmd.equals("fetch tea"))
            return true;
            else
            return false;
            }
            }

剩下的就是配置包含實(shí)現的 JAR 文件,讓 ServiceLoader 能識別 — 這可能會(huì )非常棘手。JDK 想要 JAR 文件有一個(gè) META-INF/services 目錄,它包含一個(gè)文本文件,其文件名與接口類(lèi)名完全匹配 — 本例中是 META-INF/services/IPersonalServant。接口類(lèi)名的內容是實(shí)現的名稱(chēng),每行一個(gè),如清單 5:


清單 5. META-INF/services/IPersonalServant
            Jeeves   # comments are OK
            

幸運的是,Ant 構建系統(自 1.7.0 以來(lái))包含一個(gè)對 jar 任務(wù)的服務(wù)標簽,讓這相對容易,見(jiàn)清單 6:


清單 6. Ant 構建的 IPersonalServant
            <target name="serviceloader" depends="build">
            <jar destfile="misc.jar" basedir="./classes">
            <service type="IPersonalServant">
            <provider classname="Jeeves" />
            </service>
            </jar>
            </target>
            

這里,很容易調用 IPersonalServant,讓它執行命令。然而,解析和執行這些命令可能會(huì )非常棘手。這又是另一個(gè) “小線(xiàn)頭”。


3. Scanner

有無(wú)數 Java 工具能幫助您構建解析器,很多函數語(yǔ)言已成功構建解析器函數庫(解析器選擇器)。但如果要解析的是逗號分隔值文件,或空格分隔文本文件,又怎么辦呢?大多數工具用在此處就過(guò)于隆重了,而 String.split() 又不夠。(對于正則表達式,請記住一句老話(huà):“ 您有一個(gè)問(wèn)題,用正則表達式解決。那您就有兩個(gè)問(wèn)題了?!保?

Java 平臺的 Scanner 類(lèi)會(huì )是這些類(lèi)中您最好的選擇。以輕量級文本解析器為目標,Scanner 提供了一個(gè)相對簡(jiǎn)單的 API,用于提取結構化文本,并放入強類(lèi)型的部分。想象一下,如果您愿意,一組類(lèi)似 DSL 的命令(源自 Terry Pratchett Discworld 小說(shuō))排列在文本文件中,如清單 7:


清單 7. Igor 的任務(wù)
            fetch 1 head
            fetch 3 eye
            fetch 1 foot
            attach foot to head
            attach eye to head
            admire
            

您,或者是本例中稱(chēng)為 Igor的私仆,能輕松使用 Scanner 解析這組違法命令,如清單 8 所示:


清單 8. Igor 的任務(wù),由 Scanner 解析
            import java.io.*;
            import java.util.*;
            public class Igor
            implements IPersonalServant
            {
            public boolean can(String cmd)
            {
            if (cmd.equals("fetch body parts"))
            return true;
            if (cmd.equals("attach body parts"))
            return true;
            else
            return false;
            }
            public void process(File commandFile)
            throws FileNotFoundException
            {
            Scanner scanner = new Scanner(commandFile);
            // Commands come in a verb/number/noun or verb form
            while (scanner.hasNext())
            {
            String verb = scanner.next();
            if (verb.equals("fetch"))
            {
            int num = scanner.nextInt();
            String type = scanner.next();
            fetch (num, type);
            }
            else if (verb.equals("attach"))
            {
            String item = scanner.next();
            String to = scanner.next();
            String target = scanner.next();
            attach(item, target);
            }
            else if (verb.equals("admire"))
            {
            admire();
            }
            else
            {
            System.out.println("I don't know how to "
            + verb + ", marthter.");
            }
            }
            }
            public void fetch(int number, String type)
            {
            if (parts.get(type) == null)
            {
            System.out.println("Fetching " + number + " "
            + type + (number > 1 ? "s" : "") + ", marthter!");
            parts.put(type, number);
            }
            else
            {
            System.out.println("Fetching " + number + " more "
            + type + (number > 1 ? "s" : "") + ", marthter!");
            Integer currentTotal = parts.get(type);
            parts.put(type, currentTotal + number);
            }
            System.out.println("We now have " + parts.toString());
            }
            public void attach(String item, String target)
            {
            System.out.println("Attaching the " + item + " to the " +
            target + ", marthter!");
            }
            public void admire()
            {
            System.out.println("It'th quite the creathion, marthter");
            }
            private Map<String, Integer> parts = new HashMap<String, Integer>();
            }
            

假設 Igor 已在 ServantLoader 中注冊,可以很方便地將 can() 調用改得更實(shí)用,并重用前面的 Servant 代碼,如清單 9 所示:


清單 9. Igor 做了什么
            import java.io.*;
            import java.util.*;
            public class Servant
            {
            public static void main(String[] args)
            throws IOException
            {
            ServiceLoader<IPersonalServant> servantLoader =
            ServiceLoader.load(IPersonalServant.class);
            IPersonalServant i = null;
            for (IPersonalServant ii : servantLoader)
            if (ii.can("fetch body parts"))
            i = ii;
            if (i == null)
            throw new IllegalArgumentException("No suitable servant found");
            for (String arg : args)
            {
            i.process(new File(arg));
            }
            }
            }
            

真正 DSL 實(shí)現顯然不會(huì )僅僅打印到標準輸出流。我把追蹤哪些部分、跟隨哪些部分的細節留待給您(當然,還有忠誠的 Igor)。


4. Timer

java.util.TimerTimerTask 類(lèi)提供了方便、相對簡(jiǎn)單的方法可在定期或一次性延遲的基礎上執行任務(wù):


清單 10. 稍后執行
            import java.util.*;
            public class Later
            {
            public static void main(String[] args)
            {
            Timer t = new Timer("TimerThread");
            t.schedule(new TimerTask() {
            public void run() {
            System.out.println("This is later");
            System.exit(0);
            }
            }, 1 * 1000);
            System.out.println("Exiting main()");
            }
            }

Timer 有許多 schedule() 重載,它們提示某一任務(wù)是一次性還是重復的,并且有一個(gè)啟動(dòng)的 TimerTask 實(shí)例。TimerTask 實(shí)際上是一個(gè) Runnable(事實(shí)上,它實(shí)現了它),但還有另外兩個(gè)方法:cancel() 用來(lái)取消任務(wù),scheduledExecutionTime() 用來(lái)返回任務(wù)何時(shí)啟動(dòng)的近似值。

請注意 Timer 卻創(chuàng )建了一個(gè)非守護線(xiàn)程在后臺啟動(dòng)任務(wù),因此在清單 10 中我需要調用 System.exit() 來(lái)取消任務(wù)。在長(cháng)時(shí)間運行的程序中,最好創(chuàng )建一個(gè) Timer 守護線(xiàn)程(使用帶有指示守護線(xiàn)程狀態(tài)的參數的構造函數),從而它不會(huì )讓 VM 活動(dòng)。

這個(gè)類(lèi)沒(méi)什么神奇的,但它確實(shí)能幫助我們對后臺啟動(dòng)的程序的目的了解得更清楚。它還能節省一些 Thread 代碼,并作為輕量級 ScheduledExecutorService(對于還沒(méi)準備好了解整個(gè) java.util.concurrent 包的人來(lái)說(shuō))。


5. JavaSound

盡管在服務(wù)器端應用程序中不常出現,但 sound 對管理員有著(zhù)有用的 “被動(dòng)” 意義 — 它是惡作劇的好材料。盡管它很晚才出現在 Java 平臺中,JavaSound API 最終還是加入了核心運行時(shí)庫,封裝在 javax.sound * 包 — 其中一個(gè)包是 MIDI 文件,另一個(gè)是音頻文件示例(如普遍的 .WAV 文件格式)。

JavaSound 的 “hello world” 是播放一個(gè)片段,如清單 11 所示:


清單 11. 再放一遍,Sam
            public static void playClip(String audioFile)
            {
            try
            {
            AudioInputStream inputStream =
            AudioSystem.getAudioInputStream(
            this.getClass().getResourceAsStream(audioFile));
            DataLine.Info info =
            new DataLine.Info( Clip.class, audioInputStream.getFormat() );
            Clip clip = (Clip) AudioSystem.getLine(info);
            clip.addLineListener(new LineListener() {
            public void update(LineEvent e) {
            if (e.getType() == LineEvent.Type.STOP) {
            synchronized(clip) {
            clip.notify();
            }
            }
            }
            });
            clip.open(audioInputStream);
            clip.setFramePosition(0);
            clip.start();
            synchronized (clip) {
            clip.wait();
            }
            clip.drain();
            clip.close();
            }
            catch (Exception ex)
            {
            ex.printStackTrace();
            }
            }
            

大多數還是相當簡(jiǎn)單(至少 JavaSound 一樣簡(jiǎn)單)。第一步是創(chuàng )建一個(gè)文件的 AudioInputStream 來(lái)播放。為了讓此方法盡量與上下文無(wú)關(guān),我們從加載類(lèi)的 ClassLoader 中抓取文件作為 InputStream。(AudioSystem 還需要一個(gè) FileString,如果提前知道聲音文件的具體路徑。)一旦完成, DataLine.Info 對象就提供給 AudioSystem,得到一個(gè) Clip,這是播放音頻片段最簡(jiǎn)單的方法。(其他方法提供了對片段更多的控制 — 例如獲取一個(gè) SourceDataLine — 但對于 “播放” 來(lái)說(shuō),過(guò)于復雜)。

這里應該和對 AudioInputStream 調用 open() 一樣簡(jiǎn)單。(“應該” 的意思是如果您沒(méi)遇到下節描述的錯誤。)調用 start() 開(kāi)始播放,drain() 等待播放完成,close() 釋放音頻線(xiàn)路。播放是在單獨的線(xiàn)程進(jìn)行,因此調用 stop() 將會(huì )停止播放,然后調用 start() 將會(huì )從播放暫停的地方重新開(kāi)始;使用 setFramePosition(0) 重新定位到開(kāi)始。

沒(méi)聲音?

JDK 5 發(fā)行版中有個(gè)討厭的小錯誤:在有些平臺上,對于一些短的音頻片段,代碼看上去運行正常,但就是 ... 沒(méi)聲音。顯然媒體播放器在應該出現的位置之前觸發(fā)了 STOP 事件。(見(jiàn) 參考資料 一節中錯誤頁(yè)的鏈接。)

這個(gè)錯誤 “無(wú)法修復”,但解決方法相當簡(jiǎn)單:注冊一個(gè) LineListener 來(lái)監聽(tīng) STOP 事件,當觸發(fā)時(shí),調用片段對象的 notifyAll()。然后在 “調用者” 代碼中,通過(guò)調用 wait() 等待片段完成(還調用 notifyAll())。在沒(méi)出現錯誤的平臺上,這些錯誤是多余的,在 Windows? 及有些 Linux? 版本上,會(huì )讓程序員 “開(kāi)心” 或 “憤怒”。


結束語(yǔ)

現在您都了解了,廚房里的工具。我知道很多人已清楚了解我此處介紹的工具,而我的職業(yè)經(jīng)驗告訴我,很多人將從這篇介紹文章,或者說(shuō)是對長(cháng)期遺忘在凌亂的抽屜中的小工具的提示中受益。

我在此系列中做個(gè)簡(jiǎn)短的中斷,讓別人能加入分享他們各自領(lǐng)域的專(zhuān)業(yè)經(jīng)驗。但別擔心,我還會(huì )回來(lái)的,無(wú)論是本系列還是探索其他領(lǐng)域的新的 5 件事。在那之前,我鼓勵您一直探索 Java 平臺,去發(fā)現那些能讓編程更高效的寶石。


參考資料

學(xué)習

獲得產(chǎn)品和技術(shù)

  • StAX,即 Streaming API for XML,是標準的 XML 處理 API,能將 XML 數據流式處理,輸入或輸出到應用程序中。


本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
在java中如何用鍵盤(pán)輸入一個(gè)數,字符,字符串
java基礎習題(一)
java.util.Scanner應用詳解
Java程序設計總復習題
JAVA經(jīng)典算法50題(4)【面試+工作】
Java基礎語(yǔ)句
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久