<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    amp@java

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      99 隨筆 :: 0 文章 :: 228 評論 :: 0 Trackbacks

    2018年8月18日 #

    當年是從CSDN博客遷過來的,因為那里很不穩定,那時候這里很火的;
    前段時間發現,Blogjava的登錄頁面居然沒有驗證碼了,圖片顯示錯誤,無法登錄,預感這個地方要涼,只好到處找替代,但沒找到好用的博客,github是一個選項,但似乎不是很適合做博客。
    現在終于又可以登錄了,不過首頁居然只剩一篇文章,我以為數據都沒了,登錄發現還是有的,不過太讓人不放心了。
    posted @ 2018-08-18 22:14 amp@java 閱讀(149) | 評論 (0)編輯 收藏

    2017年11月19日 #

     昨天下午開始,之前用得好好的USB鼠標,突然不能用了,找到個PS/2鼠標,卻發現主板沒有鼠標的PS/2口,只有鍵盤的PS/2口,真是奇葩,幸好在用的鍵盤是PS/2口的,費了九牛二虎之力,用鍵盤操作,Win+R,compmgmt.msc進入計算機管理,上下箭頭移到設備管理器,發現右邊的USB控制器前全是黃色嘆號,嘗試卸載再安裝,卻怎么也裝不上,重新啟動發現鼠標在啟動Windows前還是亮燈的,但到了顯示Windows徽標的時候燈就滅了,應該不是硬件問題,而是驅動問題。
    為了進一步證實硬件沒問題,找了個安裝系統的U盤,插進去啟動,能夠正常使用鼠標,于是目標就聚焦在找回驅動上。
    開始折騰:
    1、首先是找官方驅動啊,我這電腦是老機,AMD7系列主板,找了半天,這個主板驅動并沒有包含USB控制器,因為USB控制器都是Windows自帶的驅動,下載了一個南橋驅動,安裝后并沒有效果;
    2、Windows自帶的驅動原來都是放在C:\Windows\System32\DriverStore\FileRepository下,USB控制器相關的驅動,就在usbport.inf_amd64_xxxxxxxxxx文件夾里,xxxxxxxxx是一串16進制數字,悲催的是,安裝這個驅動時要不提示找不到指定文件,要不說第三方INF沒有簽名;
    3、自己折騰搞不定,找個軟件吧,第一個想到的是驅動之家官方的驅動精靈,下載下來發現是個全家桶啊,什么騰訊管家,金山毒霸,瀏覽器首頁修改一應俱全,而且沒有鼠標點擊,用TAB鍵根本移動不到取消框,只好默認全部安裝了,裝完啟動,檢測,提示系統自帶驅動缺失,于是回車修復,但每次修復完,重新檢測還是那樣,而且沒有提示USB控制器驅動安裝有問題,有些功能用鍵盤無法操作,不知道是不是還有哪里可以操作一下,于是又搜了一下,如何用鍵盤代替鼠標,居然真的找到了!
    4、按WIN鍵,輸入設置,回車,打開設置主頁:

    移到“輕松使用”,進去后左邊選擇“鼠標”,在右邊啟用“使用數字小鍵盤在屏幕上移動鼠標”(按空格鍵開關),最好把三個開關都打開,如果沒有啟用CTRL鍵加速功能,鼠標移動非常慢:

    好了,現在可以用小鍵盤移動鼠標了;
    5、繼續回到驅動精靈,再次修復,還是不行啊,這個東西除了帶來一堆垃圾,什么作用都沒有!于是把它帶來的垃圾以及它自己卸載了。
    6、似乎360也有一個驅動大師,于是就下載了一個,這個倒是很純潔,但是功能太弱,完全沒發現問題;
    7、剛才搜索“安裝驅動 找不到指定的文件”時,發現一個論壇提到了這個,是驅動人生的論壇,好像還有解決方案,但要注冊才能下載,難道驅動人生可以解決?于是就下載了一個驅動人生,安裝的時候還是附帶全家桶,不過現在可以用鍵盤移動鼠標取消了,只安裝了驅動人生自己,跟剛才兩個軟件不同的是,它提示USB外設驅動沒有安裝,于是點修復,結果反反復復出現等待光標,就是無法完成;
    8、之前又搜索到,驅動安裝的日志在C:\Windows\INF\setupapi.dev.log文件里,于是打開這個文件,發現Driver package failed signature verification. Error = 0xE000022F,驅動程序簽名有問題,所以不能安裝成功;
    9、Windows10有個高級啟動選項是禁用強制驅動簽名,如何進入高級啟動選項呢?以前是按F8,現在不行了,要在設置里面,更新和安全,恢復,高級啟動,立即重啟,然后設置疑難解答,高級啟動,再重啟,就可以進入高級啟動菜單,按7進入禁止強制驅動簽名模式,重啟后再用驅動人生修復,果然成功了;
    10、打開驅動人生下載目錄,DTLFolder\DriversDownLoad,發現它下載了USB驅動目錄是USB_10.0.10240.16384_WHQL_107049,里面文件如下:

    除了第一個xml文件是程序自己用的外,其他都是USB驅動用到的文件,點右鍵發現那幾個sys文件,除了usbohci.sys和usbuhci.sys外,其他都有數字簽名,而usbuhci.sys我這里沒用到,問題就出在usbohci.sys上:


    11、難道是驅動人生替換了未簽名的文件?圖謀不軌?為了驗證一下,我又下載了一個Windows10安裝光盤(版本是當前使用的1703版):
    cn_windows_10_multiple_editions_version_1703_updated_march_2017_x64_dvd_10194190.iso
    12、怎么提取安裝光盤中的內置驅動?找了一下,原來Windows的安裝盤從VISTA起,不再使用XP以前的I386目錄和Drivers.cab文件存放驅動,而是打包在一個Install.wim鏡像文件中,要找到驅動文件,必須用工具提取,這個工具就是Imagex.exe,微軟自己做的命令行工具,但是我的電腦上沒有,于是下載了一個64位的,放在C盤根目錄,通過如下命令即可提取:
    c:\IMAGEX_x64 /mount f:\sources\install.wim 5 i:\1703
    其中F盤是在iso文件上點右鍵,打開方式選“Windows資源管理器”打開后虛擬出來的盤符,其實就相當于系統自帶的虛擬光驅,I盤是硬盤,用來存放掛載的鏡像文件,5是選擇掛載哪一個版本的Windows(多合一版),如果不知道要掛哪個,把這個數字改成100,會顯示xml文件內容,并提示找不到這個索引號,從xml文件內容就能找到各版本的信息,然后再重新掛載正確的即可。這個掛載其實是個解壓縮過程,時間很長,提取完之后就跟安裝好了Windows一樣,目錄都列好了。
    今天又發現另一個圖形化的工具,Dism++,比這個操作更簡單。Dism是PowerShell內置的命令,也是與鏡像有關,也能掛載提取,但用了一下似乎提示權限有問題,Dism++是國內開源愛好者自己開發的圖形化工具,與Dism沒有關系。
    13、好了,原版的Windows已經準備好,進入Windows\System32\DriverStore\FileRepository目錄,搜索usbohci.sys,在usbport.inf_amd64_8e5f608c0111283d目錄下,點右鍵一看,也是沒簽名的:
    這不是坑爹嗎?你自己帶的東西都沒簽名,然后又不給用!!!!
    14、有點懷疑是Windows自己更新的時候修改了一些策略,導致之前可以用的不能用了,為了再次驗證,又繼續下載了兩個版本的Windows10安裝光盤,分別是早期的1607和最新的1709,找到usbohci.sys,如下所示:
    從左到右依次為1607,1703,1709,均未簽名,基本可以判斷是Windows自己抽風了。
    15、昨天晚上搞到12點多,搞定鼠標后沒有重啟測試,今天早上開機,果然發現鼠標又不能用了,因為我沒有選擇禁用強制簽名選項來啟動,系統發現那個沒簽名的驅動,就把它停了,嘗試卸載,結果再裝也裝不上,于是只好又設置高級啟動,重新禁止強制簽名,進入系統,裝上驅動,恰好這時Windows又在后臺偷偷摸摸地更新,不知道更新了啥,讓我重啟。
    16、重啟之后,奇跡出現,剛才明明提示沒有簽名強制安裝的驅動,現在居然正常啟動也沒問題了,而且查看驅動詳情的時候出現了矛盾的一幕:
    外面顯示數字簽名者:未經數字簽名,里面的sys文件又顯示數字簽名者是Microsoft Windows,然而進入C:\Windows\System32\drivers目錄,找到usbohci.sys,點右鍵,卻發現并沒有數字簽名:
    好吧,你開心就好,反正不要再禁我的鼠標就行……
    感謝這次蛋疼的折騰之旅,讓我知道了驅動程序來自哪里,安裝日志在哪里,哪個軟件坑爹又沒用,怎么玩安裝盤,怎么用鍵盤操作鼠標,怎么進入高級啟動界面……
    我為什么要知道這些??????????????為微軟的疏忽買單啊!!!!!
    啥都不說了,它又提示我重啟了,不知道又有什么奇跡會發生……
    posted @ 2017-11-19 12:43 amp@java 閱讀(1156) | 評論 (0)編輯 收藏

    2017年11月17日 #

     每次換手機,把舊手機的數據遷移到新手機就是個很麻煩的事情,幸好最近華為的“手機克隆”APP越來越強大,居然能夠把微信的聊天記錄包括圖片原封不動地遷移到新手機上,以前用微信自帶的聊天記錄轉移功能只能轉移文字信息,圖片視頻全部丟失,不知道現在的怎么樣。手機克隆還能把SD卡的內容也轉移過來,基本滿足了需要。
    但是要把手機上的東西傳到電腦就沒那么簡單了,現在已經沒有了以前的大容量存儲模式,只能選擇MTP模式,這種模式其實不是一個完整的文件系統,有很多限制,所以一些傳統的軟件讀取不到,例如FastCopy是用不了的,用Windows自帶的文件管理器來復制,開始計算時間就要等很久,中間出了個錯就前功盡棄;還有通過手機上的APP訪問電腦共享的方式,在手機上復制也可以,但是同樣會莫名其妙卡死,FTP同理,折騰了好久,還是覺得自己動手比較好。
    MTP協議在維基百科里解釋得比較清楚:https://en.wikipedia.org/wiki/Media_Transfer_Protocol ,簡單點說就是:
    1、不是以塊設備的形式訪問,跟U盤不同;
    2、只能單線程訪問,不能同時進行多個操作,只能一個接一個;
    3、控制權在設備上,對外展示的內容由設備決定;
    4、默認不能直接對文件進行部分修改,只能復制過來修改完再復制回去,但Android對協議做了擴展,能夠修改部分文件內容;
    5、在Linux上有些軟件能夠把它掛載為文件系統,這樣其他軟件就能像訪問普通文件系統一樣訪問了,但是Windows下似乎沒有。

    不過有人開發了一個在Windows下通過JNI實現的Java庫jmtp,項目托管在Google Code,被墻了,但是GitHub有人fork了一個,可以下載下來,我下載的是https://github.com/reindahl/jmtp
    里面包含了C++的代碼和Java的代碼,以及兩個已經編譯好的dll文件,分別用于Win32和Win64,把其中一個dll文件放在工程目錄下,再把Java源代碼加入工程中即可使用,文檔比較簡陋,但是看test目錄下的MtpTest.java,基本可以摸到如何使用了,這個協議比較簡單,其實沒什么功能,我要的只是把文件復制到電腦上。
    根據MtpTest.java,稍微修改一下,做個遞歸復制即可把手機上的所有文件復制到電腦上:
    package test;

    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.math.BigInteger;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.rmi.server.SocketSecurityException;
    import java.util.ArrayList;


    import jmtp.PortableDevice;
    import jmtp.PortableDeviceFolderObject;
    import jmtp.PortableDeviceManager;
    import jmtp.PortableDeviceObject;
    import jmtp.PortableDeviceStorageObject;


    public class TestApp {

       

        
    public static void main(String[] args) {
            
    // TODO Auto-generated method stub
            
            ArrayList
    <PortableDeviceStorageObject> devices = new ArrayList<>();

            PortableDeviceManager manager 
    = new PortableDeviceManager();

            
    for (PortableDevice device : manager) {
                System.out.println(device);
                device.open();
                
                
    // Iterate over deviceObjects
                for (PortableDeviceObject object : device.getRootObjects()) {
                    String storageName
    =object.getName();
                    System.out.println(storageName);

                    
    // If the object is a storage object
                    if (object instanceof PortableDeviceStorageObject) {
                        PortableDeviceStorageObject storage 
    = (PortableDeviceStorageObject) object;
                        System.out.println(storage.getChildObjects().length);
                        
    for (PortableDeviceObject child : storage.getChildObjects()) {
                                copyall(child,
    "E:\\手機備份\\"+object.getName());
                        }
                    }
                }

                device.close();
                System.out.println(size);
            }

      
      
        }
        
        
    public static void copyall(PortableDeviceObject obj,String path) {

            if(obj instanceof PortableDeviceFolderObject) {
              

                String objName=obj.getName();
                
    if(objName.contains(":")) {
                    objName
    =objName.replace(':''');
                }
                String newPath 
    = path+"\\"+objName;
                System.out.println(
    "創建文件夾:"+newPath);
                
                File file = new File(newPath);
                if(!file.exists()) {
                    file.mkdirs();
                }
                for(PortableDeviceObject subObj:((PortableDeviceFolderObject) obj).getChildObjects()) {             
                    copyall(subObj,newPath);
                }
            }
            
    else {      
                
    if(obj.getName().contains(":"))
                    
    return;
                System.out.println(
    "開始復制文件到:"+path+"\\"+obj.getName());
                File file 
    = new File(path);
                obj.copy(file.toPath());                     
                System.out.println(
    "文件復制完成!");
            }
        }

    }
    其中發現有點問題:
    1、Android設備文件名里是可以包含冒號(:)的,但Windows是不可以的,所以復制到這些文件的時候會有問題,于是遇到目錄名這樣就把它改為中文的冒號(:),但是遇到文件名這樣就不行了,因為這個庫的copy函數只需要指定目標目錄,不需要指定目標文件名,所以這些文件只能放棄;
    2、Android手機的MTP協議是由“媒體存儲”這個系統APP控制的,有時候手機上可以看到的文件,通過MTP訪問卻怎么也看不到,重啟手機也不行,應該就是這個APP沒有更新數據,需要把它的系統數據清除掉,等它重建完重新訪問就可以看到了,不過這個重建時間非常長,可以查看它數據占用的空間,剛清除之后會發現它占用的空間會不斷增長,到了不增長的時候就是重建完了,就可以正常訪問了;
    3、這個庫有時候還有點bug,有一次發現它讀取到的文件和文件夾都沒有了最后一個.后面的部分,所以總是卡住,重新插拔一下手機數據線又沒問題了;
    4、為了避免復制了半天結果發現不完整,又要重來,最好在復制前先統計一下文件大小,看看跟手機上看到的占用存儲空間是不是一致,對于MTP設備上的文件,可以通過getSize函數得到大小,把上面復制操作改為大小累加即可,速度比復制快一些,不過由于小文件太多,也不會快很多。

    把手機里的文件復制到電腦后,通過一些簡單的分析,發現有很多其實是垃圾來的,也可以為手機空間清理提供參考,因為在電腦上分析起來比在手機上方便一些。例如一些視頻APP的緩存,居然超過1G,占用了寶貴的內部存儲空間,之前一直都沒發現,通過電腦里的按文件大小搜索才發現。
    posted @ 2017-11-17 14:54 amp@java 閱讀(2256) | 評論 (0)編輯 收藏

    2017年2月10日 #

    09款老速騰,不支持USB和AUX,要聽歌除了CD以外,就是刻錄在CD上的MP3了,以前不知道用什么軟件刻錄了一張碟,能夠完美地顯示中文文件名和ID3信息,前幾天用ImgBurn刻了一張,發現中文是亂碼,開始以為是ID3信息顯示亂碼,于是下載了一個Mp3Tag,把所有ID3信息都清除,結果顯示文件名依然亂碼,重新寫入ID3信息,發現ID3可以正常顯示,但文件名還是亂碼。
    于是就把原來那張可以正常顯示中文的碟拿來研究一下,發現它的ID3標簽只是ID3v1,而后來重新寫入的ID3是ID3v2.3,兩個都可以正常顯示中文,說明ID3信息是正常的,文件名亂碼不是這個問題。
    但是不知道用什么軟件來顯示光盤的文件系統,只能一次次摸索。
    幸好有一張CD-RW可以反復嘗試。
    ImgBurn默認是使用ISO9660+UDF,而ISO 9660則使用最老的ISO 9660文件系統,也就是1988版本,文件名默認是不支持中文的,不知道是不是這個原因,于是就把文件系統改為ISO 9660+Joliet,如下圖:

    并且把ISO 9660標準改為1999:

    可能是因為字符編碼那里改為了ASCII,所以就好了。
    后來又嘗試只使用UDF文件系統,結果認不出碟。



    所以,目前能夠使用中文的環境其實就是:
    ImgBurn使用ISO 9660 1999標準;
    ID3使用v1或v2.3都可以。

    posted @ 2017-02-10 09:17 amp@java 閱讀(402) | 評論 (0)編輯 收藏

    2016年1月12日 #

    最近新部署了一個信息系統,廠家居然沒有升級方案,所有數據都要重新輸入,包括用戶、角色等都要重新配置,真是操蛋。要是一個個錄入簡直是日狗了,這些用戶在其他信息系統早已存在,但是每個都復制粘貼提交一遍也不是辦法,于是就想用程序自動完成這些操作。步驟如下:
    1、從其他信息系統的數據庫導出用戶信息,也可以直接從其他信息系統的界面把所有用戶信息復制下來放在一個文本文件里,反正就是準備好數據源;
    2、在需要錄入用戶信息的系統中,用人工操作的方式登錄系統,并錄入一個用戶,同時用Wireshark抓包,查看整個過程要提交一些什么樣的表單數據;
    3、在程序中用httpclient提交同樣的數據,完成登錄,并從第1步的數據源中讀取用戶信息,然后循環提交錄入用戶所需的數據,完成用戶的錄入;
    4、新系統沒有默認的用戶角色,是需要一個個修改的,是根據用戶的ID來確定當前修改的用戶,并且提交一個角色ID來進行設定,因此需要首先獲取用戶的ID,然后根據該ID來提交角色ID,而用戶ID是通過用戶列表頁面獲取到的,因此還需要通過正則表達式來獲取所有用戶的ID,然后循環提交角色ID,完成角色設定。

    花了不少時間才搞定,有幾個地方需要注意:
    1、如果表單數據不包含中文,直接把表單的Name和Value加在HttpPost的URL的?后面即可,不需要專門建立NameValuePair,如下所示:
    HttpPost httppost = new HttpPost("http://1.1.1.1/test/adduser?userid=abc&username=efg");
    httpclient.execute(httppost);

    但是,如果表單數據包含中文,例如姓名,用這種方式提交的表單數據會出現亂碼,即使通過URLEncoder進行編碼后再發也不行,必須建立NameValuePair,再加到HttpPost的Entity里面,如下所示:
    HttpPost httppost = new HttpPost("http://1.1.1.1/test/adduser?userid=abc");
    List
    <NameValuePair> nvps = new ArrayList<NameValuePair>();
    nvps.add(
    new BasicNameValuePair("username","張三"));
    httppost.setEntity(
    new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
    httpclient.execute(httppost);

    2、用于網頁內容查找的正則表達式的使用方式一般為:
    Pattern p = Pattern.compile(".*?abc(whattoget)123.*?");
    Matcher m = p.matcher(line);
    if(m.matches()){
         String whattoget 
    = m.group(1);
    }
    ".*?abc(whattoget)123.*?"就是一個正則表達式,如果用于匹配一行的時候,由于要查找的內容是在行中間,所以前后需要加上.*?,表示前后可以是任意字符,也可以什么都沒有,而表達式中間的(whattoget)就表示一個group,編號為1,編號為0的group是整個匹配的字符串,找到之后提取group(1)即可得到想要的內容。
    測試正則表達式是一項很麻煩的工作,不過有個很好的軟件可以完成此工作:RegexBuddy,支持各種語言的正則表達式的調試。
    學習正則表達式的寶典是《Mastering Regular Expression》。
    posted @ 2016-01-12 15:54 amp@java 閱讀(3574) | 評論 (1)編輯 收藏

    2015年4月2日 #

    今天遇到一個非常奇怪的問題,有臺裝XP的電腦,插上USB鍵盤沒反應,還以為是鍵盤壞了,又找來兩個不同型號的鍵盤,依然不行,又以為是USB接口壞了,結果在電腦啟動的時候又可以按F2進入BIOS,這樣就只有一個原因,Windows的驅動沒裝上了。
    幸好這電腦還有傳統的PS/2口,而且插上就能識別,否則連Windows都進不去,因為按Ctrl+Alt+Del沒反應。
    進去之后提示安裝USB鍵盤驅動,到最后一步提示安裝失敗,原因是拒絕訪問。
    上網搜了一下,安裝驅動拒絕訪問的其中一個原因是注冊表有個鍵的權限設置有問題,改過來即可,但是我打開注冊表,連那個鍵都沒找到,不是這個原因。
    不過從這個解決方案中也知道了驅動安裝的日志是在Windows目錄下的setupapi.log文件里面,于是打開那個文件,發現每次安裝都有兩個拒絕訪問的錯誤,但并沒有說是注冊表拒絕訪問,在拒絕訪問之前,還提到一個叫MlCoInst.dll的文件沒有簽名。
    上網搜MlCoInst.dll,沒有找到任何結果,在System32目錄下找到它,看屬性,果然沒有簽名,是個三無文件,不知道為什么每次安裝驅動都要調用它。
    日志里面還提到了“共同安裝程序”,似乎和CoInst有點關聯,于是又查了一下,原來安裝驅動的時候可以通過調用“共同安裝程序”來實現某些目的,例如修改驅動程序的簽名狀態,欺騙操作系統,這樣就可以只安裝一次驅動即可,不用每次插入都安裝一次。
    于是嘗試把MlCoInst.dll刪除,提示刪除失敗。
    在注冊表里面搜索MlCoInst.dll,把所有找到的鍵值都刪除,再次插拔鍵盤,順利安裝完畢,刪除MlCoInst.dll,也成功了,果然是它的問題。

    這個應該是某個USB設備的驅動引進來的,而且修改了usb.inf,每次安裝任何USB設備都要調用它,但它可能與Windows的簽名機制有沖突,所以導致安裝失敗,真是坑爹!
    posted @ 2015-04-02 17:04 amp@java 閱讀(3605) | 評論 (0)編輯 收藏

    2015年3月10日 #

    做GUI程序的時候,通常有個后臺工作線程在努力工作,但是中間又需要一些暫停,而關閉程序的時候,必須立即結束那個線程,退出程序,也有的時候需要停止后臺線程,但不關閉程序。例如,做一個目錄監控程序,發現目錄中有文件的時候,執行一定的操作,執行完之后沒有文件了,就要暫停一下,過幾秒或幾分鐘再次檢測,這時候就要對線程進行暫停操作,如果在暫停的時候,用戶要關閉程序,就必須馬上停止線程,如果用戶需要暫停檢測,按下某個按鈕后,需要讓線程馬上停止,但再次按下某個按鈕,線程又必須馬上開始。

    以前我都是通過檢測停止標記和用Thread.sleep(time)來完成的,后臺線程的每次循環都要檢查停止標記,如果發現停止標記已設定,就不再循環,退出線程,在線程內部,如果需要暫停,就執行Thread.sleep(time)。通過把線程的setDaemon(true)方法,還可以讓線程作為后臺線程,當圖形界面關閉后,線程也自動退出。

    但是,這種方式有個問題,如果我需要在圖形界面上點擊按鈕來停止線程,但并不退出程序,而點擊按鈕的時候線程正處于sleep狀態,就對它沒有任何辦法,只能讓它醒過來再操作,如果sleep的時間比較長,例如1分鐘,那么點擊按鈕之后,用戶最多要等1分鐘才能把線程停下來。當然,Thread對象有個interrupt方法,但是已經被標記為過期,一般不建議使用了。感謝評論中watchzerg的提醒,Thread的interrupt()并沒有標記為過期,可以按照他的說法來操作,更為簡單。

    怎么讓線程能暫停,又能隨時叫醒呢?原來Java里最原始的對象Object就自帶此功能。

    每個Object都有wait(time)和notify()方法,前者就是讓擁有該Obejct的線程處于暫停狀態,后者則讓線程馬上喚醒,通過這兩個方法,就能夠滿足上述的所有要求。

    首先,建立一個同步對象:
    Object syncObj = new Object();

    然后在線程中需要暫停的地方,調用該對象的wait(time)方法:
    synchronized (syncObj) {
           syncObj.wait(60*1000);
    }

    在圖形界面的按鈕監聽事件中,對該對象執行notify()方法:
            button_1.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {

                    thread.setStop(true);
                    synchronized (syncObj) {
                            syncObj.notify();
                     }
                    //為了等待線程退出,還可以加上以下語句:
                    thread.join();

                }
            });

    posted @ 2015-03-10 16:52 amp@java 閱讀(7692) | 評論 (2)編輯 收藏

    Eclipse有個功能就是把整個項目打包成一個可執行的Jar文件,里面包含了所有項目引用了的庫,如果電腦上安裝了JRE,直接雙擊就可以啟動,看起來很方便,如下圖所示:

    可以選擇把所有類庫打包進去,也就是Jar里面還包含一堆Jar:

    還可以生成ant腳本:


    生成的jar文件,可以直接通過java -jar xx.jar啟動,簡單快捷。


    然而,這樣導出來的可執行jar,啟動速度卻非常慢,這跟包的大小有關。有個項目導出來的包有40MB,結果啟動需要近一分鐘,在性能差的電腦上,甚至要幾分鐘,就是你執行完命令后,沒有任何界面顯示,但是查看任務管理器發現java進程的CPU占用率在浮動,說明正在努力啟動,過了一段時間之后界面突然顯示出來,簡直讓人崩潰。可是在Eclipse里面運行,卻是一點就開。

    我開始嘗試縮小導出的包。但是Eclipse的導出對話框并沒有提供需要打包哪些庫的選項,都是默認把所有庫都打包進去,但是它可以生成ant腳本,可以通過編輯ant腳本的方式來減少不必要的庫。經過多次嘗試,終于把40MB的包縮成了17MB,啟動速度快了一些,但是依然需要半分鐘以上。

    后來覺得,能不能不打包直接運行呢?于是把導出來的包用壓縮軟件解壓,再把里面包含的jar包繼續解壓,最后得到一堆沒有壓縮的class文件,再通過指定classpath的方式,直接運行程序入口class,發現啟動速度和在Eclipse里面一樣,一點就開。

    打包的好處是部署簡單,只需要一個文件,但帶來的缺點實在不能忍,打散的方式部署起來稍微難一點,但是啟動速度夠快,對普通用戶來說,這個才是最重要的。

    當然,還有一種方式是像Eclipse那樣,啟動時顯示一個圖片,底下一個進度條顯示啟動進度,不過這樣也加大了工作量,而且每次都要等那進度條,實際上也很煩。
    posted @ 2015-03-10 15:07 amp@java 閱讀(5143) | 評論 (0)編輯 收藏

    2015年2月15日 #

         摘要: JMF太老了,各種問題得不到解決,Oracle也沒再升級過,如果能找到新東西,最好能把它扔掉。最近OpenCV比較火,還有人用Java封裝了OpenCV,成立了JavaCV項目,通過改造VideoInput這個基于C語言的項目,能夠用Java來調用攝像頭,JMF可以扔掉了。如果想測試,非常簡單,把那些編譯好的jar文件放入Build Path即可,如果是在Windows X86環境下,則只需要把帶...  閱讀全文
    posted @ 2015-02-15 11:41 amp@java 閱讀(10430) | 評論 (7)編輯 收藏

    2014年12月16日 #

    其實我覺得用java結合SQL來做同樣的事情更簡單,但是EXCEL的好處是不用編程,雖然里面有好多陷阱,但是掌握之后能夠熟練運用還是比每次都編個程序更簡單。VLOOKUP函數的使用比較復雜,搞了好久都不明白,終于找到了下面這篇文章,解決了很多問題。

    以下是轉載文章,原來的出處不知道是哪里,應該是來自

    http://www.excelpx.com
    但具體的地址不詳。



    VLOOKUP函數是Excel中幾個最重函數之一,為了方便大家學習,蘭色幻想特針對VLOOKUP函數的使用和擴展應用,進行一次全面綜合的說明。本文為入門部分

         一、入門級

          VLOOKUP是一個查找函數,給定一個查找的目標,它就能從指定的查找區域中查找返回想要查找到的值。它的基本語法為:

          VLOOKUP(查找目標查找范圍返回值的列數精確OR模糊查找)

    下面以一個實例來介紹一下這四個參數的使用

         例1:如下圖所示,要求根據表二中的姓名,查找姓名所對應的年齡。  

       公式:B13 =VLOOKUP(A13,$B$2:$D$8,3,0)  

       參數說明:

           1 查找目標:就是你指定的查找的內容或單元格引用。本例中表二A列的姓名就是查找目標。我們要根據表二的“姓名”在表一中A列進行查找。

            公式:B13 =VLOOKUP(A13,$B$2:$D$8,3,0)   

           2 查找范圍(VLOOKUP(A13,$B$2:$D$8,3,0) : 指定了查找目標,如果沒有說從哪里查找,EXCEL肯定會很為難。所以下一步我們就要指定從哪個范圍中進行查找。VLOOKUP的這第二個參數可以從一個 單元格區域中查找,也可以從一個常量數組或內存數組中查找。本例中要從表一中進行查找,那么范圍我們要怎么指定呢?這里也是極易出錯的地方。大家一定要注 意,給定的第二個參數查找范圍要符合以下條件才不會出錯:

            A 查找目標一定要在該區域的第一列。本例中查找表二的姓名,那么姓名所對應的表一的姓名列,那么表一的姓名列(列)一定要是查找區域的第一列。象本例中,給定的區域要從第二列開始,即$B$2:$D$8,而不能是$A$2:$D$8。因為查找的“姓名”不在$A$2:$D$8區域的第一列。

            B 該區域中一定要包含要返回值所在的列,本例中要返回的值是年齡。年齡列(表一的D列)一定要包括在這個范圍內,即:$B$2:$D$8,如果寫成$B$2:$C$8就是錯的。

           3 返回值的列數(B13 =VLOOKUP(A13,$B$2:$D$8,3,0))。這是VLOOKUP第3個參數。它是一個整數值。它怎么得來的呢。它是“返回值”在第二個參數給定的區域中的列數。本例中我們要返回的是“年齡”,它是第二個參數查找范圍$B$2:$D$8的第3列。這里一定要注意,列數不是在工作表中的列數(不是第4列),而是在查找范圍區域的第幾列。如果本例中要是查找姓名所對應的性別,第3個參數的值應該設置為多少呢。答案是2。因為性別在$B$2:$D$8的第2列中。

           4 精確OR模糊查找(VLOOKUP(A13,$B$2:$D$8,3,0)  ),最后一個參數是決定函數精確和模糊查找的關鍵。精確即完全一樣,模糊即包含的意思。第4個參數如果指定值是0或FALSE就表示精確查找,而值為1 或TRUE時則表示模糊。這里蘭色提醒大家切記切記,在使用VLOOKUP時千萬不要把這個參數給漏掉了,如果缺少這個參數默為值為模糊查找,我們就無法精確查找到結果了。  

          好了,關于VLOOKUP函數的入門級應用就說到這里,VLOOKUP函數可不只是這么簡單的查找,我們講的還只是1/10的用法。其他的沒法在一篇文章中說明。敬請期待“VLOOKUP的使用方法-進階篇”吧。

     

    上一講咱們學習了VLOOKUP的基本用法和示例,本講將介紹VLOOKUP在使用中的一些小技巧。

    Excel函數速成教程全系列(包括VLOOKUP函數,IF函數,offset函數,sumif函數等66個函數)預計6月初全部錄制完成,現已在淘寶開始預訂(8折優惠),地址:http://item.taobao.com/item.htm?id=17500884347

    一、VLOOKUP多行查找時復制公式的問題

        VLOOKUP函數的第三個參數是查找返回值所在的列數,如果我們需要查找返回多列時,這個列數值需要一個個的更改,比如返回第2列的,參數設置為2,如 果需要返回第3列的,就需要把值改為3。。。如果有十幾列會很麻煩的。那么能不能讓第3個參數自動變呢?向后復制時自動變為2,3,4,5。。。   

        在EXCEL中有一個函數COLUMN,它可以返回指定單元格的列數,比如

             =COLUMNS(A1) 返回值1

             =COLUMNS(B1) 返回值2

       而單元格引用復制時會自動發生變化,即A1隨公式向右復制時會變成B1,C1,D1。。這樣我們用COLUMN函數就可以轉換成數字1,2,3,4。。。 

        例:下例中需要同時查找性別,年齡,身高,體重。

       

         公式:=VLOOKUP($A13,$B$2:$F$8,COLUMN(B1),0)

      公式說明:這里就是使用COLUMN(B1)轉化成可以自動遞增的數字。

    二、VLOOKUP查找出現錯誤值的問題。

        1、如何避免出現錯誤值。

         EXCEL2003 在VLOOKUP查找不到,就#N/A的錯誤值,我們可以利用錯誤處理函數把錯誤值轉換成0或空值。

          即:=IF(ISERROR(VLOOKUP(參數略)),"",VLOOKUP(參數略)

         EXCEL2007,EXCEL2010中提供了一個新函數IFERROR,處理起來比EXCEL2003簡單多了。

         IFERROR(VLOOKUP(),"") 

        2、VLOOKUP函數查找時出現錯誤值的幾個原因

          A、實在是沒有所要查找到的值

          B、查找的字符串或被查找的字符中含有空格或看不見的空字符,驗證方法是用=號對比一下,如果結果是FALSE,就表示兩個單元格看上去相同,其實結果不同。

          C、參數設置錯誤。VLOOKUP的最后一個參數沒有設置成1或者是沒有設置掉。第二個參數數據源區域,查找的值不是區域的第一列,或者需要反回的字段不在區域里,參數設置在入門講里已注明,請參閱。

         D、數值格式不同,如果查找值是文本,被查找的是數字類型,就會查找不到。解決方法是把查找的轉換成文本或數值,轉換方法如下:

         文本轉換成數值:*1或--或/1

         數值轉抱成文本:&""  

         VLOOKUP函數的初級篇就說到這里了,咱們下一講將介紹VLOOKUP的模糊查找有、反向查找等。

     

     在學習了VLOOKUP的入門和初級篇后,本文將帶將大家學習VLOOKUP的進階篇:VLOOKUP的模糊查找。

        一、字符的模糊查找    

            在A列我們知道如何查找型號為“AAA”的產品所對應的B列價格,即:

        =VLOOKUP(C1,A:B,2,0)

           如果我們需要查找包含“AAA”的產品名稱怎么表示呢?如下圖表中所示。

         公式=VLOOKUP("*"&A10&"*",A2:B6,2,0)  

        公式說明:VLOOKUP的第一個參數允許使用通配符“*”來表示包含的意思,把*放在字符的兩邊,即"*" & 字符 & "*"。

       二、數字的區間查找

          數字的區間查找即給定多個區間,指定一個數就可以查找出它在哪個區間并返回這個區間所對應的值。

        在VLOOKUP入門中我們提示VLOOKUP的第4個參數,如果為0或FALSE是精確查找,如果是1或TRUE或省略則為模糊查找,那么實現區間查找正是第4個參數的模糊查找應用。

        首先我們需要了解一下VLOOKUP函數模糊查找的兩個重要規則:

        1、引用的數字區域一定要從小到大排序。雜亂的數字是無法準確查找到的。如下面A列符合模糊查找的前題,B列則不符合 

        

        2、模糊查找的原理是給一定個數,它會找到和它最接近,但比它小的那個數。詳見下圖說明。

        

       最后看一個實例: 

        例:如下圖所示,要求根據上面的提成比率表,在提成表計算表中計算每個銷售額的提成比率和提成額。

        

       公式:=VLOOKUP(A11,$A$3:$B$7,2)

       公式說明:

        1、上述公式省略了VLOOKUP最后一個參數,相當于把第四個參數設置成1或TRUE。這表示VLOOKUP要進行數字的區間查找。

        2、圖中公式中在查找5000時返回比率表0所對應的比率1%,原因是0和10000與5000最接近,但VLOOKUP只選比查找值小的那一個,所以公式會返回0所對應的比率1%。

     

     前言:前面我們分別學習了VLOOKUP函數的入門、初級和進階篇。今天我們學習VLOOKUP函數的高級應用部分-VLOOKUP函數的數組應用。(本文由蘭色幻想原創,轉載請注明轉自excel精英培訓

     一、VLOOKUP的反向查找。

        一般情況下,VLOOKUP函數只能從左向右查找。但如果需要從右向右查找,則需要把區域進行“乾坤大挪移”,把列的位置用數組互換一下。

        例1:要求在如下圖所示表中的姓名反查工號。

         

        公式:=VLOOKUP(A9,IF({1,0},B2:B5,A2:A5),2,0)

        公式剖析:

            1、這里其實不是VLOOKUP可以實現從右至右的查找,而是利用IF函數的數組效應把兩列換位重新組合后,再按正常的從左至右查找。

            2、IF({1,0},B2:B5,A2:A5)這是本公式中最重要的組成部分。在EXCEL函數中使用數組時(前提時該函數的參數支持數組),返回的結 果也會是一個數組。這里1和0不是實際意義上的數字,而是1相關于TRUE,0相當于FALSE,當為1時,它會返回IF的第二個參數(B列),為0時返 回第二個參數(A列)。根據數組運算返回數組,所以使用IF后的結果返回一個數組(非單元格區域):{"張一","A001";"趙 三","A002";"楊五","A003";"孫二","A004"}

     二、VLOOKUP函數的多條件查找。

          VLOOKUP函數需要借用數組才能實現多條件查找。

         例2:要求根據部門和姓名查找C列的加班時間。

         分析:我們可以延用例1的思路,我們的努力方向不是讓VLOOKUP本身實現多條件查找,而是想辦法重構一個數組。多個條件我們可以用&連接在一起,同樣兩列我們也可以連接成一列數據,然后用IF函數進行組合。

        公式:{=VLOOKUP(A9&B9,IF({1,0},A2:A5&B2:B5,C2:C5),2,0)}

        公式剖析:

           1、A9&B9 把兩個條件連接在一起。把他們做為一個整體進行查找。

           2、A2:A5&B2:B5,和條件連接相對應,把部分和姓名列也連接在一起,作為一個待查找的整體。

           3、IF({1,0},A2:A5&B2:B5,C2:C5) 用IF({1,0}把連接后的兩列與C列數據合并成一個兩列的內存數組。按F9后可以查看的結果為:

           {"銷售張一",1;"銷售趙三",5;"人事楊五",3;"銷售趙三",6}

           4、完成了數組的重構后,接下來就是VLOOKUP的基本查找功能了,另外公式中含有多個數據與多個數據運算(A2:A5&B2:B5),,所以必須以數組形式輸入,即按ctrl+shift后按ENTER結束輸入。

         三、VLOOKUP函數的批量查找。

         VLOOKUP一般情況下只能查找一個,那么多項該怎么查找呢?

         例3 要求把如圖表中所有張一的消費金額全列出來

         分析:經過前面的學習,我們也有這樣一個思路,我們在實現復雜的查找時,努力的方向是怎么重構一個查找內容和查找的區域。要想實現多項查找,我們可以對查找的內容進行編號,第一個出現的是后面連接1,第二個出現的連接2。。。

         公式:{=VLOOKUP(B$9&ROW(A1),IF({1,0},$B$2:$B$6&COUNTIF(INDIRECT("b2:b"&ROW($2:$6)),B$9),$C$2:$C$6),2,)}

         公式剖析:

            1、B$9&ROW(A1) 連接序號,公式向下復制時會變成B$9連接1,2,3

            2、給所有的張一進行編號。要想生成編號,就需要生成一個不斷擴充的區域(INDIRECT("b2:b"&ROW($2:$6)),然后在這個逐行擴充的區域內統計“張一”的個數,在連接上$B$2:$B$6后就可以對所有的張一進行編號了。

           3、IF({1,0}把編號后的B列和C組重構成一個兩列數組

         通過以上的講解,我們需要知道,VLOOKUP函數的基本用法是固定的,要實現高級查找,就需要借助其他函數來重構查找內容和查找數組。

         至此VLOOKUP函數從入門到高級的四篇VLOOKUP函數使用教程全部結束了,VLOOKUP函數在數組運算中還有著其他應用,但只是配角了,所以本系列不再介紹。由于筆者水平有限,不免有錯漏之處,請大家多多指點。

    posted @ 2014-12-16 09:10 amp@java 閱讀(519) | 評論 (0)編輯 收藏

    僅列出標題  下一頁
    主站蜘蛛池模板: 免费高清A级毛片在线播放| 亚洲欧美日韩一区二区三区在线| 国产JIZZ中国JIZZ免费看| 大胆亚洲人体视频| 免费观看又污又黄在线观看| 日韩亚洲国产综合久久久| 色视频在线观看免费| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 最近中文字幕mv免费高清电影 | 亚洲国产日韩精品| 成人免费毛片视频| 久久久久亚洲精品无码网址色欲 | 亚洲精品乱码久久久久蜜桃| 免费黄色app网站| 另类小说亚洲色图| 国产亚洲精品免费视频播放| 国产精品免费αv视频| 亚洲第一福利网站| 成人免费的性色视频| 亚洲日韩一区二区三区| 免费观看国产小粉嫩喷水| 一级做a爰片久久毛片免费陪| 亚洲中文字幕无码久久精品1| 日韩免费高清大片在线| 日本亚洲精品色婷婷在线影院| 日韩在线a视频免费播放| 免费国产高清毛不卡片基地| 国产亚洲精品一品区99热| 69av免费观看| 亚洲aⅴ天堂av天堂无码麻豆| 亚洲第一页日韩专区| 久久午夜羞羞影院免费观看| 亚洲一卡2卡3卡4卡国产网站 | 亚洲av无码国产精品色午夜字幕| 综合在线免费视频| 337P日本欧洲亚洲大胆精品| 亚洲大成色www永久网站| 毛片免费观看网址| 四虎影视无码永久免费| 亚洲色大网站WWW永久网站| 久久亚洲中文字幕精品一区四|