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

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

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

    wang123

    2009年3月10日

    Hibernate Shard簡(jiǎn)介介紹

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

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

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

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

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

       2) ShardSelectionStrategy, 新增對(duì)象時(shí),存儲(chǔ)到哪個(gè)分區(qū)。
             框架默認(rèn)提供了一個(gè)輪詢選擇策略 RoundRobinShardSelectionStrategy, 但一般不這樣使用。
            通常采用“attribute-based sharding”機(jī)制,基于屬性分區(qū)。一般是用戶根據(jù)表自己實(shí)現(xiàn)一個(gè)基于屬性分區(qū)的策略類ShardSelectionStrategy ,例如,以下WeatherReport基于continent屬性選擇分區(qū):
    Java代碼 復(fù)制代碼
    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, 該策略用于查找單個(gè)對(duì)象時(shí),判斷它在哪個(gè)或哪幾個(gè)分區(qū)上。
          默認(rèn)使用 AllShardsShardResolutionStrategy ,可以自定義例如:
    Java代碼 復(fù)制代碼
    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ū)下的查詢

       對(duì)于簡(jiǎn)單查詢 HibernateShard 可以滿足。

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

        HibernateShard 目前通過(guò) Criteria 接口的實(shí)現(xiàn)對(duì) 聚合提供了較好的支持, 因?yàn)?Criteria 以API接口指定了 Projection 操作,邏輯相對(duì)簡(jiǎn)單。

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

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

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

    Java代碼 復(fù)制代碼
    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ū)下 查詢功能受到一定限制,有些功能不支持。實(shí)踐中,需要在應(yīng)用層面對(duì)水平分區(qū)算法進(jìn)行更多的考慮。
        3) 不支持跨分區(qū)的 關(guān)系 操作。例如:刪除A分區(qū)上的 s 表,B分區(qū)上的關(guān)聯(lián)子表 t的記錄無(wú)法進(jìn)行參照完整性約束檢查。 (其實(shí)這個(gè)相對(duì) 跨分區(qū)查詢的挑戰(zhàn)應(yīng)該說(shuō)小的多,也許google工程師下個(gè)版本會(huì)支持,呵呵)

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

    參考資料:HibernateShard 參考指南。

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

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

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

     

    將經(jīng)緯度轉(zhuǎn)換成十進(jì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ù)成坐標(biāo)(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代碼 復(fù)制代碼
    1. package com.tdt.test;   
    2.   
    3. import com.tdt.api.gis.LocationInfo;   
    4.   
    5. /**  
    6.  * <p>Title:坐標(biāo)轉(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 坐標(biāo)  
    23.      * @param type 坐標(biāo)類型  
    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)度是一倫敦為點(diǎn)作南北兩極的線為0度,所有往西和往東各180度    
    36.             String e2 = dms.substring(3, dms.length());//需要運(yùn)算的小數(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")) {      //緯度,緯度是以赤道為基準(zhǔn),相當(dāng)于把地球分兩半,兩個(gè)半球面上的點(diǎ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.         //請(qǐng)求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. }  

    運(yùn)行結(jié)果

    在濟(jì)南市,位于輕騎路和八澗堡路附近;在環(huán)保科技園國(guó)際商務(wù)中心和濟(jì)南市區(qū)賢文莊附近。

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

    用Java來(lái)顯示圖片生成器

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

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

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

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

         4、可以將圖片存儲(chǔ)到一個(gè)文件或者存儲(chǔ)到一個(gè)輸出流;

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

         6、打印在圖片上的文字支持自動(dòng)換行;

     

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

     

    二、下面列出相關(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.      * 畫(huà)干擾線  
    37.      */  
    38.     private void drawRandomLine(Graphics graph){   
    39.         for(int i=0;i<lineNum;i++){   
    40.             //線條的顏色   
    41.             graph.setColor(getRandomColor(100155));   
    42.                
    43.             //線條兩端坐標(biāo)值   
    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.             //畫(huà)線條   
    51.             graph.drawLine(x1, y1, x2, y2);   
    52.         }   
    53.     }   
    54.        
    55.     /**  
    56.      * 隨機(jī)獲取顏色對(duì)象  
    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)應(yīng)用了模板方法模式   
    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.         //獲取畫(huà)布   
    76.         Graphics graph = image.getGraphics();   
    77.            
    78.         //畫(huà)長(zhǎng)方形   
    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.         //畫(huà)干擾線   
    87.         drawRandomLine(graph);   
    88.            
    89.         //畫(huà)字符串   
    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的源代碼

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

    Java代碼 復(fù)制代碼
    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的源代碼

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

    Java代碼 復(fù)制代碼
    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. }  

     

    三、實(shí)例代碼

         1、圖片存儲(chǔ)到文件

    StringBuffer sb = new StringBuffer();   
    1. sb.append("中華人民共和國(guó)\n");   
    2. sb.append("中華人民共和國(guó)\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程序的源代碼很容易被別人偷看,只要有一個(gè)反編譯器,任何人都可以分析別人的代碼。本文討論如何在不修改原有程序的情況下,通過(guò)加密技術(shù)保護(hù)源代碼。

      一、為什么要加密?

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

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

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

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

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

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

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

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

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

    二、定制類裝入器

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

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

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

    以下是引用片段:

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

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

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

      【Listing 2:ClassLoader.loadClass()的一個(gè)簡(jiǎn)單實(shí)現(xiàn)】

    以下是引用片段:

    public Class loadClass( String name, boolean resolve )  如:http://www.5a520.cn
      throws ClassNotFoundException {
      try {
      // 我們要?jiǎng)?chuàng)建的Class對(duì)象
      Class clasz = null;
      // 必需的步驟1:如果類已經(jīng)在系統(tǒng)緩沖之中,
      // 我們不必再次裝入它
      clasz = findLoadedClass( name );
      if (clasz != null)
      return clasz;
      // 下面是定制部分
      byte classData[] = /* 通過(guò)某種方法獲取字節(jié)碼數(shù)據(jù) */;
      if (classData != null) {
      // 成功讀取字節(jié)碼數(shù)據(jù),現(xiàn)在把它轉(zhuǎn)換成一個(gè)Class對(duì)象
      clasz = defineClass( name, classData, 0, classData.length );
      }
      // 必需的步驟2:如果上面沒(méi)有成功,
      // 我們嘗試用默認(rèn)的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顯示了一個(gè)簡(jiǎn)單的loadClass實(shí)現(xiàn)。代碼中的大部分對(duì)所有ClassLoader對(duì)象來(lái)說(shuō)都一樣,但有一小部分(已通過(guò)注釋標(biāo)記)是特有的。在處理過(guò)程中,ClassLoader對(duì)象要用到其他幾個(gè)輔助方法:

      findLoadedClass:用來(lái)進(jìn)行檢查,以便確認(rèn)被請(qǐng)求的類當(dāng)前還不存在。loadClass方法應(yīng)該首先調(diào)用它。

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

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

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

      三、加密、解密

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

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

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

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

    以下是引用片段:

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

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

    以下是引用片段:

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

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

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

     

    四、應(yīng)用實(shí)例

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

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

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

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

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

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

    以下是引用片段:

      import java.io.*;
      import java.security.*;
      import java.lang.reflect.*;
      import javax.crypto.*;
      import javax.crypto.spec.*;
      public class DecryptStart extends ClassLoader
      {
      // 這些對(duì)象在構(gòu)造函數(shù)中設(shè)置,
      // 以后loadClass()方法將利用它們解密類
      private SecretKey key;
      private Cipher cipher;
      // 構(gòu)造函數(shù):設(shè)置解密所需要的對(duì)象
      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過(guò)程:我們要在這里讀入密匙,創(chuàng)建DecryptStart的
      // 實(shí)例,它就是我們的定制ClassLoader。
      // 設(shè)置好ClassLoader以后,我們用它裝入應(yīng)用實(shí)例,
      // 最后,我們通過(guò)Java Reflection API調(diào)用應(yīng)用實(shí)例的main方法
      static public void main( String args[] ) throws Exception {
      String keyFilename = args[0];
      String appName = args[1];
      // 這些是傳遞給應(yīng)用本身的參數(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)建應(yīng)用主類的一個(gè)實(shí)例
      // 通過(guò)ClassLoader裝入它
      System.err.println( "[DecryptStart: loading "+appName+"]" );
      Class clasz = dr.loadClass( appName );
      // 最后,通過(guò)Reflection API調(diào)用應(yīng)用實(shí)例
      // 的main()方法
      // 獲取一個(gè)對(duì)main()的引用
      String proto[] = new String[1];
      Class mainArgs[] = { (new String[1]).getClass() };
      Method main = clasz.getMethod( "main", mainArgs );
      // 創(chuàng)建一個(gè)包含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 {
      // 我們要?jiǎng)?chuàng)建的Class對(duì)象
      Class clasz = null;
      // 必需的步驟1:如果類已經(jīng)在系統(tǒng)緩沖之中
      // 我們不必再次裝入它
      clasz = findLoadedClass( name );
      if (clasz != null)
      return clasz;
      // 下面是定制部分
      try {
      // 讀取經(jīng)過(guò)加密的類文件
      byte classData[] = Util.readFile( name+".class" );
      if (classData != null) {
      // 解密...
      byte decryptedClassData[] = cipher.doFinal( classData );
      // ... 再把它轉(zhuǎn)換成一個(gè)類
      clasz = defineClass( name, decryptedClassData,
      0, decryptedClassData.length );
      System.err.println( "[DecryptStart: decrypting class "+name+"]" );
      }
      } catch( FileNotFoundException fnfe )
      // 必需的步驟2:如果上面沒(méi)有成功
      // 我們嘗試用默認(rèn)的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()
      );
      }
      }
      } 

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

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

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

      五、注意事項(xiàng)

      我們看到,要在不修改源代碼的情況下加密一個(gè)Java應(yīng)用是很容易的。不過(guò),世上沒(méi)有完全安全的系統(tǒng)。本文的加密方式提供了一定程度的源代碼保護(hù),但對(duì)某些攻擊來(lái)說(shuō)它是脆弱的。

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

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

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

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

    java中的io筆記

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

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

     

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

    InputStream 和 OutputStream 是兩個(gè) abstact 類,對(duì)于字節(jié)為導(dǎo)向的 stream 都擴(kuò)展這兩個(gè)雞肋(基類 ^_^ ) ;

    1、 InputStream

     

    42134.gif 

    1.1

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

    construct---

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

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

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

    1.2

    StringBufferInputStream -- 把一個(gè) String 對(duì)象作為 InputStream .

    construct---  

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

     

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

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

    1.3

    FileInputStream -- 把一個(gè)文件作為 InputStream ,實(shí)現(xiàn)對(duì)文件的讀取操作

    construct---

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

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

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

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

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

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

    1.4

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

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

    construct---

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

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

    1.5

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

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

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

    construct---

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

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

     

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

     

    42135.gif
    2.1

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

    當(dāng)數(shù)據(jù)寫(xiě)入緩沖區(qū)時(shí),它自動(dòng)擴(kuò)大。用 toByteArray() 和 toString() 能檢索數(shù)據(jù)。

    constructor

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

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

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

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

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

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

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

    2.2  

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

    constructor

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

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

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

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

    2.3

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

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

    constructor

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

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

     

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

    以 Unicode 字符為導(dǎo)向的 stream ,表示以 Unicode 字符為單位從 stream 中讀取或往 stream 中寫(xiě)入信息。

    Reader/Writer 為 abstact 類

    以 Unicode 字符為導(dǎo)向的 stream 包括下面幾種類型:

    1. Reader

     

    42136.gif 

    1.1

      CharArrayReader :與 ByteArrayInputStream 對(duì)應(yīng)此類實(shí)現(xiàn)一個(gè)可用作字符輸入流的字符緩沖區(qū)

    constructor

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

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

    1.2

    StringReader : 與 StringBufferInputStream 對(duì)應(yīng)其源為一個(gè)字符串的字符流。

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

    1.3

    FileReader : 與 FileInputStream 對(duì)應(yīng)

    1.4

    PipedReader :與 PipedInputStream 對(duì)應(yīng)

     

    2.  Writer

     42137.gif

    2.1    CharArrayWrite : 與 ByteArrayOutputStream 對(duì)應(yīng)

    2.2   StringWrite :無(wú)與之對(duì)應(yīng)的以字節(jié)為導(dǎo)向的 stream

    2.3  FileWrite : 與 FileOutputStream 對(duì)應(yīng)

    2.4  PipedWrite :與 PipedOutputStream 對(duì)應(yīng)

     

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

    3.1

    InputStreamReader 和 OutputStreamReader :

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

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

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

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

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

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

    例如: // 實(shí)現(xiàn)從鍵盤(pán)輸入一個(gè)整數(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)// 當(dāng)應(yīng)用程序試圖將字符串轉(zhuǎn)換成一種數(shù)值類型,但該字符串不能轉(zhuǎn)換為適當(dāng)格式時(shí),拋出該異常。   
    14.               {   
    15.               System.out.println(" 輸入的不是數(shù)字 ");   
    16.               }  

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

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

     

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

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

     

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

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

    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)絡(luò)數(shù)據(jù)流: InputStream, OutputStream,( 字節(jié)流 ) Reader, Writer( 字符流 )

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

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

    三、按是否要緩沖分:

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

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

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

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

    五、按輸入輸出分:

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

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

    六、特殊需要:

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

    2 、對(duì)象輸入輸出: ObjectInputStream, ObjectOutputStream

    3 、進(jìn)程間通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

    4 、合并輸入: SequenceInputStream

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

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

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

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

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

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

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

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

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

    解決一個(gè) MySQL 服務(wù)器進(jìn)程 狂占CPU 的技術(shù)筆記

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

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

     MYSQL CPU 占用 100% 的解決過(guò)程

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

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

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

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

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

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

      于是進(jìn)入 mysql 的 shell 命令行,調(diào)用 show processlist, 查看當(dāng)前 mysql 使用頻繁的 sql 語(yǔ)句:

    mysql> show processlist;

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

    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 檢查這三個(gè)表的結(jié)構(gòu) :

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

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

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

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

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

      建立此索引之后,CPU 馬上降到了 80% 左右。看到找到了問(wèn)題所在,于是檢查另一個(gè)反復(fù)出現(xiàn)在 show processlist 中的 sql 語(yǔ)句:

    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, 沒(méi)有為 keywords 建立 index。_mydata_key 目前有 33 萬(wàn)條記錄,在沒(méi)有索引的情況下對(duì)33萬(wàn)條記錄進(jìn)行文本檢索匹配,不耗費(fèi)大量的 cpu 時(shí)間才怪。看來(lái)就是針對(duì)這個(gè)表的檢索出問(wèn)題了。于是同樣為 _mydata_key 表根據(jù)字段 keywords 加上索引:

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

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

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

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

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

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

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

       

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

      索引 index 用于

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

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

         

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

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

      使用org.apache.commons.net.ftp包開(kāi)發(fā)FTP客戶端,實(shí)現(xiàn)進(jìn)度匯報(bào),實(shí)現(xiàn)斷點(diǎn)續(xù)傳,中文支持

           摘要: 利用org.apache.commons.net.ftp包實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ftp客戶端實(shí)用類。主要實(shí)現(xiàn)一下功能 1.支持上傳下載。支持?jǐn)帱c(diǎn)續(xù)傳 2.支持進(jìn)度匯報(bào) 3.支持對(duì)于中文目錄及中文文件創(chuàng)建的支持。 import java.io.File;    import java.io.FileOutputStream;  ...  閱讀全文

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

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

      導(dǎo)航

      統(tǒng)計(jì)

      常用鏈接

      留言簿(3)

      隨筆檔案

      搜索

      最新評(píng)論

      閱讀排行榜

      評(píng)論排行榜

      主站蜘蛛池模板: 国产精品色午夜免费视频| 亚洲精品无码永久在线观看 | 亚洲性日韩精品一区二区三区 | 91免费国产自产地址入| 国产特级淫片免费看| 亚洲日本中文字幕区| 小说区亚洲自拍另类| 日韩电影免费在线观看中文字幕 | 亚洲男同帅GAY片在线观看| 亚洲日本国产综合高清| 国产一精品一av一免费爽爽| 暖暖免费高清日本中文| 亚洲欧洲国产日韩精品| 国产亚洲精彩视频| 黄色免费网站网址| 亚洲日韩国产精品第一页一区| 亚洲日韩国产一区二区三区在线| 国产在线观看免费视频软件| 四虎成人免费大片在线| 久久精品国产亚洲av麻豆小说| 男女污污污超污视频免费在线看| 国产成人精品免费视频网页大全| 国产午夜亚洲精品午夜鲁丝片| 亚洲精品动漫免费二区| 鲁大师在线影院免费观看| 亚洲精品高清在线| 亚洲中文字幕乱码一区| 99久久精品免费视频| 久久久亚洲精品蜜桃臀| 亚洲经典千人经典日产| 亚洲人成免费网站| 亚洲国产成人高清在线观看| 黄色片网站在线免费观看| 男人的好看免费观看在线视频| 亚洲国产精品久久久久网站| WWW国产成人免费观看视频| 永久免费bbbbbb视频| 亚洲人成777在线播放| 无码成A毛片免费| 亚洲综合国产一区二区三区| 菠萝菠萝蜜在线免费视频|