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

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

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

    wang123

    2009年3月6日

    Hibernate Shard簡介介紹

    HibernateShard
        多數(shù)據(jù)庫水平分區(qū)解決方案。

    1. 簡介
         Hibernate 的一個擴展,用于處理多數(shù)據(jù)庫水平分區(qū)架構(gòu)。
         由google工程師 2007年 捐獻給 Hibernate社區(qū)。 
         http://www.hibernate.org/414.html
         目前版本:   3.0.0 beta2, 未發(fā)GA版。
         條件:Hibernate Core 3.2, JDK 5.0

    2. 水平分區(qū)原理
         一個庫表如 Order 存在于多個數(shù)據(jù)庫實例上。按特定的分區(qū)邏輯,將該庫表的數(shù)據(jù)存儲在這些實例中,一條記錄的主鍵 PK,在所有實例中不得重復。
       
        水平分區(qū)在大型網(wǎng)站,大型企業(yè)應用中經(jīng)常采用。 像www.sina.com.cn ,www.163.com  www.bt285.cn www.guihua.org
        目的出于海量數(shù)據(jù)分散存儲,分散操作,分散查詢以便提高數(shù)據(jù)處理量和整體數(shù)據(jù)處理性能。
      
        使用:
          google工程師的設計還是非常好的,完全兼容 Hibernate本身的主要接口。
    Java代碼 復制代碼
    1. org.hibernate.Session   
    2. org.hibernate.SessionFactory    
    3.  org.hibernate.Criteria    
    4.  org.hibernate.Query   

         因此程序員開發(fā)變化不大,甚至不需要關(guān)心后臺使用了分區(qū)數(shù)據(jù)庫。程序遷移問題不大。而且配置上比較簡明。

    3. 三種策略:
       1) ShardAccessStrategy, 查詢操作時,到那個分區(qū)執(zhí)行。
          默認提供兩個實現(xiàn):
          順序策略:SequentialShardAccessStrategy, 每個query按順序在所有分區(qū)上執(zhí)行。
          平行策略:ParallelShardAccessStrategy, 每個query以多線程方式并發(fā)平行的在所有分區(qū)上執(zhí)行。 此策略下,需要使用線程池機制滿足特定的性能需要,java.util.concurrent.ThreadPoolExecutor。

       2) ShardSelectionStrategy, 新增對象時,存儲到哪個分區(qū)。
             框架默認提供了一個輪詢選擇策略 RoundRobinShardSelectionStrategy, 但一般不這樣使用。
            通常采用“attribute-based sharding”機制,基于屬性分區(qū)。一般是用戶根據(jù)表自己實現(xiàn)一個基于屬性分區(qū)的策略類ShardSelectionStrategy ,例如,以下WeatherReport基于continent屬性選擇分區(qū):
    Java代碼 復制代碼
    1.   public class WeatherReportShardSelectionStrategy implements ShardSelectionStrategy {   
    2. public ShardId selectShardIdForNewObject(Object obj) {   
    3.     if(obj instanceof WeatherReport) {   
    4.         return ((WeatherReport)obj).getContinent().getShardId();   
    5.     }   
    6.     throw new IllegalArgumentException();   
    7. }   

     

       3) ShardResolutionStrategy, 該策略用于查找單個對象時,判斷它在哪個或哪幾個分區(qū)上。
          默認使用 AllShardsShardResolutionStrategy ,可以自定義例如:
    Java代碼 復制代碼
    1. public class WeatherReportShardResolutionStrategy extends AllShardsShardResolutionStrategy {   
    2.     public WeatherReportShardResolutionStrategy(List<ShardId> shardIds) {   
    3.         super(shardIds);   
    4.     }   
    5.   
    6.     public List<ShardId> selectShardIdsFromShardResolutionStrategyData(   
    7.             ShardResolutionStrategyData srsd) {   
    8.         if(srsd.getEntityName().equals(WeatherReport.class.getName())) {   
    9.             return Continent.getContinentByReportId(srsd.getId()).getShardId();   
    10.         }   
    11.         return super.selectShardIdsFromShardResolutionStrategyData(srsd);   
    12.     }   
    13. }  



    4. 水平分區(qū)下的查詢

       對于簡單查詢 HibernateShard 可以滿足。

       水平分區(qū)下多庫查詢是一個挑戰(zhàn)。主要存在于以下三種操作:
       1) distinct
             因為需要遍歷所有shard分區(qū),并進行合并判斷重復記錄。
       2) order by
             類似 1)
       3) aggregation
             count,sim,avg等聚合操作先分散到分區(qū)執(zhí)行,再進行匯總。
             是不是有點類似于 MapReduce ? 呵呵。
      
       目前 HibernateShard 不支持 1), 2), 對 3) 部分支持

        HibernateShard 目前通過 Criteria 接口的實現(xiàn)對 聚合提供了較好的支持, 因為 Criteria 以API接口指定了 Projection 操作,邏輯相對簡單。

        而HQL,原生 SQL 還不支持此類操作。

        
    5. 再分區(qū)和虛擬分區(qū)
          當數(shù)據(jù)庫規(guī)模增大,需要調(diào)整分區(qū)邏輯和數(shù)據(jù)存儲時, 需要再分區(qū)。
          兩種方式: 1)數(shù)據(jù)庫數(shù)據(jù)遷移其他分區(qū); 2) 改變記錄和分區(qū)映射關(guān)系。這兩種方式都比較麻煩。尤其“改變記錄和分區(qū)映射關(guān)系”,需要調(diào)整 ShardResolutionStrategy。

         HibernateShard 提供了一種虛擬分區(qū)層。當需要調(diào)整分區(qū)策略時,只需要調(diào)整虛擬分區(qū)和物理分區(qū)映射關(guān)系即可。以下是使用虛擬分區(qū)時的配置創(chuàng)建過程:

    Java代碼 復制代碼
    1.     
    2.   Map<Integer, Integer> virtualShardMap = new HashMap<Integer, Integer>();   
    3. virtualShardMap.put(00);   
    4. virtualShardMap.put(10);   
    5. virtualShardMap.put(21);   
    6. virtualShardMap.put(31);   
    7. ShardedConfiguration shardedConfig =   
    8.     new ShardedConfiguration(   
    9.         prototypeConfiguration,   
    10.         configurations,   
    11.         strategyFactory,   
    12.         virtualShardMap);   
    13. return shardedConfig.buildShardedSessionFactory();  


    6.  局限:
        1)HibernateShard 不支持垂直分區(qū), 垂直+水平混合分區(qū)。

        2) 水平分區(qū)下 查詢功能受到一定限制,有些功能不支持。實踐中,需要在應用層面對水平分區(qū)算法進行更多的考慮。
        3) 不支持跨分區(qū)的 關(guān)系 操作。例如:刪除A分區(qū)上的 s 表,B分區(qū)上的關(guān)聯(lián)子表 t的記錄無法進行參照完整性約束檢查。 (其實這個相對 跨分區(qū)查詢的挑戰(zhàn)應該說小的多,也許google工程師下個版本會支持,呵呵)

        4) 解析策略接口似乎和對象ID全局唯一性有些自相矛盾,
    AllShardsShardResolutionStrategy 的接口返回的是給定對象ID所在的 shard ID集合,按理應該是明確的一個 shard ID.

    參考資料:HibernateShard 參考指南。

    posted @ 2009-04-01 18:49 王| 編輯 收藏

    GPS經(jīng)緯度可以用來Java解析

    現(xiàn)在正開發(fā)的定位模塊用到的定位設置是塞格車圣導航設備,發(fā)送指令返回的經(jīng)緯度需要轉(zhuǎn)換成十進制,再到GIS系統(tǒng)獲取地理信息描述。以后需要要經(jīng)常用到這方面的知識,隨筆寫下。

     

    將經(jīng)緯度轉(zhuǎn)換成十進制

     公式:
        Decimal Degrees = Degrees + minutes/60 + seconds/3600
      例:57°55'56.6" =57+55/60+56.6/3600=57.9323888888888
     
    如把經(jīng)緯度  (longitude,latitude) (205.395583333332,57.9323888888888)轉(zhuǎn)換據(jù)成坐標(Degrees,minutes,seconds)(205°23'44.1",57°55'56.6")。
    步驟如下:

    1、 直接讀取"度":205

    2、(205.395583333332-205)*60=23.734999999920 得到"分":23

    3、(23.734999999920-23)*60=44.099999995200 得到"秒":44.1

     

    發(fā)送定位指令,終端返回的經(jīng)緯度信息如下:

    (ONE072457A3641.2220N11706.2569E000.000240309C0000400)

    按照協(xié)議解析

     

    獲得信息體的經(jīng)緯度是主要,其它不要管,直接用String類的substring()方法截掉,獲取的經(jīng)緯度

    3641.2220N11706.2569E http://www.bt285.cn

    Java代碼 復制代碼
    1. package com.tdt.test;   
    2.   
    3. import com.tdt.api.gis.LocationInfo;   
    4.   
    5. /**  
    6.  * <p>Title:坐標轉(zhuǎn)換 </p>  
    7.  *   
    8.  * <p>Description:</p>  
    9.  *   
    10.  * <p>Copyright: Copyright (c) 2009</p>  
    11.  *   
    12.  * <p>Company:</p>  
    13.  *   
    14.  * @author sunnylocus  
    15.  * @version 1.0 [2009-03-24]  
    16.  *   
    17.  */  
    18. public class LonlatConversion {   
    19.   
    20.     /**  
    21.      *   
    22.      * @param dms 坐標  
    23.      * @param type 坐標類型  
    24.      * @return String 解析后的經(jīng)緯度  
    25.      */  
    26.     public static String xypase(String dms, String type) {   
    27.         if (dms == null || dms.equals("")) {   
    28.             return "0.0";   
    29.         }   
    30.         double result = 0.0D;   
    31.         String temp = "";   
    32.            
    33.         if (type.equals("E")) {//經(jīng)度   
    34.             String e1 = dms.substring(03);//截取3位數(shù)字,經(jīng)度共3位,最多180度   
    35.                                             //經(jīng)度是一倫敦為點作南北兩極的線為0度,所有往西和往東各180度    
    36.             String e2 = dms.substring(3, dms.length());//需要運算的小數(shù)   
    37.   
    38.             result = Double.parseDouble(e1);   
    39.             result += (Double.parseDouble(e2) / 60.0D);   
    40.             temp = String.valueOf(result);   
    41.             if (temp.length() > 9) {   
    42.                 temp = e1 + temp.substring(temp.indexOf("."), 9);   
    43.             }   
    44.         } else if (type.equals("N")) {      //緯度,緯度是以赤道為基準,相當于把地球分兩半,兩個半球面上的點和平面夾角0~90度   
    45.             String n1 = dms.substring(02);//截取2位,緯度共2位,最多90度   
    46.             String n2 = dms.substring(2, dms.length());   
    47.   
    48.             result = Double.parseDouble(n1);   
    49.             result += Double.parseDouble(n2) / 60.0D;   
    50.             temp = String.valueOf(result);   
    51.             if (temp.length() > 8) {   
    52.                 temp = n1 + temp.substring(temp.indexOf("."), 8);   
    53.             }   
    54.         }   
    55.         return temp;   
    56.     }   
    57.     public static void main(String[] args) {   
    58.         String info="(ONE072457A3641.2220N11706.2569E000.000240309C0000400)";           
    59.         info=info.substring(11,info.length()-13);   
    60.         //緯度   
    61.         String N = info.substring(0, info.indexOf("N"));   
    62.         //經(jīng)度   
    63.         String E = info.substring(info.indexOf("N")+1,info.indexOf("E"));   
    64.         //請求gis,獲取地理信息描述   
    65.         double x = Double.parseDouble(CoordConversion.xypase(E,"E"));   
    66.         double y = Double.parseDouble(CoordConversion.xypase(N,"N"));   
    67.         String result =LocationInfo.getLocationInfo("test", x, y); //System.out.println("徑度:"+x+","+"緯度:"+y);   
    68.         System.out.println(result);   
    69.     }   
    70. }  

    運行結(jié)果

    在濟南市,位于輕騎路和八澗堡路附近;在環(huán)??萍紙@國際商務中心和濟南市區(qū)賢文莊附近。

    posted @ 2009-03-26 17:08 王| 編輯 收藏

    用Java來顯示圖片生成器

    一、本圖片生成器具有以下功能特性:

         1、可以設置圖片的寬度、高度、外框顏色、背景色;

         2、可以設置圖片字體的大小、名稱、顏色;

         3、可以設置輸出圖片的格式,如JPEG、GIF等;

         4、可以將圖片存儲到一個文件或者存儲到一個輸出流;

         5、可以為圖片增加若干條干擾線(在生成隨機碼圖片時可用此特性);

         6、打印在圖片上的文字支持自動換行;

     

    另外,本圖片生成器還用到了模板方法模式。

     

    二、下面列出相關(guān)的源代碼

         1、抽象類AbstractImageCreator的源代碼

     /**本代碼在 http://www.bt285.cn  http://www.5a520.cn 已使用了 */
    1. public abstract class AbstractImageCreator {   
    2.     private static Random rnd = new Random(new Date().getTime());   
    3.        
    4.     //圖片寬度   
    5.     private int width = 200;   
    6.        
    7.     //圖片高度   
    8.     private int height = 80;   
    9.        
    10.     //外框顏色   
    11.     private Color rectColor;   
    12.        
    13.     //背景色   
    14.     private Color bgColor;   
    15.        
    16.     //干擾線數(shù)目   
    17.     private int lineNum = 0;   
    18.        
    19.     //圖片格式   
    20.     private String formatName = "JPEG";   
    21.        
    22.     //字體顏色   
    23.     private Color fontColor = new Color(000);   
    24.        
    25.     //字體名稱   
    26.     private String fontName = "宋體";   
    27.        
    28.     //字體大小   
    29.     private int fontSize = 15;   
    30.        
    31.   
    32.     //##### 這里省略成員變臉的get、set方法 #####   
    33.   
    34.   
    35.     /**  
    36.      * 畫干擾線  
    37.      */  
    38.     private void drawRandomLine(Graphics graph){   
    39.         for(int i=0;i<lineNum;i++){   
    40.             //線條的顏色   
    41.             graph.setColor(getRandomColor(100155));   
    42.                
    43.             //線條兩端坐標值   
    44.             int x1 = rnd.nextInt(width);   
    45.             int y1 = rnd.nextInt(height);   
    46.                
    47.             int x2 = rnd.nextInt(width);   
    48.             int y2 = rnd.nextInt(height);   
    49.                
    50.             //畫線條   
    51.             graph.drawLine(x1, y1, x2, y2);   
    52.         }   
    53.     }   
    54.        
    55.     /**  
    56.      * 隨機獲取顏色對象  
    57.      */  
    58.     private Color getRandomColor(int base, int range){   
    59.         if((base + range) > 255) range = 255 - base;   
    60.            
    61.         int red = base + rnd.nextInt(range);   
    62.         int green = base + rnd.nextInt(range);   
    63.         int blue = base + rnd.nextInt(range);   
    64.            
    65.         return new Color(red, green, blue);   
    66.     }   
    67.            
    68.                 //該方法內(nèi)應用了模板方法模式   
    69.     public void drawImage(String text)throws IOException{   
    70.         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);   
    71.            
    72.         if(rectColor == null) rectColor = new Color(000);   
    73.         if(bgColor == null) bgColor = new Color(240251200);   
    74.            
    75.         //獲取畫布   
    76.         Graphics graph = image.getGraphics();   
    77.            
    78.         //畫長方形   
    79.         graph.setColor(bgColor);   
    80.         graph.fillRect(00, width, height);   
    81.            
    82.         //外框   
    83.         graph.setColor(rectColor);   
    84.         graph.drawRect(00, width-1, height-1);   
    85.            
    86.         //畫干擾線   
    87.         drawRandomLine(graph);   
    88.            
    89.         //畫字符串   
    90.         drawString(graph, text);   
    91.            
    92.         //執(zhí)行   
    93.         graph.dispose();   
    94.            
    95.         //輸出圖片結(jié)果   
    96.         saveImage(image);   
    97.     }   
    98.        
    99.     protected abstract void drawString(Graphics graph, String text);   
    100.        
    101.     protected abstract void saveImage(BufferedImage image)throws IOException;   
    102.        
    103. }  

     

         2、類DefaultImageCreator的源代碼

              該類將生成的圖片存儲到一個文件中,需要設置outputFilePath成員變量值,該成員變量值表示圖片的存儲全路徑。

    Java代碼 復制代碼
    1. public class DefaultImageCreator extends AbstractImageCreator {   
    2.     private String outputFilePath;   
    3.        
    4.     public String getOutputFilePath() {   
    5.         return outputFilePath;   
    6.     }   
    7.   
    8.     public void setOutputFilePath(String outputFilePath) {   
    9.         this.outputFilePath = outputFilePath;   
    10.     }   
    11.        
    12.     public DefaultImageCreator(){   
    13.            
    14.     }   
    15.        
    16.     public DefaultImageCreator(String outputFilePath){   
    17.         this.outputFilePath = outputFilePath;   
    18.     }   
    19.   
    20.     @Override  
    21.     protected void drawString(Graphics graph, String text) {   
    22.         graph.setColor(getFontColor());   
    23.         Font font = new Font(getFontName(), Font.PLAIN, getFontSize());   
    24.         graph.setFont(font);   
    25.            
    26.         FontMetrics fm = graph.getFontMetrics(font);   
    27.         int fontHeight = fm.getHeight(); //字符的高度   
    28.            
    29.         int offsetLeft = 0;   
    30.         int rowIndex = 1;   
    31.         for(int i=0;i<text.length();i++){   
    32.             char c = text.charAt(i);   
    33.             int charWidth = fm.charWidth(c); //字符的寬度   
    34.   
    35.             //另起一行   
    36.             if(Character.isISOControl(c) || offsetLeft >= (getWidth()-charWidth)){   
    37.                 rowIndex++;   
    38.                 offsetLeft = 0;   
    39.             }   
    40.                
    41.             graph.drawString(String.valueOf(c), offsetLeft, rowIndex * fontHeight);   
    42.             offsetLeft += charWidth;   
    43.         }   
    44.     }   
    45.        
    46.     @Override  
    47.     protected void saveImage(BufferedImage image)throws IOException{   
    48.         ImageIO.write(image, getFormatName(), new File(outputFilePath));   
    49.     }   
    50.   
    51. }  

     

         3、類OutputStreamImageCreator的源代碼

             該類將生成的圖片存儲到一個輸出流中,需要設置out成員變量值。

    Java代碼 復制代碼
    1. public class OutputStreamImageCreator extends DefaultImageCreator {   
    2.     private OutputStream out ;   
    3.        
    4.     public OutputStream getOut() {   
    5.         return out;   
    6.     }   
    7.   
    8.     public void setOut(OutputStream out) {   
    9.         this.out = out;   
    10.     }   
    11.        
    12.     public OutputStreamImageCreator(){   
    13.            
    14.     }   
    15.        
    16.     public OutputStreamImageCreator(OutputStream out){   
    17.         this.out = out;   
    18.     }   
    19.   
    20.     @Override  
    21.     public String getOutputFilePath() {   
    22.         return null;   
    23.     }   
    24.   
    25.     @Override  
    26.     public void setOutputFilePath(String outputFilePath) {   
    27.         outputFilePath = null;   
    28.     }   
    29.   
    30.     @Override  
    31.     protected void saveImage(BufferedImage image) throws IOException {   
    32.         if(out!=null) ImageIO.write(image, getFontName(), out);   
    33.     }   
    34.        
    35. }  

     

    三、實例代碼

         1、圖片存儲到文件

    StringBuffer sb = new StringBuffer();   
    1. sb.append("中華人民共和國\n");   
    2. sb.append("中華人民共和國\n");   
    3.   
    4. DefaultImageCreator creator = new DefaultImageCreator("c:\\img.jpeg");   
    5. creator.setWidth(150);   
    6. creator.setHeight(100);   
    7. creator.setLineNum(60);   
    8. creator.setFontSize(20);   
    9. creator.drawImage(sb.toString());  

     

    posted @ 2009-03-23 18:49 王| 編輯 收藏

    加密java源代碼

    Java程序的源代碼很容易被別人偷看,只要有一個反編譯器,任何人都可以分析別人的代碼。本文討論如何在不修改原有程序的情況下,通過加密技術(shù)保護源代碼。

      一、為什么要加密?

      對于傳統(tǒng)的C或C++之類的語言來說,要在Web上保護源代碼是很容易的,只要不發(fā)布它就可以。遺憾的是,Java程序的源代碼很容易被別人偷看。只要有一個反編譯器,任何人都可以分析別人的代碼。Java的靈活性使得源代碼很容易被竊取,但與此同時,它也使通過加密保護代碼變得相對容易,我們唯一需要了解的就是Java的ClassLoader對象。當然,在加密過程中,有關(guān)Java Cryptography Extension(JCE)的知識也是必不可少的。

      有幾種技術(shù)可以“模糊”Java類文件,使得反編譯器處理類文件的效果大打折扣。然而,修改反編譯器使之能夠處理這些經(jīng)過模糊處理的類文件并不是什么難事,所以不能簡單地依賴模糊技術(shù)來保證源代碼的安全。

      我們可以用流行的加密工具加密應用,比如PGP(Pretty Good Privacy)或GPG(GNU Privacy Guard)。這時,最終用戶在運行應用之前必須先進行解密。但解密之后,最終用戶就有了一份不加密的類文件,這和事先不進行加密沒有什么差別。

      Java運行時裝入字節(jié)碼的機制隱含地意味著可以對字節(jié)碼進行修改。JVM每次裝入類文件時都需要一個稱為ClassLoader的對象,這個對象負責把新的類裝入正在運行的JVM。JVM給ClassLoader一個包含了待裝入類(比如java.lang.Object)名字的字符串,然后由ClassLoader負責找到類文件,裝入原始數(shù)據(jù),并把它轉(zhuǎn)換成一個Class對象。

      我們可以通過定制ClassLoader,在類文件執(zhí)行之前修改它。這種技術(shù)的應用非常廣泛??在這里,它的用途是在類文件裝入之時進行解密,因此可以看成是一種即時解密器。由于解密后的字節(jié)碼文件永遠不會保存到文件系統(tǒng),所以竊密者很難得到解密后的代碼。

      由于把原始字節(jié)碼轉(zhuǎn)換成Class對象的過程完全由系統(tǒng)負責,所以創(chuàng)建定制ClassLoader對象其實并不困難,只需先獲得原始數(shù)據(jù),接著就可以進行包含解密在內(nèi)的任何轉(zhuǎn)換。

      Java 2在一定程度上簡化了定制ClassLoader的構(gòu)建。在Java 2中,loadClass的缺省實現(xiàn)仍舊負責處理所有必需的步驟,但為了顧及各種定制的類裝入過程,它還調(diào)用一個新的findClass方法。

      這為我們編寫定制的ClassLoader提供了一條捷徑,減少了麻煩:只需覆蓋findClass,而不是覆蓋loadClass。這種方法避免了重復所有裝入器必需執(zhí)行的公共步驟,因為這一切由loadClass負責。

      不過,本文的定制ClassLoader并不使用這種方法。原因很簡單。如果由默認的ClassLoader先尋找經(jīng)過加密的類文件,它可以找到;但由于類文件已經(jīng)加密,所以它不會認可這個類文件,裝入過程將失敗。因此,我們必須自己實現(xiàn)loadClass,稍微增加了一些工作量。

    二、定制類裝入器

      每一個運行著的JVM已經(jīng)擁有一個ClassLoader。這個默認的ClassLoader根據(jù)CLASSPATH環(huán)境變量的值,在本地文件系統(tǒng)中尋找合適的字節(jié)碼文件。

      應用定制ClassLoader要求對這個過程有較為深入的認識。我們首先必須創(chuàng)建一個定制ClassLoader類的實例,然后顯式地要求它裝入另外一個類。這就強制JVM把該類以及所有它所需要的類關(guān)聯(lián)到定制的ClassLoader。Listing 1顯示了如何用定制ClassLoader裝入類文件。

      【Listing 1:利用定制的ClassLoader裝入類文件】

    以下是引用片段:

      // 首先創(chuàng)建一個ClassLoader對象 如 http://www.bt285.cn
      ClassLoader myClassLoader = new myClassLoader();
      // 利用定制ClassLoader對象裝入類文件
      // 并把它轉(zhuǎn)換成Class對象
      Class myClass = myClassLoader.loadClass( "mypackage.MyClass" );
      // 最后,創(chuàng)建該類的一個實例
      Object newInstance = myClass.newInstance();
      // 注意,MyClass所需要的所有其他類,都將通過
      // 定制的ClassLoader自動裝入 

       如前所述,定制ClassLoader只需先獲取類文件的數(shù)據(jù),然后把字節(jié)碼傳遞給運行時系統(tǒng),由后者完成余下的任務。

      ClassLoader有幾個重要的方法。創(chuàng)建定制的ClassLoader時,我們只需覆蓋其中的一個,即loadClass,提供獲取原始類文件數(shù)據(jù)的代碼。這個方法有兩個參數(shù):類的名字,以及一個表示JVM是否要求解析類名字的標記(即是否同時裝入有依賴關(guān)系的類)。如果這個標記是true,我們只需在返回JVM之前調(diào)用resolveClass。

      【Listing 2:ClassLoader.loadClass()的一個簡單實現(xiàn)】

    以下是引用片段:

    public Class loadClass( String name, boolean resolve )  如:http://www.5a520.cn
      throws ClassNotFoundException {
      try {
      // 我們要創(chuàng)建的Class對象
      Class clasz = null;
      // 必需的步驟1:如果類已經(jīng)在系統(tǒng)緩沖之中,
      // 我們不必再次裝入它
      clasz = findLoadedClass( name );
      if (clasz != null)
      return clasz;
      // 下面是定制部分
      byte classData[] = /* 通過某種方法獲取字節(jié)碼數(shù)據(jù) */;
      if (classData != null) {
      // 成功讀取字節(jié)碼數(shù)據(jù),現(xiàn)在把它轉(zhuǎn)換成一個Class對象
      clasz = defineClass( name, classData, 0, classData.length );
      }
      // 必需的步驟2:如果上面沒有成功,
      // 我們嘗試用默認的ClassLoader裝入它
      if (clasz == null)
      clasz = findSystemClass( name );
      // 必需的步驟3:如有必要,則裝入相關(guān)的類
      if (resolve && clasz != null)
      resolveClass( clasz );
      // 把類返回給調(diào)用者
      return clasz;
      } catch( IOException ie ) {
      throw new ClassNotFoundException( ie.toString() );
      } catch( GeneralSecurityException gse ) {
      throw new ClassNotFoundException( gse.toString() );
      }
      } 

        Listing 2顯示了一個簡單的loadClass實現(xiàn)。代碼中的大部分對所有ClassLoader對象來說都一樣,但有一小部分(已通過注釋標記)是特有的。在處理過程中,ClassLoader對象要用到其他幾個輔助方法:

      findLoadedClass:用來進行檢查,以便確認被請求的類當前還不存在。loadClass方法應該首先調(diào)用它。

      defineClass:獲得原始類文件字節(jié)碼數(shù)據(jù)之后,調(diào)用defineClass把它轉(zhuǎn)換成一個Class對象。任何loadClass實現(xiàn)都必須調(diào)用這個方法。

      findSystemClass:提供默認ClassLoader的支持。如果用來尋找類的定制方法不能找到指定的類(或者有意地不用定制方法),則可以調(diào)用該方法嘗試默認的裝入方式。這是很有用的,特別是從普通的JAR文件裝入標準Java類時。

      resolveClass:當JVM想要裝入的不僅包括指定的類,而且還包括該類引用的所有其他類時,它會把loadClass的resolve參數(shù)設置成true。這時,我們必須在返回剛剛裝入的Class對象給調(diào)用者之前調(diào)用resolveClass。

      三、加密、解密

      Java加密擴展即Java Cryptography Extension,簡稱JCE。它是Sun的加密服務軟件,包含了加密和密匙生成功能。JCE是JCA(Java Cryptography Architecture)的一種擴展。

      JCE沒有規(guī)定具體的加密算法,但提供了一個框架,加密算法的具體實現(xiàn)可以作為服務提供者加入。除了JCE框架之外,JCE軟件包還包含了SunJCE服務提供者,其中包括許多有用的加密算法,比如DES(Data Encryption Standard)和Blowfish。

      為簡單計,在本文中我們將用DES算法加密和解密字節(jié)碼。下面是用JCE加密和解密數(shù)據(jù)必須遵循的基本步驟:

      步驟1:生成一個安全密匙。在加密或解密任何數(shù)據(jù)之前需要有一個密匙。密匙是隨同被加密的應用一起發(fā)布的一小段數(shù)據(jù),Listing 3顯示了如何生成一個密匙。 【Listing 3:生成一個密匙】

    以下是引用片段:

    // DES算法要求有一個可信任的隨機數(shù)源
      SecureRandom sr = new SecureRandom();
      // 為我們選擇的DES算法生成一個KeyGenerator對象
      KeyGenerator kg = KeyGenerator.getInstance( "DES" );
      kg.init( sr );
      // 生成密匙
      SecretKey key = kg.generateKey();
      // 獲取密匙數(shù)據(jù)
      byte rawKeyData[] = key.getEncoded();
      /* 接下來就可以用密匙進行加密或解密,或者把它保存
      為文件供以后使用 */
      doSomething( rawKeyData );  

    步驟2:加密數(shù)據(jù)。得到密匙之后,接下來就可以用它加密數(shù)據(jù)。除了解密的ClassLoader之外,一般還要有一個加密待發(fā)布應用的獨立程序(見Listing 4)。 【Listing 4:用密匙加密原始數(shù)據(jù)】

    以下是引用片段:

    // DES算法要求有一個可信任的隨機數(shù)源
      SecureRandom sr = new SecureRandom();
      byte rawKeyData[] = /* 用某種方法獲得密匙數(shù)據(jù) */;
      // 從原始密匙數(shù)據(jù)創(chuàng)建DESKeySpec對象
      DESKeySpec dks = new DESKeySpec( rawKeyData );
      // 創(chuàng)建一個密匙工廠,然后用它把DESKeySpec轉(zhuǎn)換成
      // 一個SecretKey對象
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
      SecretKey key = keyFactory.generateSecret( dks );
      // Cipher對象實際完成加密操作
      Cipher cipher = Cipher.getInstance( "DES" );
      // 用密匙初始化Cipher對象
      cipher.init( Cipher.ENCRYPT_MODE, key, sr );
      // 現(xiàn)在,獲取數(shù)據(jù)并加密
      byte data[] = /* 用某種方法獲取數(shù)據(jù) */
      // 正式執(zhí)行加密操作
      byte encryptedData[] = cipher.doFinal( data );
      // 進一步處理加密后的數(shù)據(jù)
      doSomething( encryptedData );  

      步驟3:解密數(shù)據(jù)。運行經(jīng)過加密的應用時,ClassLoader分析并解密類文件。操作步驟如Listing 5所示。 【Listing 5:用密匙解密數(shù)據(jù)】

      // DES算法要求有一個可信任的隨機數(shù)源
      SecureRandom sr = new SecureRandom();
      byte rawKeyData[] = /* 用某種方法獲取原始密匙數(shù)據(jù) */;
      // 從原始密匙數(shù)據(jù)創(chuàng)建一個DESKeySpec對象
      DESKeySpec dks = new DESKeySpec( rawKeyData );
      // 創(chuàng)建一個密匙工廠,然后用它把DESKeySpec對象轉(zhuǎn)換成
      // 一個SecretKey對象
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
      SecretKey key = keyFactory.generateSecret( dks );
      // Cipher對象實際完成解密操作
      Cipher cipher = Cipher.getInstance( "DES" );
      // 用密匙初始化Cipher對象
      cipher.init( Cipher.DECRYPT_MODE, key, sr );
      // 現(xiàn)在,獲取數(shù)據(jù)并解密
      byte encryptedData[] = /* 獲得經(jīng)過加密的數(shù)據(jù) */
      // 正式執(zhí)行解密操作
      byte decryptedData[] = cipher.doFinal( encryptedData );
      // 進一步處理解密后的數(shù)據(jù)
      doSomething( decryptedData );  

     

    四、應用實例

      前面介紹了如何加密和解密數(shù)據(jù)。要部署一個經(jīng)過加密的應用,步驟如下:

      步驟1:創(chuàng)建應用。我們的例子包含一個App主類,兩個輔助類(分別稱為Foo和Bar)。這個應用沒有什么實際功用,但只要我們能夠加密這個應用,加密其他應用也就不在話下。

      步驟2:生成一個安全密匙。在命令行,利用GenerateKey工具(參見GenerateKey.java)把密匙寫入一個文件: % java GenerateKey key.data

      步驟3:加密應用。在命令行,利用EncryptClasses工具(參見EncryptClasses.java)加密應用的類: % java EncryptClasses key.data App.class Foo.class Bar.class

      該命令把每一個.class文件替換成它們各自的加密版本。

      步驟4:運行經(jīng)過加密的應用。用戶通過一個DecryptStart程序運行經(jīng)過加密的應用。DecryptStart程序如Listing 6所示。 【Listing 6:DecryptStart.java,啟動被加密應用的程序】

    以下是引用片段:

      import java.io.*;
      import java.security.*;
      import java.lang.reflect.*;
      import javax.crypto.*;
      import javax.crypto.spec.*;
      public class DecryptStart extends ClassLoader
      {
      // 這些對象在構(gòu)造函數(shù)中設置,
      // 以后loadClass()方法將利用它們解密類
      private SecretKey key;
      private Cipher cipher;
      // 構(gòu)造函數(shù):設置解密所需要的對象
      public DecryptStart( SecretKey key ) throws GeneralSecurityException,
      IOException {
      this.key = key;
      String algorithm = "DES";
      SecureRandom sr = new SecureRandom();
      System.err.println( "[DecryptStart: creating cipher]" );
      cipher = Cipher.getInstance( algorithm );
      cipher.init( Cipher.DECRYPT_MODE, key, sr );
      }
      // main過程:我們要在這里讀入密匙,創(chuàng)建DecryptStart的
      // 實例,它就是我們的定制ClassLoader。
      // 設置好ClassLoader以后,我們用它裝入應用實例,
      // 最后,我們通過Java Reflection API調(diào)用應用實例的main方法
      static public void main( String args[] ) throws Exception {
      String keyFilename = args[0];
      String appName = args[1];
      // 這些是傳遞給應用本身的參數(shù)
      String realArgs[] = new String[args.length-2];
      System.arraycopy( args, 2, realArgs, 0, args.length-2 );
      // 讀取密匙
      System.err.println( "[DecryptStart: reading key]" );
      byte rawKey[] = Util.readFile( keyFilename );
      DESKeySpec dks = new DESKeySpec( rawKey );
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );
      SecretKey key = keyFactory.generateSecret( dks );
      // 創(chuàng)建解密的ClassLoader
      DecryptStart dr = new DecryptStart( key );
      // 創(chuàng)建應用主類的一個實例
      // 通過ClassLoader裝入它
      System.err.println( "[DecryptStart: loading "+appName+"]" );
      Class clasz = dr.loadClass( appName );
      // 最后,通過Reflection API調(diào)用應用實例
      // 的main()方法
      // 獲取一個對main()的引用
      String proto[] = new String[1];
      Class mainArgs[] = { (new String[1]).getClass() };
      Method main = clasz.getMethod( "main", mainArgs );
      // 創(chuàng)建一個包含main()方法參數(shù)的數(shù)組
      Object argsArray[] = { realArgs };
      System.err.println( "[DecryptStart: running "+appName+".main()]" );
      // 調(diào)用main()
      main.invoke( null, argsArray );
      }
      public Class loadClass( String name, boolean resolve )
      throws ClassNotFoundException {
      try {
      // 我們要創(chuàng)建的Class對象
      Class clasz = null;
      // 必需的步驟1:如果類已經(jīng)在系統(tǒng)緩沖之中
      // 我們不必再次裝入它
      clasz = findLoadedClass( name );
      if (clasz != null)
      return clasz;
      // 下面是定制部分
      try {
      // 讀取經(jīng)過加密的類文件
      byte classData[] = Util.readFile( name+".class" );
      if (classData != null) {
      // 解密...
      byte decryptedClassData[] = cipher.doFinal( classData );
      // ... 再把它轉(zhuǎn)換成一個類
      clasz = defineClass( name, decryptedClassData,
      0, decryptedClassData.length );
      System.err.println( "[DecryptStart: decrypting class "+name+"]" );
      }
      } catch( FileNotFoundException fnfe )
      // 必需的步驟2:如果上面沒有成功
      // 我們嘗試用默認的ClassLoader裝入它
      if (clasz == null)
      clasz = findSystemClass( name );
      // 必需的步驟3:如有必要,則裝入相關(guān)的類
      if (resolve && clasz != null)
      resolveClass( clasz );
      // 把類返回給調(diào)用者
      return clasz;
      } catch( IOException ie ) {
      throw new ClassNotFoundException( ie.toString()
      );
      } catch( GeneralSecurityException gse ) {
      throw new ClassNotFoundException( gse.toString()
      );
      }
      }
      } 

     對于未經(jīng)加密的應用,正常執(zhí)行方式如下: % java App arg0 arg1 arg2

      對于經(jīng)過加密的應用,則相應的運行方式為: % java DecryptStart key.data App arg0 arg1 arg2

      DecryptStart有兩個目的。一個DecryptStart的實例就是一個實施即時解密操作的定制ClassLoader;同時,DecryptStart還包含一個main過程,它創(chuàng)建解密器實例并用它裝入和運行應用。示例應用App的代碼包含在App.java、Foo.java和Bar.java內(nèi)。Util.java是一個文件I/O工具,本文示例多處用到了它。完整的代碼請從本文最后下載。

      五、注意事項

      我們看到,要在不修改源代碼的情況下加密一個Java應用是很容易的。不過,世上沒有完全安全的系統(tǒng)。本文的加密方式提供了一定程度的源代碼保護,但對某些攻擊來說它是脆弱的。

      雖然應用本身經(jīng)過了加密,但啟動程序DecryptStart沒有加密。攻擊者可以反編譯啟動程序并修改它,把解密后的類文件保存到磁盤。降低這種風險的辦法之一是對啟動程序進行高質(zhì)量的模糊處理?;蛘?,啟動程序也可以采用直接編譯成機器語言的代碼,使得啟動程序具有傳統(tǒng)執(zhí)行文件格式的安全性。

      另外還要記住的是,大多數(shù)JVM本身并不安全。狡猾的黑客可能會修改JVM,從ClassLoader之外獲取解密后的代碼并保存到磁盤,從而繞過本文的加密技術(shù)。Java沒有為此提供真正有效的補救措施。

      不過應該指出的是,所有這些可能的攻擊都有一個前提,這就是攻擊者可以得到密匙。如果沒有密匙,應用的安全性就完全取決于加密算法的安全性。雖然這種保護代碼的方法稱不上十全十美,但它仍不失為一種保護知識產(chǎn)權(quán)和敏感用戶數(shù)據(jù)的有效方案。

    posted @ 2009-03-19 12:51 王| 編輯 收藏

    java中的io筆記

     Java 流在處理上分為字符流和字節(jié)流。字符流處理的單元為 2 個字節(jié)的 Unicode 字符,分別操作字符、字符數(shù)組或字符串,而字節(jié)流處理單元為 1 個字節(jié),操作字節(jié)和字節(jié)數(shù)組。

    Java 內(nèi)用 Unicode 編碼存儲字符,字符流處理類負責將外部的其他編碼的字符流和 java 內(nèi) Unicode 字符流之間的轉(zhuǎn)換。而類 InputStreamReader 和 OutputStreamWriter 處理字符流和字節(jié)流的轉(zhuǎn)換。字符流(一次可以處理一個緩沖區(qū))一次操作比字節(jié)流(一次一個字節(jié))效率高。 

     

    ( 一 )以字節(jié)為導向的 stream------InputStream/OutputStream

    InputStream 和 OutputStream 是兩個 abstact 類,對于字節(jié)為導向的 stream 都擴展這兩個雞肋(基類 ^_^ ) ;

    1、 InputStream

     

    42134.gif 

    1.1

    ByteArrayInputStream -- 把內(nèi)存中的一個緩沖區(qū)作為 InputStream 使用 . 如使用http://www.5a520.cn

    construct---

    (A)ByteArrayInputStream(byte[]) 創(chuàng)建一個新字節(jié)數(shù)組輸入流( ByteArrayInputStream ),它從指定字節(jié)數(shù)組中讀取數(shù)據(jù)( 使用 byte 作為其緩沖區(qū)數(shù)組)

    (B)---ByteArrayInputStream(byte[], int, int) 創(chuàng)建一個新字節(jié)數(shù)組輸入流,它從指定字節(jié)數(shù)組中讀取數(shù)據(jù)。

    ---mark:: 該字節(jié)數(shù)組未被復制。

    1.2

    StringBufferInputStream -- 把一個 String 對象作為 InputStream .

    construct---  

    StringBufferInputStream(String) 據(jù)指定串創(chuàng)建一個讀取數(shù)據(jù)的輸入流串。

     

    注釋:不推薦使用 StringBufferInputStream 方法。 此類不能將字符正確的轉(zhuǎn)換為字節(jié)。

    同 JDK 1.1 版中的類似,從一個串創(chuàng)建一個流的最佳方法是采用 StringReader 類。

    1.3

    FileInputStream -- 把一個文件作為 InputStream ,實現(xiàn)對文件的讀取操作

    construct---

    (A)FileInputStream(File name) 創(chuàng)建一個輸入文件流,從指定的 File 對象讀取數(shù)據(jù)。

    (B)FileInputStream(FileDescriptor) 創(chuàng)建一個輸入文件流,從指定的文件描述器讀取數(shù)據(jù)。

    (C)-FileInputStream(String  name) 創(chuàng)建一個輸入文件流,從指定名稱的文件讀取數(shù)據(jù)。

    method ---- read() 從當前輸入流中讀取一字節(jié)數(shù)據(jù)。

    read(byte[]) 將當前輸入流中 b.length 個字節(jié)數(shù)據(jù)讀到一個字節(jié)數(shù)組中。

    read(byte[], int, int) 將輸入流中 len 個字節(jié)數(shù)據(jù)讀入一個字節(jié)數(shù)組中。

    1.4

    PipedInputStream :實現(xiàn)了 pipe 的概念,主要在線程中使用 . 管道輸入流是指一個通訊管道的接收端。

    一個線程通過管道輸出流發(fā)送數(shù)據(jù),而另一個線程通過管道輸入流讀取數(shù)據(jù),這樣可實現(xiàn)兩個線程間的通訊。

    construct---

    PipedInputStream() 創(chuàng)建一個管道輸入流,它還未與一個管道輸出流連接。

    PipedInputStream(PipedOutputStream) 創(chuàng)建一個管道輸入流 , 它已連接到一個管道輸出流。

    1.5

    SequenceInputStream :把多個 InputStream 合并為一個 InputStream . “序列輸入流”類允許應用程序把幾個輸入流連續(xù)地合并起來,

    并且使它們像單個輸入流一樣出現(xiàn)。每個輸入流依次被讀取,直到到達該流的末尾。

    然后“序列輸入流”類關(guān)閉這個流并自動地切換到下一個輸入流。

    construct---

    SequenceInputStream(Enumeration) 創(chuàng)建一個新的序列輸入流,并用指定的輸入流的枚舉值初始化它。

    SequenceInputStream(InputStream, InputStream) 創(chuàng)建一個新的序列輸入流,初始化為首先 讀輸入流 s1, 然后讀輸入流 s2 。

     

    2、 OutputSteam  http://www.bt285.cn

     

    42135.gif
    2.1

    ByteArrayOutputStream : 把信息存入內(nèi)存中的一個緩沖區(qū)中 . 該類實現(xiàn)一個以字節(jié)數(shù)組形式寫入數(shù)據(jù)的輸出流。

    當數(shù)據(jù)寫入緩沖區(qū)時,它自動擴大。用 toByteArray() 和 toString() 能檢索數(shù)據(jù)。

    constructor

    (A)--- ByteArrayOutputStream() 創(chuàng)建一個新的字節(jié)數(shù)組輸出流。

    (B)--- ByteArrayOutputStream() 創(chuàng)建一個新的字節(jié)數(shù)組輸出流。

    (C)--- ByteArrayOutputStream(int) 創(chuàng)建一個新的字節(jié)數(shù)組輸出流,并帶有指定大小字節(jié)的緩沖區(qū)容量。

    toString(String) 根據(jù)指定字符編碼將緩沖區(qū)內(nèi)容轉(zhuǎn)換為字符串,并將字節(jié)轉(zhuǎn)換為字符。

    write(byte[], int, int) 將指定字節(jié)數(shù)組中從偏移量 off 開始的 len 個字節(jié)寫入該字節(jié)數(shù)組輸出流。

    write(int) 將指定字節(jié)寫入該字節(jié)數(shù)組輸出流。

    writeTo(OutputStream) 用 out.write(buf, 0, count) 調(diào)用輸出流的寫方法將該字節(jié)數(shù)組輸出流的全部內(nèi)容寫入指定的輸出流參數(shù)。

    2.2  

    FileOutputStream: 文件輸出流是向 File 或 FileDescriptor 輸出數(shù)據(jù)的一個輸出流。

    constructor

    (A)FileOutputStream(File  name) 創(chuàng)建一個文件輸出流,向指定的 File 對象輸出數(shù)據(jù)。

    (B)FileOutputStream(FileDescriptor) 創(chuàng)建一個文件輸出流,向指定的文件描述器輸出數(shù)據(jù)。

    (C)FileOutputStream(String  name) 創(chuàng)建一個文件輸出流,向指定名稱的文件輸出數(shù)據(jù)。

    (D)FileOutputStream(String, boolean) 用指定系統(tǒng)的文件名,創(chuàng)建一個輸出文件。

    2.3

    PipedOutputStream: 管道輸出流是指一個通訊管道的發(fā)送端。 一個線程通過管道輸出流發(fā)送數(shù)據(jù),

    而另一個線程通過管道輸入流讀取數(shù)據(jù),這樣可實現(xiàn)兩個線程間的通訊。

    constructor

    (A)PipedOutputStream() 創(chuàng)建一個管道輸出流,它還未與一個管道輸入流連接。

    (B)PipedOutputStream(PipedInputStream) 創(chuàng)建一個管道輸出流,它已連接到一個管道輸入流。

     

    ( 二 )以字符為導向的 stream Reader/Writer

    以 Unicode 字符為導向的 stream ,表示以 Unicode 字符為單位從 stream 中讀取或往 stream 中寫入信息。

    Reader/Writer 為 abstact 類

    以 Unicode 字符為導向的 stream 包括下面幾種類型:

    1. Reader

     

    42136.gif 

    1.1

      CharArrayReader :與 ByteArrayInputStream 對應此類實現(xiàn)一個可用作字符輸入流的字符緩沖區(qū)

    constructor

    CharArrayReader(char[]) 用指定字符數(shù)組創(chuàng)建一個 CharArrayReader 。

    CharArrayReader(char[], int, int) 用指定字符數(shù)組創(chuàng)建一個 CharArrayReader

    1.2

    StringReader : 與 StringBufferInputStream 對應其源為一個字符串的字符流。

    StringReader(String) 創(chuàng)建一新的串讀取者。

    1.3

    FileReader : 與 FileInputStream 對應

    1.4

    PipedReader :與 PipedInputStream 對應

     

    2.  Writer

     42137.gif

    2.1    CharArrayWrite : 與 ByteArrayOutputStream 對應

    2.2   StringWrite :無與之對應的以字節(jié)為導向的 stream

    2.3  FileWrite : 與 FileOutputStream 對應

    2.4  PipedWrite :與 PipedOutputStream 對應

     

    3、兩種不同導向的 stream 之間的轉(zhuǎn)換  

    3.1

    InputStreamReader 和 OutputStreamReader :

    把一個以字節(jié)為導向的 stream 轉(zhuǎn)換成一個以字符為導向的 stream 。

    InputStreamReader 類是從字節(jié)流到字符流的橋梁:它讀入字節(jié),并根據(jù)指定的編碼方式,將之轉(zhuǎn)換為字符流。

    使用的編碼方式可能由名稱指定,或平臺可接受的缺省編碼方式。

    InputStreamReader 的 read() 方法之一的每次調(diào)用,可能促使從基本字節(jié)輸入流中讀取一個或多個字節(jié)。

    為了達到更高效率,考慮用 BufferedReader 封裝 InputStreamReader ,

    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    例如: // 實現(xiàn)從鍵盤輸入一個整數(shù)

    1. String s = null;              
    2. InputStreamReader re = new InputStreamReader(System.in);   
    3.               BufferedReader br = new BufferedReader(re);   
    4.               try {   
    5.               s = br.readLine();   
    6.               System.out.println("s= " + Integer.parseInt(s));   
    7.               br.close();   
    8.               }   
    9.               catch (IOException e)   
    10.               {   
    11.               e.printStackTrace();   
    12.               }   
    13.               catch (NumberFormatException e)// 當應用程序試圖將字符串轉(zhuǎn)換成一種數(shù)值類型,但該字符串不能轉(zhuǎn)換為適當格式時,拋出該異常。   
    14.               {   
    15.               System.out.println(" 輸入的不是數(shù)字 ");   
    16.               }  

    InputStreamReader(InputStream) 用缺省的字符編碼方式,創(chuàng)建一個 InputStreamReader 。

    InputStreamReader(InputStream, String) 用已命名的字符編碼方式,創(chuàng)建一個 InputStreamReader 。

     

    OutputStreamWriter 將多個字符寫入到一個輸出流,根據(jù)指定的字符編碼將多個字符轉(zhuǎn)換為字節(jié)。

    每個 OutputStreamWriter 合并它自己的 CharToByteConverter, 因而是從字符流到字節(jié)流的橋梁。

     

    (三)Java IO 的一般使用原則 :  

    一、按數(shù)據(jù)來源(去向)分類:

    1 、是文件: FileInputStream, FileOutputStream, ( 字節(jié)流 )FileReader, FileWriter( 字符 )

    2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 字節(jié)流 )

    3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 )

    4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字節(jié)流 )StringReader, StringWriter( 字符流 )

    5 、網(wǎng)絡數(shù)據(jù)流: InputStream, OutputStream,( 字節(jié)流 ) Reader, Writer( 字符流 )

    二、按是否格式化輸出分:

    1 、要格式化輸出: PrintStream, PrintWriter

    三、按是否要緩沖分:

    1 、要緩沖: BufferedInputStream, BufferedOutputStream,( 字節(jié)流 ) BufferedReader, BufferedWriter( 字符流 )

    四、按數(shù)據(jù)格式分:

    1 、二進制格式(只要不能確定是純文本的) : InputStream, OutputStream 及其所有帶 Stream 結(jié)束的子類

    2 、純文本格式(含純英文與漢字或其他編碼方式); Reader, Writer 及其所有帶 Reader, Writer 的子類

    五、按輸入輸出分:

    1 、輸入: Reader, InputStream 類型的子類

    2 、輸出: Writer, OutputStream 類型的子類

    六、特殊需要:

    1 、從 Stream 到 Reader,Writer 的轉(zhuǎn)換類: InputStreamReader, OutputStreamWriter

    2 、對象輸入輸出: ObjectInputStream, ObjectOutputStream

    3 、進程間通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

    4 、合并輸入: SequenceInputStream

    5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader

    決定使用哪個類以及它的構(gòu)造進程的一般準則如下(不考慮特殊需要):

    首先,考慮最原始的數(shù)據(jù)格式是什么: 原則四

    第二,是輸入還是輸出:原則五

    第三,是否需要轉(zhuǎn)換流:原則六第 1 點

    第四,數(shù)據(jù)來源(去向)是什么:原則一

    第五,是否要緩沖:原則三 (特別注明:一定要注意的是 readLine() 是否有定義,有什么比 read, write 更特殊的輸入或輸出方法)

    第六,是否要格式化輸出:原則二

    posted @ 2009-03-18 19:10 王| 編輯 收藏

    解決一個 MySQL 服務器進程 狂占CPU 的技術(shù)筆記

    早上幫朋友一臺服務器解決了 Mysql cpu 占用 100% 的問題。稍整理了一下,將經(jīng)驗記錄在這篇文章里

      朋友主機(Windows 2003 + IIS + PHP + MYSQL )近來 MySQL 服務進程 (mysqld-nt.exe) CPU 占用率總為 100% 高居不下。此主機有10個左右的 database, 分別給十個網(wǎng)站調(diào)用。據(jù)朋友測試,導致 mysqld-nt.exe cpu 占用奇高的是網(wǎng)站A,一旦在 IIS 中將此網(wǎng)站停止服務,CPU 占用就降下來了。一啟用,則馬上上升。

     MYSQL CPU 占用 100% 的解決過程

      今天早上仔細檢查了一下。目前此小說網(wǎng)站 http://www.5a520.cn 的七日平均日 IP 為2000,PageView 為 3萬左右。網(wǎng)站A 用的 database 目前有39個表,記錄數(shù) 60.1萬條,占空間 45MB。按這個數(shù)據(jù),MySQL 不可能占用這么高的資源。

      于是在服務器上運行命令,將 mysql 當前的環(huán)境變量輸出到文件 output.txt:

    d:\web\mysql> mysqld.exe --help >output.txt

      發(fā)現(xiàn) tmp_table_size 的值是默認的 32M,于是修改 My.ini, 將 tmp_table_size 賦值到 200M:

    d:\web\mysql> notepad c:\windows\my.ini
    [mysqld]
    tmp_table_size=200M
    

      然后重啟 MySQL 服務。CPU 占用有輕微下降,以前的CPU 占用波形圖是 100% 一根直線,現(xiàn)在則在 97%~100%之間起伏。這表明調(diào)整 tmp_table_size 參數(shù)MYSQL 性能提升有改善作用。但問題還沒有完全解決。

      于是進入 mysql 的 shell 命令行,調(diào)用 show processlist, 查看當前 mysql 使用頻繁的 sql 語句:

    mysql> show processlist;

      反復調(diào)用此命令,發(fā)現(xiàn)網(wǎng)站 A 的兩個 SQL 語句經(jīng)常在 process list 中出現(xiàn),其語法如下:

    SELECT t1.pid, t2.userid, t3.count, t1.date
    FROM _mydata AS t1
    LEFT JOIN _myuser AS t3 ON t1.userid=t3.userid
    LEFT JOIN _mydata_body AS t2 ON t1.pid=t3.pid
    ORDER BY t1.pid
    LIMIT 0,15

      調(diào)用 show columns 檢查這三個表的結(jié)構(gòu) :

    mysql> show columns from _myuser;
    mysql> show columns from _mydata;
    mysql> show columns from _mydata_body;

      終于發(fā)現(xiàn)了問題所在:_mydata 表,只根據(jù) pid 建立了一個 primary key,但并沒有為 userid 建立索引。而在這個 SQL 語句的第一個 LEFT JOIN ON 子句中:

    LEFT JOIN _myuser AS t3 ON t1.userid=t3.userid

      _mydata 的 userid 被參與了條件比較運算。于是我為給 _mydata 表根據(jù)字段 userid 建立了一個索引:

    mysql> ALTER TABLE `_mydata` ADD INDEX ( `userid` )

      建立此索引之后,CPU 馬上降到了 80% 左右??吹秸业搅藛栴}所在,于是檢查另一個反復出現(xiàn)在 show processlist 中的 sql 語句:

    SELECT COUNT(*)
    FROM _mydata AS t1, _mydata_key AS t2
    WHERE t1.pid=t2.pid and t2.keywords = '孔雀'

      經(jīng)檢查 _mydata_key 表的結(jié)構(gòu),發(fā)現(xiàn)它只為 pid 建了了 primary key, 沒有為 keywords 建立 index。_mydata_key 目前有 33 萬條記錄,在沒有索引的情況下對33萬條記錄進行文本檢索匹配,不耗費大量的 cpu 時間才怪??磥砭褪轻槍@個表的檢索出問題了。于是同樣為 _mydata_key 表根據(jù)字段 keywords 加上索引:

    mysql> ALTER TABLE `_mydata_key` ADD INDEX ( `keywords` )

      建立此索引之后,CPU立刻降了下來,在 50%~70%之間震蕩。

      再次調(diào)用 show prosslist,網(wǎng)站A 的sql 調(diào)用就很少出現(xiàn)在結(jié)果列表中了。但發(fā)現(xiàn)此主機運行了幾個 Discuz 的論壇程序, Discuz 論壇的好幾個表也存在著這個問題。于是順手一并解決,cpu占用再次降下來了

     解決 MYSQL CPU 占用 100% 的經(jīng)驗總結(jié)

       http://www.bt285.cn  BT下載

    1. 增加 tmp_table_size 值。mysql 的配置文件中,tmp_table_size 的默認大小是 32M。如果一張臨時表超出該大小,MySQL產(chǎn)生一個 The table tbl_name is full 形式的錯誤,如果你做很多高級 GROUP BY 查詢,增加 tmp_table_size 值。 這是 mysql 官方關(guān)于此選項的解釋:

      tmp_table_size

      This variable determines the maximum size for a temporary table in memory. If the table becomes too large, a MYISAM table is created on disk. Try to avoid temporary tables by optimizing the queries where possible, but where this is not possible, try to ensure temporary tables are always stored in memory. Watching the processlist for queries with temporary tables that take too long to resolve can give you an early warning that tmp_table_size needs to be upped. Be aware that memory is also allocated per-thread. An example where upping this worked for more was a server where I upped this from 32MB (the default) to 64MB with immediate effect. The quicker resolution of queries resulted in less threads being active at any one time, with all-round benefits for the server, and available memory.

    2. 對 WHERE, JOIN, MAX(), MIN(), ORDER BY 等子句中的條件判斷中用到的字段,應該根據(jù)其建立索引 INDEX。索引被用來快速找出在一個列上用一特定值的行。沒有索引,MySQL不得不首先以第一條記錄開始并然后讀完整個表直到它找出相關(guān)的行。表越大,花費時間越多。如果表對于查詢的列有一個索引,MySQL能快速到達一個位置去搜尋到數(shù)據(jù)文件的中間,沒有必要考慮所有數(shù)據(jù)。如果一個表有1000行,這比順序讀取至少快100倍。所有的MySQL索引(PRIMARY、UNIQUE和INDEX)在B樹中存儲。

       

      根據(jù) mysql 的開發(fā)文檔:

      索引 index 用于

      • 快速找出匹配一個WHERE子句的行
      • 當執(zhí)行聯(lián)結(jié)(JOIN)時,從其他表檢索行。
      • 對特定的索引列找出MAX()或MIN()值
      • 如果排序或分組在一個可用鍵的最左面前綴上進行(例如,ORDER BY key_part_1,key_part_2),排序或分組一個表。如果所有鍵值部分跟隨DESC,鍵以倒序被讀取。
      • 在一些情況中,一個查詢能被優(yōu)化來檢索值,不用咨詢數(shù)據(jù)文件。如果對某些表的所有使用的列是數(shù)字型的并且構(gòu)成某些鍵的最左面前綴,為了更快,值可以從索引樹被檢索出來。

        假定你發(fā)出下列SELECT語句:
        mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
        如果一個多列索引存在于col1和col2上,適當?shù)男锌梢灾苯颖蝗〕?。如果分開的單行列索引存在于col1和col2上,優(yōu)化器試圖通過決定哪個索引將找到更少的行并來找出更具限制性的索引并且使用該索引取行。

         

        開發(fā)人員做 SQL 數(shù)據(jù)表設計的時候,一定要通盤考慮清楚。

      posted @ 2009-03-12 20:16 王| 編輯 收藏

      使用org.apache.commons.net.ftp包開發(fā)FTP客戶端,實現(xiàn)進度匯報,實現(xiàn)斷點續(xù)傳,中文支持

           摘要: 利用org.apache.commons.net.ftp包實現(xiàn)一個簡單的ftp客戶端實用類。主要實現(xiàn)一下功能 1.支持上傳下載。支持斷點續(xù)傳 2.支持進度匯報 3.支持對于中文目錄及中文文件創(chuàng)建的支持。 import java.io.File;    import java.io.FileOutputStream;  ...  閱讀全文

      posted @ 2009-03-10 16:34 王| 編輯 收藏

      使用NTP網(wǎng)絡時間協(xié)議同步你的網(wǎng)絡

      NTP協(xié)議全稱網(wǎng)絡時間協(xié)議(Network Time Procotol)。它的目的是在國際互聯(lián)網(wǎng)上傳遞統(tǒng)一、標準的時間。具體的實現(xiàn)方案是在網(wǎng)絡上指定若干時鐘源網(wǎng)站,為用戶提供授時服務,并且這些網(wǎng)站間應該能夠相互比對,提高準確度。

      NTP 最早是由美國Delaware大學的Mills教授設計實現(xiàn)的,從1982件最初提出到現(xiàn)在已發(fā)展了將近20年,2001年最新的NTPv4精確度已經(jīng)達到了200毫秒。對于實際應用,又有確保秒級精度的SNTP(簡單的網(wǎng)絡時間協(xié)議)。

      NTP是一個跨越廣域網(wǎng)或局域網(wǎng)的復雜的同步時間協(xié)議,它通??色@得毫秒級的精度。RFC2030[Mills 1996]描述了SNTP(Simple Network Time Protocol),目的是為了那些不需要完整NTP實現(xiàn)復雜性的主機,它是NTP的一個子集。通常讓局域網(wǎng)上的若干臺主機通過因特網(wǎng)與其他的NTP主機同步時鐘,接著再向局域網(wǎng)內(nèi)其他客戶端提供時間同步服務。

      NTP協(xié) 議是OSI參考模型的高層協(xié)議,符合UDP傳輸協(xié)議格式,擁有專用端口123。

      隨著時間的推移, 計算機的時鐘會傾向于漂移。 網(wǎng)絡時間協(xié)議 (NTP) 是一種確保您的時鐘保持準確的方法。它為路由器、交換機、工作站和服務器之間提供了一種時間同步的機制。所以NTP Server經(jīng)常應用于一些有時間同步要求的IT系統(tǒng)環(huán)境中。

      一、服務端設置

      Mac OS X Server似乎默認就有了,只說一下Linux下如何設置。
      在Ubuntu Linux中應用NTP Server非常方便:
      1. 安裝
      sudo apt-get install ntp  


      2. 配置
      配置文件是/etc/ntp.conf

      a. 找到server一項,添加你喜歡的Time Server
      server time.asia.apple.com  iburst dynamic  

      b. 設置權(quán)限,我的所有restrict條目如下
       
      1. restrict -4 default kod notrap nomodify nopeer noquery   
      2. restrict -6 default kod notrap nomodify nopeer noquery   
      3.   
      4. # Local users may interrogate the ntp server more closely.   
      5. restrict 127.0.0.1   www.5a520.cn
      6. restrict ::1  
      7.   
      8. # Clients from this (example!) subnet have unlimited access, but only if  
      9. # cryptographically authenticated.   
      10. #restrict 192.168.123.0 mask 255.255.255.0 notrust   
      11. restrict 192.168.0.0 mask 255.255.255.0  


      3. 重啟ntp服務器
      /etc/init.d/ntp restart

      4. 查看服務器是否工作正常
      在服務器運行
      Java代碼 復制代碼
      1. ntpq -p  


      二、工作站同步

      好了,測試一下吧,假設你的新服務器IP地址為192.168.0.7。在客戶端運行如下命令:
      ntpdate -u 192.168.0.7

      同步成功后,將會顯示如下:
      6 Mar 15:44:24 ntpdate[9921]: adjust time server 192.168.0.7 offset -0.007277 sec

      三、交換機同步

      Cisco IOS
      Java代碼 復制代碼
      1. ciscorouter> enable   
      2. password: *********   
      3. ciscorouter# config t   
      4. ciscorouter(config)# ntp update-calendar   
      5. ciscorouter(config)# ntp server 192.168.0.4  
      6. ciscorouter(config)# ntp server 192.168.0.7  
      7. ciscorouter(config)# exit   
      8. ciscorouter# wr mem  


      Dell 6248
      Java代碼 復制代碼
      1. dell6248-corner>en   
      2. dell6248-corner#configure    
      3. dell6248-corner(config)#sntp server 192.168.0.7  
      4. dell6248-corner(config)#  


      四、一些常見的時間服務器

      210.72.145.44  ── (國家授時中心服務器IP地址)
      133.100.11.8  ── 日本 福岡大學
      time-a.nist.gov 129.6.15.28 ── NIST, Gaithersburg, Maryland
      time-b.nist.gov 129.6.15.29 ── NIST, Gaithersburg, Maryland
      time-a.timefreq.bldrdoc.gov 132.163.4.101 ── NIST, Boulder, Colorado
      time-b.timefreq.bldrdoc.gov 132.163.4.102 ── NIST, Boulder, Colorado
      time-c.timefreq.bldrdoc.gov 132.163.4.103 ── NIST, Boulder, Colorado
      utcnist.colorado.edu 128.138.140.44 ── University of Colorado, Boulder
      time.nist.gov 192.43.244.18 ── NCAR, Boulder, Colorado
      time-nw.nist.gov 131.107.1.10 ── Microsoft, Redmond, Washington
      nist1.symmetricom.com 69.25.96.13 ── Symmetricom, San Jose, California
      nist1-dc.glassey.com 216.200.93.8 ── Abovenet, Virginia
      nist1-ny.glassey.com 208.184.49.9 ── Abovenet, New York City
      nist1-sj.glassey.com 207.126.98.204 ── Abovenet, San Jose, California
      nist1.aol-ca.truetime.com 207.200.81.113 ── TrueTime, AOL facility, Sunnyvale, California
      nist1.aol-va.truetime.com 64.236.96.53 ── TrueTime, AOL facility, Virginia


      五、使用Python腳本來獲取時間

      Java代碼 復制代碼
      1. #!/usr/bin/python   
      2. from socket import *   
      3. import struct,os,time,sys   
      4.   
      5. # Script to set Linux hardware clock (/usr/sbin/hwclock) from an NTP   
      6. # time server.   Run as "setclock.py" to simply print the time from   
      7. # the NTP server.  Run as "setclock.py --set" to set the Linux   
      8. # hardware clock (as the super user, of course).   
      9.   
      10. # Based on Simon Foster's simple SNTP client from ASPN Python cookbook.   
      11. # Adapted by Paul Rubin; this script lives at:   
      12. #    http://www.nightsong.com/phr/python/setclock.py   
      13.   
      14. time_server = ('time.apple.com'123)   
      15. # time.apple.com is a stratum 2 time server.  (123 is the SNTP port number).   
      16. # More servers info can be found at   
      17. #   
      18. #   http://www.eecis.udel.edu/~mills/ntp/servers.htm   
      19. #   
      20. # Note it's considered antisocial to use a stratum 1 server (like NIST)   
      21. for purposes like this which don't need extreme accuracy (i.e. syncing   
      22. # your own big NTP network).  See www.ntp.org for more info.   
      23. #   
      24. # You could also use time.windows.com (Microsoft server) which syncs   
      25. # all Windows XP machines everywhere, so it can presumably handle lots   
      26. # of clients.   
      27.   
      28. # number of seconds between NTP epoch (1900) and Unix epoch (1970).   
      29. TIME1970 = 2208988800L      # Thanks to F.Lundh   
      30.   
      31. client = socket( AF_INET, SOCK_DGRAM )   
      32. data = '\x1b' + 47 * '\0'  
      33. client.sendto(data, time_server)   
      34. data, address = client.recvfrom( 1024 )   
      35. if data:   
      36.     print 'Response received from', address,'\n'  
      37.     t = struct.unpack( '!12I', data )[10]   
      38.     if t == 0:   
      39.         raise 'invalid response'  
      40.     ct = time.ctime(t - TIME1970)   
      41.     print 'Current time = %s\n' % ct   
      42.     if len(sys.argv) > 1 and sys.argv[1] == "--set":   
      43.         os.system("/usr/sbin/hwclock --set '--date=%s'"% ct)   
      44. else:   
      45.     raise 'no data returned'  


      上面這個腳本原址在這里,http://www.nightsong.com/phr/python/setclock.py, http://www.bt285.cn, http://www.guihua.org。使用方式如下:
      $ python setclock.py
      Response received from ('17.151.16.23', 123)

      Current time = Fri Mar  6 16:03:19 2009

      posted @ 2009-03-06 19:48 王| 編輯 收藏

      <2009年3月>
      22232425262728
      1234567
      891011121314
      15161718192021
      22232425262728
      2930311234

      導航

      統(tǒng)計

      常用鏈接

      留言簿(3)

      隨筆檔案

      搜索

      最新評論

      閱讀排行榜

      評論排行榜

      主站蜘蛛池模板: 亚洲综合日韩中文字幕v在线| 久久精品7亚洲午夜a| 亚洲一区二区三区在线| 欧洲人免费视频网站在线| 亚洲热线99精品视频| 免费看黄的成人APP| 亚洲乱码国产乱码精品精| 国产一级一毛免费黄片| 亚洲精品卡2卡3卡4卡5卡区| 国产免费一区二区三区不卡| 久久精品亚洲综合| 亚洲成人免费在线观看| 最近中文字幕完整版免费高清 | 成年午夜视频免费观看视频| 亚洲人成网站在线观看播放青青| 国产99视频精品免费观看7| 久久精品亚洲AV久久久无码| 免费a级毛片无码a∨蜜芽试看| 亚洲色精品三区二区一区| 国产美女精品视频免费观看| 亚洲欧洲在线观看| 91成人免费在线视频| 亚洲第一成年网站视频| 亚洲av麻豆aⅴ无码电影| 97在线免费视频| 亚洲精品国产免费| 免费激情视频网站| 一级毛片视频免费观看| 亚洲AV成人一区二区三区AV| 最近高清中文字幕无吗免费看| 亚洲av永久无码一区二区三区| 亚洲AV无码乱码在线观看性色扶| 四虎影视无码永久免费| 亚洲丰满熟女一区二区v| 四虎影视在线永久免费看黄| 最近免费中文字幕MV在线视频3| 久久久久亚洲AV无码专区首JN| 日韩免费观看视频| 免费国产99久久久香蕉| 亚洲精品亚洲人成在线| 亚洲人成网站在线观看播放|