欧美人体大胆444www,亚洲免费二区,一级二级三级在线观看http://www.aygfsteel.com/table/category/34873.htmlzh-cnWed, 12 Aug 2009 20:30:38 GMTWed, 12 Aug 2009 20:30:38 GMT60利用java操作Excel文件http://www.aygfsteel.com/table/articles/270187.html小卓小卓Tue, 12 May 2009 06:11:00 GMThttp://www.aygfsteel.com/table/articles/270187.htmlhttp://www.aygfsteel.com/table/comments/270187.htmlhttp://www.aygfsteel.com/table/articles/270187.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/270187.htmlhttp://www.aygfsteel.com/table/services/trackbacks/270187.htmlhttp://sourceforge.net/project/showfiles.php?group_id=79926下載。

一.讀取Excel文件內(nèi)容

java 代碼
  1. /**讀取Excel文件的內(nèi)容  
  2.  * @param file  待讀取的文件  
  3.  * @return  
  4.  */  
  5. public static String readExcel(File file){   
  6.     StringBuffer sb = new StringBuffer();   
  7.        
  8.     Workbook wb = null;   
  9.     try {   
  10.         //構(gòu)造Workbook(工作薄)對象   
  11.         wb=Workbook.getWorkbook(file);   
  12.     } catch (BiffException e) {   
  13.         e.printStackTrace();   
  14.     } catch (IOException e) {   
  15.         e.printStackTrace();   
  16.     }   
  17.        
  18.     if(wb==null)   
  19.         return null;   
  20.        
  21.     //獲得了Workbook對象之后,就可以通過它得到Sheet(工作表)對象了   
  22.     Sheet[] sheet = wb.getSheets();   
  23.        
  24.     if(sheet!=null&&sheet.length>0){   
  25.         //對每個工作表進(jìn)行循環(huán)   
  26.         for(int i=0;i
  27.             //得到當(dāng)前工作表的行數(shù)   
  28.             int rowNum = sheet[i].getRows();   
  29.             for(int j=0;j
  30.                 //得到當(dāng)前行的所有單元格   
  31.                 Cell[] cells = sheet[i].getRow(j);   
  32.                 if(cells!=null&&cells.length>0){   
  33.                     //對每個單元格進(jìn)行循環(huán)   
  34.                     for(int k=0;k
  35.                         //讀取當(dāng)前單元格的值   
  36.                         String cellValue = cells[k].getContents();   
  37.                         sb.append(cellValue+"\t");   
  38.                     }   
  39.                 }   
  40.                 sb.append("\r\n");   
  41.             }   
  42.             sb.append("\r\n");   
  43.         }   
  44.     }   
  45.     //最后關(guān)閉資源,釋放內(nèi)存   
  46.     wb.close();   
  47.     return sb.toString();   
  48. }  

二.寫入Excel文件

這里有很多格式了,比如文本內(nèi)容加粗,加上某些顏色等,可以參考jxl的api,同時還推薦一篇不錯的文章:http://www.ibm.com/developerworks/cn/java/l-javaExcel/?ca=j-t10

java 代碼
  1. /**生成一個Excel文件  
  2.      * @param fileName  要生成的Excel文件名  
  3.      */  
  4.     public static void writeExcel(String fileName){   
  5.         WritableWorkbook wwb = null;   
  6.         try {   
  7.             //首先要使用Workbook類的工廠方法創(chuàng)建一個可寫入的工作薄(Workbook)對象   
  8.             wwb = Workbook.createWorkbook(new File(fileName));   
  9.         } catch (IOException e) {   
  10.             e.printStackTrace();   
  11.         }   
  12.         if(wwb!=null){   
  13.             //創(chuàng)建一個可寫入的工作表   
  14.             //Workbook的createSheet方法有兩個參數(shù),第一個是工作表的名稱,第二個是工作表在工作薄中的位置   
  15.             WritableSheet ws = wwb.createSheet("sheet1"0);   
  16.                
  17.             //下面開始添加單元格   
  18.             for(int i=0;i<10;i++){   
  19.                 for(int j=0;j<5;j++){   
  20.                     //這里需要注意的是,在Excel中,第一個參數(shù)表示列,第二個表示行   
  21.                     Label labelC = new Label(j, i, "這是第"+(i+1)+"行,第"+(j+1)+"列");   
  22.                     try {   
  23.                         //將生成的單元格添加到工作表中   
  24.                         ws.addCell(labelC);   
  25.                     } catch (RowsExceededException e) {   
  26.                         e.printStackTrace();   
  27.                     } catch (WriteException e) {   
  28.                         e.printStackTrace();   
  29.                     }   
  30.   
  31.                 }   
  32.             }   
  33.   
  34.             try {   
  35.                 //從內(nèi)存中寫入文件中   
  36.                 wwb.write();   
  37.                 //關(guān)閉資源,釋放內(nèi)存   
  38.                 wwb.close();   
  39.             } catch (IOException e) {   
  40.                 e.printStackTrace();   
  41.             } catch (WriteException e) {   
  42.                 e.printStackTrace();   
  43.             }   
  44.         }   
  45.     }   

三.在一個Excel文件中查找是否包含某一個關(guān)鍵字

java 代碼
  1. /**搜索某一個文件中是否包含某個關(guān)鍵字  
  2.      * @param file  待搜索的文件  
  3.      * @param keyWord  要搜索的關(guān)鍵字  
  4.      * @return  
  5.      */  
  6.     public static boolean searchKeyWord(File file,String keyWord){   
  7.         boolean res = false;   
  8.            
  9.         Workbook wb = null;   
  10.         try {   
  11.             //構(gòu)造Workbook(工作薄)對象   
  12.             wb=Workbook.getWorkbook(file);   
  13.         } catch (BiffException e) {   
  14.             return res;   
  15.         } catch (IOException e) {   
  16.             return res;   
  17.         }   
  18.            
  19.         if(wb==null)   
  20.             return res;   
  21.            
  22.         //獲得了Workbook對象之后,就可以通過它得到Sheet(工作表)對象了   
  23.         Sheet[] sheet = wb.getSheets();   
  24.            
  25.         boolean breakSheet = false;   
  26.            
  27.         if(sheet!=null&&sheet.length>0){   
  28.             //對每個工作表進(jìn)行循環(huán)   
  29.             for(int i=0;i
  30.                 if(breakSheet)   
  31.                     break;   
  32.                    
  33.                 //得到當(dāng)前工作表的行數(shù)   
  34.                 int rowNum = sheet[i].getRows();   
  35.                    
  36.                 boolean breakRow = false;   
  37.                    
  38.                 for(int j=0;j
  39.                     if(breakRow)   
  40.                         break;   
  41.                     //得到當(dāng)前行的所有單元格   
  42.                     Cell[] cells = sheet[i].getRow(j);   
  43.                     if(cells!=null&&cells.length>0){   
  44.                         boolean breakCell = false;   
  45.                         //對每個單元格進(jìn)行循環(huán)   
  46.                         for(int k=0;k
  47.                             if(breakCell)   
  48.                                 break;   
  49.                             //讀取當(dāng)前單元格的值   
  50.                             String cellValue = cells[k].getContents();   
  51.                             if(cellValue==null)   
  52.                                 continue;   
  53.                             if(cellValue.contains(keyWord)){   
  54.                                 res = true;   
  55.                                 breakCell = true;   
  56.                                 breakRow = true;   
  57.                                 breakSheet = true;   
  58.                             }   
  59.                         }   
  60.                     }   
  61.                 }   
  62.             }   
  63.         }   
  64.         //最后關(guān)閉資源,釋放內(nèi)存   
  65.         wb.close();   
  66.            
  67.         return res;   
  68.     }  

四.往Excel中插入圖片圖標(biāo)

插入圖片的實現(xiàn)很容易,參看以下代碼:

java 代碼
  1. /**往Excel中插入圖片  
  2.  * @param dataSheet  待插入的工作表  
  3.  * @param col 圖片從該列開始  
  4.  * @param row 圖片從該行開始  
  5.  * @param width 圖片所占的列數(shù)  
  6.  * @param height 圖片所占的行數(shù)  
  7.  * @param imgFile 要插入的圖片文件  
  8.  */  
  9. public static void insertImg(WritableSheet dataSheet, int col, int row, int width,   
  10.         int height, File imgFile){   
  11.     WritableImage img = new WritableImage(col, row, width, height, imgFile);   
  12.     dataSheet.addImage(img);   
  13. }   

以上代碼的注釋已經(jīng)很清楚了,大概也就不用再解釋了,我們可以用如下程序驗證:

java 代碼
  1.     try {   
  2.         //創(chuàng)建一個工作薄   
  3. WritableWorkbook workbook = Workbook.createWorkbook(new File("D:/test1.xls"));   
  4. //待插入的工作表   
  5. WritableSheet imgSheet = workbook.createSheet("Images",0);   
  6. //要插入的圖片文件   
  7. File imgFile = new File("D:/1.png");   
  8. //圖片插入到第二行第一個單元格,長寬各占六個單元格   
  9. insertImg(imgSheet,0,1,6,6,imgFile);   
  10. workbook.write();   
  11. workbook.close();   
  12.  catch (IOException e) {   
  13. e.printStackTrace();   
  14.  catch (WriteException e) {   
  15. e.printStackTrace();   

 但是jxl只支持png格式的圖片,jpg格式和gif格式都不支持

五.插入頁眉頁腳

一般的頁眉頁腳都分為三個部分,左,中,右三部分,利用如下代碼可實現(xiàn)插入頁眉頁腳

java 代碼
  1. /**向Excel中加入頁眉頁腳  
  2.  * @param dataSheet 待加入頁眉的工作表  
  3.  * @param left  
  4.  * @param center  
  5.  * @param right  
  6.  */  
  7. public static void setHeader(WritableSheet dataSheet,String left,String center,String right){   
  8.     HeaderFooter hf = new HeaderFooter();   
  9.     hf.getLeft().append(left);   
  10.     hf.getCentre().append(center);   
  11.     hf.getRight().append(right);   
  12.     //加入頁眉   
  13.     dataSheet.getSettings().setHeader(hf);   
  14.     //加入頁腳   
  15.     //dataSheet.getSettings().setFooter(hf);   
  16. }  

我們可以用如下代碼測試該方法:

java 代碼
  1.     try {   
  2.         //創(chuàng)建一個工作薄   
  3.     WritableWorkbook workbook = Workbook.createWorkbook(new File("D:/test1.xls"));   
  4.     //待插入的工作表   
  5.     WritableSheet dataSheet = workbook.createSheet("加入頁眉",0);   
  6.     ExcelUtils.setHeader(dataSheet, "chb""2007-03-06""第1頁,共3頁");   
  7.     workbook.write();   
  8.     workbook.close();   
  9. catch (IOException e) {   
  10.     e.printStackTrace();   
  11. catch (WriteException e) {   
  12.     e.printStackTrace();   
  13. }   
  14.   }  


小卓 2009-05-12 14:11 發(fā)表評論
]]>
各種排序算法java實現(xiàn)http://www.aygfsteel.com/table/articles/269223.html小卓小卓Wed, 06 May 2009 05:23:00 GMThttp://www.aygfsteel.com/table/articles/269223.htmlhttp://www.aygfsteel.com/table/comments/269223.htmlhttp://www.aygfsteel.com/table/articles/269223.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/269223.htmlhttp://www.aygfsteel.com/table/services/trackbacks/269223.html插入排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;
/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class InsertSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int temp;
        for(int i=1;i<data.length;i++){
            for(int j=i;(j>0)&&(data[j]<data[j-1]);j--){
                SortUtil.swap(data,j,j-1);
            }
        }       
    }

}
冒泡排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class BubbleSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int temp;
        for(int i=0;i<data.length;i++){
            for(int j=data.length-1;j>i;j--){
                if(data[j]<data[j-1]){
                    SortUtil.swap(data,j,j-1);
                }
            }
        }
    }

}

選擇排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class SelectionSort implements SortUtil.Sort {

    /*
     * (non-Javadoc)
     *
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int temp;
        for (int i = 0; i < data.length; i++) {
            int lowIndex = i;
            for (int j = data.length - 1; j > i; j--) {
                if (data[j] < data[lowIndex]) {
                    lowIndex = j;
                }
            }
            SortUtil.swap(data,i,lowIndex);
        }
    }

}

Shell排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class ShellSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        for(int i=data.length/2;i>2;i/=2){
            for(int j=0;j<i;j++){
                insertSort(data,j,i);
            }
        }
        insertSort(data,0,1);
    }

    /**
     * @param data
     * @param j
     * @param i
     */
    private void insertSort(int[] data, int start, int inc) {
        int temp;
        for(int i=start+inc;i<data.length;i+=inc){
            for(int j=i;(j>=inc)&&(data[j]<data[j-inc]);j-=inc){
                SortUtil.swap(data,j,j-inc);
            }
        }
    }

}

快速排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class QuickSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        quickSort(data,0,data.length-1);       
    }
    private void quickSort(int[] data,int i,int j){
        int pivotIndex=(i+j)/2;
        //swap
        SortUtil.swap(data,pivotIndex,j);
       
        int k=partition(data,i-1,j,data[j]);
        SortUtil.swap(data,k,j);
        if((k-i)>1) quickSort(data,i,k-1);
        if((j-k)>1) quickSort(data,k+1,j);
       
    }
    /**
     * @param data
     * @param i
     * @param j
     * @return
     */
    private int partition(int[] data, int l, int r,int pivot) {
        do{
           while(data[++l]<pivot);
           while((r!=0)&&data[--r]>pivot);
           SortUtil.swap(data,l,r);
        }
        while(l<r);
        SortUtil.swap(data,l,r);       
        return l;
    }

}
改進(jìn)后的快速排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class ImprovedQuickSort implements SortUtil.Sort {

    private static int MAX_STACK_SIZE=4096;
    private static int THRESHOLD=10;
    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int[] stack=new int[MAX_STACK_SIZE];
       
        int top=-1;
        int pivot;
        int pivotIndex,l,r;
       
        stack[++top]=0;
        stack[++top]=data.length-1;
       
        while(top>0){
            int j=stack[top--];
            int i=stack[top--];
           
            pivotIndex=(i+j)/2;
            pivot=data[pivotIndex];
           
            SortUtil.swap(data,pivotIndex,j);
           
            //partition
            l=i-1;
            r=j;
            do{
                while(data[++l]<pivot);
                while((r!=0)&&(data[--r]>pivot));
                SortUtil.swap(data,l,r);
            }
            while(l<r);
            SortUtil.swap(data,l,r);
            SortUtil.swap(data,l,j);
           
            if((l-i)>THRESHOLD){
                stack[++top]=i;
                stack[++top]=l-1;
            }
            if((j-l)>THRESHOLD){
                stack[++top]=l+1;
                stack[++top]=j;
            }
           
        }
        //new InsertSort().sort(data);
        insertSort(data);
    }
    /**
     * @param data
     */
    private void insertSort(int[] data) {
        int temp;
        for(int i=1;i<data.length;i++){
            for(int j=i;(j>0)&&(data[j]<data[j-1]);j--){
                SortUtil.swap(data,j,j-1);
            }
        }      
    }

}

歸并排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class MergeSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int[] temp=new int[data.length];
        mergeSort(data,temp,0,data.length-1);
    }
   
    private void mergeSort(int[] data,int[] temp,int l,int r){
        int mid=(l+r)/2;
        if(l==r) return ;
        mergeSort(data,temp,l,mid);
        mergeSort(data,temp,mid+1,r);
        for(int i=l;i<=r;i++){
            temp[i]=data[i];
        }
        int i1=l;
        int i2=mid+1;
        for(int cur=l;cur<=r;cur++){
            if(i1==mid+1)
                data[cur]=temp[i2++];
            else if(i2>r)
                data[cur]=temp[i1++];
            else if(temp[i1]<temp[i2])
                data[cur]=temp[i1++];
            else
                data[cur]=temp[i2++];           
        }
    }

}

改進(jìn)后的歸并排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class ImprovedMergeSort implements SortUtil.Sort {

    private static final int THRESHOLD = 10;

    /*
     * (non-Javadoc)
     *
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        int[] temp=new int[data.length];
        mergeSort(data,temp,0,data.length-1);
    }

    private void mergeSort(int[] data, int[] temp, int l, int r) {
        int i, j, k;
        int mid = (l + r) / 2;
        if (l == r)
            return;
        if ((mid - l) >= THRESHOLD)
            mergeSort(data, temp, l, mid);
        else
            insertSort(data, l, mid - l + 1);
        if ((r - mid) > THRESHOLD)
            mergeSort(data, temp, mid + 1, r);
        else
            insertSort(data, mid + 1, r - mid);

        for (i = l; i <= mid; i++) {
            temp[i] = data[i];
        }
        for (j = 1; j <= r - mid; j++) {
            temp[r - j + 1] = data[j + mid];
        }
        int a = temp[l];
        int b = temp[r];
        for (i = l, j = r, k = l; k <= r; k++) {
            if (a < b) {
                data[k] = temp[i++];
                a = temp[i];
            } else {
                data[k] = temp[j--];
                b = temp[j];
            }
        }
    }

    /**
     * @param data
     * @param l
     * @param i
     */
    private void insertSort(int[] data, int start, int len) {
        for(int i=start+1;i<start+len;i++){
            for(int j=i;(j>start) && data[j]<data[j-1];j--){
                SortUtil.swap(data,j,j-1);
            }
        }
    }

}
堆排序:

package org.rut.util.algorithm.support;

import org.rut.util.algorithm.SortUtil;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class HeapSort implements SortUtil.Sort{

    /* (non-Javadoc)
     * @see org.rut.util.algorithm.SortUtil.Sort#sort(int[])
     */
    public void sort(int[] data) {
        MaxHeap h=new MaxHeap();
        h.init(data);
        for(int i=0;i<data.length;i++)
            h.remove();
        System.arraycopy(h.queue,1,data,0,data.length);
    }


     private static class MaxHeap{
        
       
        void init(int[] data){
            this.queue=new int[data.length+1];
            for(int i=0;i<data.length;i++){
                queue[++size]=data[i];
                fixUp(size);
            }
        }
        
        private int size=0;

        private int[] queue;
               
        public int get() {
            return queue[1];
        }

        public void remove() {
            SortUtil.swap(queue,1,size--);
            fixDown(1);
        }
        //fixdown
        private void fixDown(int k) {
            int j;
            while ((j = k << 1) <= size) {
                if (j < size && queue[j]<queue[j+1])
                    j++;
                if (queue[k]>queue[j]) //不用交換
                    break;
                SortUtil.swap(queue,j,k);
                k = j;
            }
        }
        private void fixUp(int k) {
            while (k > 1) {
                int j = k >> 1;
                if (queue[j]>queue[k])
                    break;
                SortUtil.swap(queue,j,k);
                k = j;
            }
        }

    }

}

 

SortUtil:

package org.rut.util.algorithm;

import org.rut.util.algorithm.support.BubbleSort;
import org.rut.util.algorithm.support.HeapSort;
import org.rut.util.algorithm.support.ImprovedMergeSort;
import org.rut.util.algorithm.support.ImprovedQuickSort;
import org.rut.util.algorithm.support.InsertSort;
import org.rut.util.algorithm.support.MergeSort;
import org.rut.util.algorithm.support.QuickSort;
import org.rut.util.algorithm.support.SelectionSort;
import org.rut.util.algorithm.support.ShellSort;

/**
 * @author treeroot
 * @since 2006-2-2
 * @version 1.0
 */
public class SortUtil {
    public final static int INSERT = 1;

    public final static int BUBBLE = 2;

    public final static int SELECTION = 3;

    public final static int SHELL = 4;

    public final static int QUICK = 5;

    public final static int IMPROVED_QUICK = 6;

    public final static int MERGE = 7;

    public final static int IMPROVED_MERGE = 8;

    public final static int HEAP = 9;

    public static void sort(int[] data) {
        sort(data, IMPROVED_QUICK);
    }
    private static String[] name={
            "insert","bubble","selection","shell","quick","improved_quick","merge","improved_merge","heap"
    };
   
    private static Sort[] impl=new Sort[]{
            new InsertSort(),
            new BubbleSort(),
            new SelectionSort(),
            new ShellSort(),
            new QuickSort(),
            new ImprovedQuickSort(),
            new MergeSort(),
            new ImprovedMergeSort(),
            new HeapSort()
    };

    public static String toString(int algorithm){
        return name[algorithm-1];
    }
   
    public static void sort(int[] data, int algorithm) {
        impl[algorithm-1].sort(data);
    }

    public static interface Sort {
        public void sort(int[] data);
    }

    public static void swap(int[] data, int i, int j) {
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }
}



小卓 2009-05-06 13:23 發(fā)表評論
]]>
JavaMail API簡介http://www.aygfsteel.com/table/articles/268801.html小卓小卓Mon, 04 May 2009 05:44:00 GMThttp://www.aygfsteel.com/table/articles/268801.htmlhttp://www.aygfsteel.com/table/comments/268801.htmlhttp://www.aygfsteel.com/table/articles/268801.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/268801.htmlhttp://www.aygfsteel.com/table/services/trackbacks/268801.html JavaMail API的設(shè)計是,為收發(fā)信息提供與協(xié)議無關(guān)的訪問。方式是把該API劃分成兩個部分:
· 該API的第一個部分是本課程的重點。基本上是如何發(fā)送和接收獨立于提供程序/協(xié)議的消息。
· 第二個部分則使用特定的協(xié)議語言,如:SMTP、POP、IMAP和NNTP。如果要讓JavaMail API與服務(wù)器通信,就需要為之提供協(xié)議。由于Sun公司對特定協(xié)議提供程序有充分的介紹,用戶可以免費獲取,所以本課程沒有介紹創(chuàng)建特定協(xié)議提供程序的內(nèi)容。
復(fù)習(xí)相關(guān)協(xié)議
在學(xué)習(xí)JavaMail API的深層知識之前,讓我們回過頭來看一看在該API中使用的協(xié)議,本質(zhì)上有4種人們常用的協(xié)議:
· SMTP
· POP
· IMAP
· MIME
您還需要了解NNTP及其他一些協(xié)議。理解這些協(xié)議的基本原理有助于您理解如何使用JavaMail API。而該API的設(shè)計要與協(xié)議無關(guān),所以不能克服這些基礎(chǔ)協(xié)議的限制。如果選用的協(xié)議不支持某種功能,那么JavaMail API也無法在其上添加這種功能。(正如您一會兒就會看到的,在操作POP協(xié)議時,常常會碰到這種問題)。
SMTP
簡單郵件傳輸協(xié)議(SMTP)是用于傳送電子郵件的機制。在JavaMail API環(huán)境中,您的基于JavaMail的程序?qū)⑴c您公司或Internet服務(wù)提供商(ISP)的SMTP服務(wù)器通信。該SMTP服務(wù)器將會把消息轉(zhuǎn)發(fā)給用作接收消息的SMTP服務(wù)器,最后用戶可通過POP或IMAP協(xié)議獲取該消息。由于支持身份驗證,所以不需要SMTP服務(wù)器是一種開放的轉(zhuǎn)發(fā)器,但需要確保SMTP服務(wù)器配置正確。JavaMail API中沒有集成用于處理諸如配置服務(wù)器以轉(zhuǎn)發(fā)消息或添加/刪除電子郵件帳戶這一類任務(wù)的功能。
POP
POP的含義是郵局協(xié)議,當(dāng)前的版本為3,也稱作POP3,該協(xié)議是在RFC 1939中定義的。POP是Internet上的大多數(shù)人用來接收郵件的機制。它為每個用戶的每個郵箱定義支持,這是它所做的全部工作,也是大多數(shù)問題的根源。在使用POP協(xié)議時,人們熟悉的很多功能,如查看收到了多少新郵件消息的功能,POP根本不支持。這些功能都內(nèi)置到諸如Eudora或Microsoft Outlook之類的郵件程序中,能為您記住接收的上一封郵件,以及計算有多少新郵件這類信息。因此,使用JavaMail API時,如果想獲取這類信息,將需要由自己進(jìn)行計算。
IMAP
IMAP是用于接收消息的更加高級的協(xié)議,它是在RFC 2060中定義的。IMAP的含義是“Internet消息訪問協(xié)議”,當(dāng)前版本是第4版,也稱作IMAP4。使用IMAP時,您的郵件服務(wù)器必須支持該協(xié)議。您不能只是簡單地把程序轉(zhuǎn)變?yōu)橹С諭MAP,而不是支持POP,就指望能支持IMAP中的一切。假定您的郵件服務(wù)器支持IMAP,那么基于JavaMail的程序就可利用在服務(wù)器上擁有多個文件夾的用戶,并且這些文件夾可以被多個用戶共享的功能。
由于IMAP協(xié)議具有更高級的功能,您也許會想IMAP應(yīng)該被每一個人使用,但事實不是這樣。因為IMAP會加重郵件服務(wù)器的負(fù)荷,它需要服務(wù)器接收新消息,發(fā)送消息給請求的用戶,并在多個文件夾中為每個用戶維護(hù)這些消息。而這要集中備份,因而長期下去用戶的文件夾會變得越來越大,當(dāng)磁盤空間用光了時,每個人都會遭受損失。而使用POP協(xié)議時,已保存消息可以解除服務(wù)器的重負(fù)。
MIME
MIME的含義是“多用途的網(wǎng)際郵件擴充協(xié)議”。它不是一種郵件傳輸協(xié)議,相反,它定義傳輸?shù)膬?nèi)容:消息的格式、附件等。許多文檔都定義了MIME協(xié)議,包含:RFC 822、RFC 2045、RFC 2046和RFC 2047。作為JavaMail API的用戶,一般不需要擔(dān)心這些格式。但是,這些格式確實存在,并為您的程序所用。
NNP和其他協(xié)議
由于JavaMail API分開了提供程序和其他部分,所以您可以輕松地為附加協(xié)議添加支持。Sun公司提供第3方提供程序清單,這些提供程序要利用 Sun公司不支持的少見的協(xié)議。在這份清單中,您將會看到對NNTP(網(wǎng)絡(luò)新聞傳輸協(xié)議)[新聞組]、S/MIME(安全多用途的網(wǎng)際郵件擴充協(xié)議)及其他協(xié)議的提供支持的第3方提供程序。
安裝
目前有兩種版本的JavaMail API最常用:1.2和1.1.3。本課程中的所有例子都適用于這兩種版本。其中JavaMail API 1.2是最新的,而JavaMail API 1.1.3中包含了Java 2企業(yè)版(J2EE)平臺1.2.1版,所以它仍然很常用。使用JavaMail API的版本會對您的下載和安裝產(chǎn)生一些影響。這兩種版本的JavaMail API都能與JDK 1.1.6、Java 2標(biāo)準(zhǔn)版(J2SE)平臺1.2.x和1.3.x協(xié)同工作。
注意:在安裝了Sun公司的JavaMail工具后,會在演示目錄下看到許多示例程序。
安裝JavaMail 1.2
要使用JavaMail 1.2 API,可以下載JavaMail 1.2工具,然后解壓縮javamail-1_2.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.2工具帶有SMTP、IMAP4和POP3提供程序以及核心類。
安裝完JavaMail 1.2后,再安裝JavaBeans Activation Framework。
安裝JavaMail 1.1.3
要使用JavaMail 1.1.3 API,可以下載JavaMail 1.1.3工具,然后解壓縮javamail1_1_3.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.1.3工具帶有SMTP和IMAP4提供程序以及核心類。
如果您想用JavaMail 1.1.3訪問POP服務(wù)器,需要下載并安裝POP3提供程序。Sun公司擁有一個獨立于 JavaMail 工具的提供程序。在下載并解壓縮pop31_1_1.zip文件后,也還需要把pop3.jar添加到典型安裝路徑下。
安裝完JavaMail 1.1.3后,再安裝JavaBeans Activation Framework。
安裝JavaBeans Activation Framework
JavaMail API的所有版本都需要JavaBeans Activation Framework(JavaBeans激活框架),這種框架提供了對輸入任意數(shù)據(jù)塊的支持,并能相應(yīng)地對其進(jìn)行處理。看上去效果好像不太好,但該框架是在當(dāng)今的許多瀏覽器和郵件工具中可以找到的基本MIME類型支持。下載該框架后,解壓縮jaf1_0_1.zip文件,并將activation.jar文件添加到典型安裝路徑下。
對于JavaMail 1.2用戶,現(xiàn)在應(yīng)該把mail.jar和activation.jar文件添加到典型安裝路徑下。
對于JavaMail 1.1.3用戶,現(xiàn)在應(yīng)該把mail.jar、pop3.jar和activation.jar添加到典型安裝路徑下。如果您不打算使用POP3,就不需要把pop3.jar文件添加到典型安裝路徑下。
如果您不想更改安裝路徑環(huán)境變量,可以把JAR文件復(fù)制到Java運行時環(huán)境(JRE)目錄下的lib/ext目錄下。例如,對于J2SE 1.3版本,Windows平臺上的默認(rèn)目錄應(yīng)該是C:\jdk1.3\jre\lib\ext。
使用Java 2企業(yè)版
如果您使用的是J2EE,則在使用基本JavaMail API時,不需要做什么特殊的工作;JavaMail API帶有J2EE類。只要確保j2ee.jar文件位于典型安裝路徑下,并完成了所有的設(shè)置工作。
對于J2EE 1.2.1,POP3提供程序是單獨提供的,因此需要下載該提供程序,并按安裝JavaMail 1.1.3的步驟,在J2EE 1.2.1中包含POP3提供程序。J2EE 1.3的用戶會獲得J2EE和POP3提供程序,因而不需要對POP3提供程序執(zhí)行獨立安裝。使用這兩種版本的J2EE用戶,都不需要安裝JavaBeans Activation Framework。
練習(xí)
設(shè)置您的 JavaMail 環(huán)境。
復(fù)習(xí)核心類
在開始深入研究JavaMail類之前,首先讓用戶瀏覽一下構(gòu)成API的核心類:會話、消息、地址、驗證程序、傳輸,存儲和文件夾。所有這些類都可以在JavaMail API即javax.mail的頂層包中找到,盡管您將頻繁地發(fā)現(xiàn)您自己使用的子類是在javax.mail.internet包中找到的。
Session類
Session類定義了一個基本的郵件會話。通過該會話可讓別的工作順利執(zhí)行。Session對象利用java.util.Properties對象獲取諸如郵件服務(wù)器、用戶名、密碼等信息,以及其他可在整個應(yīng)用程序中共享的信息。
Session類的構(gòu)造器是私有的。您可以獲得一個可被getDefaultInstance()方法共享的單一的默認(rèn)會話:
Properties props = new Properties();
// fill props with any information
Session session = Session.getDefaultInstance(props, null);
或者,您可以用getInstance()方法創(chuàng)建一個獨特的會話:
Properties props = new Properties();
// fill props with any information
Session session = Session.getInstance(props, null);
這兩種情形下的null參數(shù)都是一種Authenticator對象,它不是在此時使用的。詳細(xì)信息請參閱其后的“Autherticator”一節(jié)。
在大多數(shù)情況下,使用共享會話就足夠了,即使為多個用戶郵箱處理郵件會話也是如此。您可以在通信過程的后面一步添加上用戶名和密碼的組合,并保持所有的一切是獨立的。
Message類
一旦創(chuàng)建了自己的Session對象,就是該去創(chuàng)建要發(fā)送的消息的時候了。這時就要用到消息類型。作為一個抽象類,您必須操作一個子類,在大多數(shù)情況下,該子類是javax.mail.internet.MimeMessage。一個MimeMessage是一種理解MIME類型和報頭(在不同的RFC文檔中均有定義)的消息。消息的報頭被嚴(yán)格限制成只能使用US-ASCII字符,盡管非ASCII字符可以被編碼到某些報頭字段中。
可以通過將Session對象傳遞給MimeMessage構(gòu)造器的方法來創(chuàng)建消息:
MimeMessage message = new MimeMessage(session);
注意:還有其他的構(gòu)造器,像用于創(chuàng)建消息的源于RFC822格式化的輸入流的構(gòu)造器。
一旦創(chuàng)建了消息,就可以設(shè)置其各個部分,如Message(消息)實現(xiàn)Part(部分)接口(以MimeMessage實現(xiàn)MimePart)。設(shè)置內(nèi)容的基本機制是setContent()方法,它帶有表示內(nèi)容和MIME類型的參數(shù):
message.setContent("Hello", "text/plain");
但是,如果正在使用 MimeMessage,并且您的消息是純文本,那么您就可以使用setText()方法。該方法只需要一個表示實際內(nèi)容的參數(shù),默認(rèn)的MIME類型為純文本:
message.setText("Hello");
對于純文本消息,setText()方法更常常被用來設(shè)置內(nèi)容。要發(fā)送其他類型的消息,如HTML消息,就要使用setContent方法()。現(xiàn)在用的更多的是HTML消息。
要設(shè)置主題,可以使用setSubject()方法:
message.setSubject("First");
Address類
一旦創(chuàng)建了會話和消息,并為消息填充了內(nèi)容,就需要用Address類為您的信件標(biāo)上地址了。同Message類一樣,Address類也是一種抽象類。您可以使用javax.mail.internet.InternetAddress類。
要創(chuàng)建只帶有電子郵件地址的地址,可以把電子郵件地址傳遞給Address類的構(gòu)造器:
Address address = new InternetAddress("president@whitehouse.gov");
如果想讓一個名字出現(xiàn)在電子郵件地址后,也可以將其傳遞給構(gòu)造器:
Address address = new InternetAddress("president@whitehouse.gov", "George Bush");
您要為消息的from(發(fā)送者)字段和to(接收者)字段創(chuàng)建地址對象。除非您的郵件服務(wù)器阻止這樣做,否則要在發(fā)送的消息中注明該消息的發(fā)送者。
一旦創(chuàng)建好了地址,有兩種方法可讓您將地址與消息連接起來。為了鑒別發(fā)送者,您可以使用setFrom()和setReplyTo()方法。
message.setFrom(address)
如果您的消息需要顯示多個地址來源,則可以使用addFrom()方法:
Address address[] = ...;
message.addFrom(address);
為了鑒別消息接收者,您可以使用addRecipient()方法。該方法除了需要一個地址參數(shù)外,還需要一個Message.RecipientType屬性(消息的接收類型)。
message.addRecipient(type, address)
地址的3種預(yù)定義類型如下:
· Message.RecipientType.TO
· Message.RecipientType.CC
· Message.RecipientType.BCC
因此,如果一條消息將發(fā)送給副總統(tǒng),同時還將發(fā)送該消息的副本給第一夫人,則采用下面的代碼:
Address toAddress = new InternetAddress("vice.president@whitehouse.gov");
Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");
message.addRecipient(Message.RecipientType.TO, toAddress);
message.addRecipient(Message.RecipientType.CC, ccAddress);
JavaMail API沒有提供檢查電子郵件地址有效性的機制。您可以自己編寫支持掃描有效字符(在RFC 822文檔中所定義的)的程序或檢驗MX(郵件交換)記錄,這些都超越了JavaMail API的范圍。
Authenticator類
與java.net類一樣,JavaMail API可以利用Authenticator(驗證程序)類通過用戶名和密碼來訪問受保護(hù)的資源。對于JavaMail API來說,這種受保護(hù)的資源是指郵件服務(wù)器。JavaMail的Authenticator類可以在javax.mail包中找到,并有別于同名的java.net類。當(dāng)JavaMail API在Java 1.1下工作時,JavaMail和java.net不會共享同一個Authenticator類名稱,這是因為Java 1.1中不含有java.net。
要使用Authenticator類,您可以使用該抽象類的子類,并通過getPasswordAuthentication()方法返回一個PasswordAuthentication實例。在創(chuàng)建時,您必須用會話記錄Authentication類。其后,當(dāng)需要進(jìn)行身份驗證時,會通知您的Authenticator。會彈出一個窗口,或從一個配置文件(盡管不加密就不安全)中讀取用戶名和密碼,并把它們作為一個PasswordAuthentication對象返回給調(diào)用程序。
Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
Transport類
發(fā)送消息的最后一步操作是使用Transport類。該類使用特定于協(xié)議(通常是SMTP)的語言來發(fā)送消息。它是一個抽象類,其操作與Session類有些相似。您可以通過只調(diào)用靜態(tài)的send()方法來使用該類的默認(rèn)版本:
Transport.send(message);
或者,您可以從用于您的協(xié)議的會話中獲取一個特定的實例,然后傳遞用戶名和密碼(不必要時可以為空)并發(fā)送消息,最后關(guān)閉連接:
message.saveChanges(); // implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
當(dāng)您需要發(fā)送多個消息時,建議采用后一種方法,因為它將保持消息間活動服務(wù)器的連接。而基本的send()機制會為每一個方法調(diào)用都建立一條獨立的連接。
注意:要查看經(jīng)過郵件服務(wù)器郵件命令,可以用session.setDebug(true)方法設(shè)置調(diào)試標(biāo)志。
Store和Folder類
使用Session類來獲取消息,開始時與發(fā)送消息很相似。但是,在獲取會話后,很有可能使用用戶名和密碼或Authenticator類來連接Store類。與Transport類一樣,您要告訴Store類將使用什么協(xié)議:
// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);
在連接Store類后,就可以獲取一個Folder類,在讀取其中的消息前必須先打開該類。
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
對于POP3協(xié)議,惟一可用的文件夾是INBOX。如果使用的是IMAP協(xié)議,則可以使用其他的文件夾。
注意:Sun公司的提供程序本來想提供方便。而Message message[]=folder.getMessages();這條語句卻是一種從服務(wù)器逐條讀取消息的緩慢操作,所以僅當(dāng)您確實需要獲取消息部分(該內(nèi)容是所檢索消息的內(nèi)容)時可以使用這條語句。
一旦讀取消息,就可以使用getContent()方法獲取其內(nèi)容,或使用writeTo()方法將其內(nèi)容寫到一個流中。getContent()方法只獲取消息內(nèi)容,而writeTo()方法則還會輸出報頭。
System.out.println(((MimeMessage)message).getContent());
一旦您閱讀完郵件,就可以關(guān)閉對文件夾和存儲的連接。
folder.close(aBoolean);
store.close();
傳遞給文件夾的close()方法的布爾變量指定了是否通過清除已刪除的消息來更新文件夾。
繼續(xù)前進(jìn)
實際上,理解使用這7個類的方式,是使用JavaMail API處理幾乎所有事情所需要的全部內(nèi)容。用這7個類以外的方式構(gòu)建的JavaMail API,其大多數(shù)功能都是以幾乎完全相同或特定的方式來執(zhí)行任務(wù)的,就好像內(nèi)容是附件。特定的任務(wù),如:搜索、隔離等將在后面進(jìn)行介紹。
使用JavaMail API
您已經(jīng)看到了如何操作JavaMail API的核心部分。在下面幾節(jié)中,您將學(xué)習(xí)如何連接幾個部分以執(zhí)行特定的任務(wù)。
發(fā)送消息
發(fā)送電子郵件消息涉及到獲取會話、創(chuàng)建和填充消息并發(fā)送消息這些操作。您可以在獲取Session時,通過為要傳遞的Properties對象設(shè)置mail.smtp.host屬性來指定您的SMTP服務(wù)器。
String host = ...;
String from = ...;
String to = ...;
// Get system properties
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", host);
// Get session
Session session = Session.getDefaultInstance(props, null);
// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail");
message.setText("Welcome to JavaMail");
// Send message
Transport.send(message);
您應(yīng)該在try-catch塊中編寫代碼,以在創(chuàng)建消息并發(fā)送它時可以拋出一個異常。
練習(xí)
發(fā)送您的第一個消息
獲取消息
對于閱讀郵件來說,首先您要獲取一個會話,然后獲取并連接到一個相應(yīng)的用于您的收件箱的存儲上,接著打開相應(yīng)的文件夾,再獲取消息。同時,不要忘記了操作完成后關(guān)閉連接。
String host = ...;
String username = ...;
String password = ...;
// Create empty properties
Properties props = new Properties();
// Get session
Session session = Session.getDefaultInstance(props, null);
// Get the store
Store store = session.getStore("pop3");
store.connect(host, username, password);
// Get folder
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; i++) {
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
}
// Close connection
folder.close(false);
store.close();
每一條消息執(zhí)行何種操作取決于自己決定。上面的代碼塊只是顯示了消息的發(fā)送者和主題。從技術(shù)上講,發(fā)送者地址列表可以為空,此時getFrom()[0]調(diào)用會拋出一個異常。
為了顯示整條消息,您可以提示用戶在看完消息的發(fā)送者和主題字段后,如果想看到消息的內(nèi)容,可以再調(diào)用消息的writeTo()方法。
BufferedReader reader = new BufferedReader (
new InputStreamReader(System.in));
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; i++) {
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
System.out.println("Do you want to read message? " +
"[YES to read/QUIT to end]");
String line = reader.readLine();
if ("YES".equals(line)) {
message[i].writeTo(System.out);
} else if ("QUIT".equals(line)) {
break;
}
}
練習(xí)
檢查郵件
刪除消息和標(biāo)志
刪除消息涉及到操作與消息關(guān)聯(lián)的標(biāo)志。對不同的狀態(tài)有不同的標(biāo)志,有些標(biāo)志是系統(tǒng)定義的,有些則是由用戶定義的。預(yù)定義的標(biāo)志都是在內(nèi)部類Flags.Flag中定義的,如下所示:
· Flags.Flag.ANSWERED
· Flags.Flag.DELETED
· Flags.Flag.DRAFT
· Flags.Flag.FLAGGED
· Flags.Flag.RECENT
· Flags.Flag.SEEN
· Flags.Flag.USER
僅僅因為標(biāo)志存在,并不表示標(biāo)志為所有的郵件服務(wù)器/提供程序所支持。例如,除了刪除消息外,POP協(xié)議對它們都不支持。檢查新郵件不是POP的任務(wù),但它已內(nèi)置到郵件客戶程序中。要搞清楚什么標(biāo)志受到支持,可以使用getPermanentFlags()方法來詢問文件夾。
要刪除消息,需要為消息設(shè)置DELETE標(biāo)志:
message.setFlag(Flags.Flag.DELETED, true);
第一次以READ_WRITE(讀-寫)模式打開文件夾:
folder.open(Folder.READ_WRITE);
然后,處理完了所有的消息,請關(guān)閉文件夾,并傳遞true值以擦去刪除的消息。
folder.close(true);
用戶可使用Folder類的expunge()方法來刪除消息。但是,該方法對Sun公司的POP3提供程序不起作用。其他提供程序或許能也或許不能實現(xiàn)其功能。它更有可能適用于IMAP提供程序。由于POP只支持對收件箱的簡單訪問,使用Sun公司的提供程序時,您將不得不關(guān)閉文件夾以刪除消息。
要移去標(biāo)志,只需傳遞一個false值給setFlag()方法。要看看是否設(shè)置了某個標(biāo)志,可以使用isSet()進(jìn)行檢查。
自我驗證
先前學(xué)到的是使用Authenticator類,以在需要時提示輸入用戶名和密碼,而不是以字符串的形式傳入它們。這里,您將真正看到如何更加充分地使用驗證。
不需使用主機、用戶名和密碼連接到Store,您可以配置Properties帶有主機,并告訴Session關(guān)于您自定義的Authenticator實例,如下所示:
// Setup properties
Properties props = System.getProperties();
props.put("mail.pop3.host", host);
// Setup authentication, get session
Authenticator auth = new PopupAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
// Get the store
Store store = session.getStore("pop3");
store.connect();
然后您可以使用Authenticator類的子類,并通過getPasswordAuthentication()方法返回一個PasswordAuthentication對象。下面是這種實現(xiàn)的一個例子,其中一個字段同時適用于兩部分內(nèi)容。它不是一個Project Swing指南,只是在一個字段中輸入了兩部分內(nèi)容,它們是用逗號隔開的。
import javax.mail.*;
import javax.swing.*;
import java.util.*;
public class PopupAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
String username, password;
String result = JOptionPane.showInputDialog(
"Enter 'username,password'");
StringTokenizer st = new StringTokenizer(result, ",");
username = st.nextToken();
password = st.nextToken();
return new PasswordAuthentication(username, password);
}
}
由于PopupAuthenticator依賴于Swing,因而將會啟動用于AWT的事件處理線程。這在本質(zhì)上要求您在代碼中添加一個對System.exit()的調(diào)用,以終止程序的執(zhí)行。
回復(fù)消息
Message類包含一個reply()方法,以用正確的接收者和主題(添加“Re::”,如果沒有的話)配置一條新消息。該方法不會為消息添加任何內(nèi)容,只是為新的接收者復(fù)制發(fā)送者或回復(fù)到的報頭。該方法使用一個布爾型參數(shù),提示是否只回復(fù)給發(fā)送者(false)或回復(fù)給所有人(true)。
MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);
在發(fā)送消息時要配置回復(fù)到地址,可使用setReplyTo()方法。
練習(xí)
回復(fù)郵件
轉(zhuǎn)發(fā)消息
轉(zhuǎn)發(fā)消息涉及的內(nèi)容要稍微多一點,沒有一個專門用于轉(zhuǎn)發(fā)消息的方法,您可以通過處理組成消息的各個部分來創(chuàng)建要轉(zhuǎn)發(fā)的消息。
一條郵件消息可由多個部分組成,每一部分是一個BodyPart(報文部分),或更特殊一點,在操作MIME消息時則是MimeBodyPart。不同的報文部分組合到一個稱為Multipart的容器中,或者又更特殊一點,是一個MimeMultipart容器。要轉(zhuǎn)發(fā)消息,您要創(chuàng)建一個用于消息文本的部分,和用于要轉(zhuǎn)發(fā)的消息的第二個部分,并將這兩個部分組合成一個multipart(多個部分)。然后您可以把這個multipart添加到一個合適的注明地址的消息中并發(fā)送它。
這就是轉(zhuǎn)發(fā)消息的本質(zhì)。要把一條消息的內(nèi)容復(fù)制給另一條消息,只需通過它的DataHandler類復(fù)制即可,它是出自于JavaBeans Activation Framework的一個類。
// Create the message to forward
Message forward = new MimeMessage(session);
// Fill in header
forward.setSubject("Fwd: " + message.getSubject());
forward.setFrom(new InternetAddress(from));
forward.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(
"Here you go with the original message:\n\n");
// Create a multi-part to combine the parts
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Create and fill part for the forwarded content
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());
// Add part to multi part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
forward.setContent(multipart);
// Send message
Transport.send(forward);
操作附件
附件是與郵件消息關(guān)聯(lián)的資源,通常保存在消息之外,如:一個文本文件,電子表格或圖片。對于像Eudora和Pine之類的常用郵件程序,您可以通過JavaMail API把資源附加到郵件消息上,并在您接收消息時獲取附件。
發(fā)送附件
發(fā)送附件與轉(zhuǎn)發(fā)消息非常相似,您要創(chuàng)建組成完整消息的各個部分。在創(chuàng)建好第一個部分即消息文本之后,您添加的用DataHandler類處理的其他部分就是您的附件,而不是轉(zhuǎn)發(fā)消息中的共享處理程序。當(dāng)您從一個文件讀取附件時,附件的數(shù)據(jù)資源是FileDataSource;從URL讀取時,則是URLDataSource。一旦您有了自己的DataSource,在將其通過setDataHandler()方法最終附加到BodyPart上之前,只需將其傳遞給DataHandler類的構(gòu)造器即可。假定您想保留附件的原始文件名,要做的最后一件事就是用BodyPart類的setFileName()方法設(shè)置與附件關(guān)聯(lián)的文件名。所有這些操作如下所示:
// Define message
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail Attachment");
// Create the message part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("Pardon Ideas");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
// Put parts in message
message.setContent(multipart);
// Send the message
Transport.send(message);
在消息中包含附件時,如果您的程序是一個servlet,您的用戶就必須上傳附件,并告訴您要把消息發(fā)送到什么位置。上傳的每一個文件都可以用一個表單來處理,該表單是以multipart/表單數(shù)據(jù)(form-data)來編碼的。
<FORM ENCTYPE="multipart/form-data"
method=post action="/myservlet">
<INPUT TYPE="file" NAME="thefile">
<INPUT TYPE="submit" VALUE="Upload">
</FORM>
注意:消息的大小要受到您的SMTP服務(wù)器的限制,而不是由JavaMail API限制的。如果出現(xiàn)了問題,可以通過設(shè)置ms和mx參數(shù)來考慮增加Java堆區(qū)的空間尺寸。
練習(xí)
發(fā)送附件
獲取附件
從消息中取出附件比發(fā)送附件涉及的操作要稍微多一點,而MIME沒有簡單的附件概念。當(dāng)消息帶有附件時,消息的內(nèi)容就是一個Multipart對象。然后需要處理各個部分,以獲取主要內(nèi)容和附件。通過part.getDisposition()方法標(biāo)記上Part.ATTACHMENT配置的部分顯然就是附件。同時,附件也可以不帶有配置(和非文本MIME類型)或Part.INLINE配置。當(dāng)配置是Part.ATTACHMENT或Part.INLINE時,您可以脫離該消息部分的內(nèi)容將其保存起來。只需通過getFileName()方法獲取原始文件名,并通過getInputStream()方法獲取輸入流即可。
Multipart mp = (Multipart)message.getContent();
for (int i=0, n=multipart.getCount(); i<n; i++) {
Part part = multipart.getBodyPart(i));
String disposition = part.getDisposition();
if ((disposition != null) &&
((disposition.equals(Part.ATTACHMENT) ||
(disposition.equals(Part.INLINE))) {
saveFile(part.getFileName(), part.getInputStream());
}
}
saveFile()方法只用于根據(jù)文件名創(chuàng)建一個文件,從輸入流中讀取字節(jié),并將它們寫入一個文件中去。如果文件已存在,將在文件名后添加一個編號,直到找到一個不存在的文件為止。
// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
file = new File(filename+i);
}
上面的代碼介紹了消息的各個部分被標(biāo)上相應(yīng)的標(biāo)志的一個最簡單的例子。要想包含所有的情況,還要對disposition值為null及消息部分為MIME類型的情況作相應(yīng)處理。
if (disposition == null) {
// Check if plain
MimeBodyPart mbp = (MimeBodyPart)part;
if (mbp.isMimeType("text/plain")) {
// Handle plain
} else {
// Special non-attachment cases here of image/gif, text/html, ...
}
...
}
處理HTML消息
發(fā)送基于HTML的消息比發(fā)送純文本消息要稍微復(fù)雜一點,盡管它不需要做大量的工作。它全部取決于您特定的需求。
發(fā)送HTML消息
如果您所要做的全部工作是發(fā)送一個等價的HTML文件作為消息,并讓郵件閱讀者憂心于取出任何嵌入的圖片或相關(guān)片段,那么就可以使用消息的setContent()方法,以字符串形式傳遞消息內(nèi)容,并把內(nèi)容類型設(shè)置為text/html。
String htmlText = "<H1>Hello</H1>" +
"<img src=\"/ShowImg.asp?p=/2008-5-14/45749picalogo.gif\">";
message.setContent(htmlText, "text/html"));
在接收端,如果您用JavaMail API獲取消息,在該API中沒有內(nèi)置任何用于以HTML格式顯示消息的功能。JavaMail API只以字節(jié)流的形式來查看消息。要以HTML格式顯示消息,您必須使用Swing JeditorPane或某些第3方HTML閱讀器組件。
if (message.getContentType().equals("text/html")) {
String content = (String)message.getContent();
JFrame frame = new JFrame();
JEditorPane text = new JEditorPane("text/html", content);
text.setEditable(false);
JScrollPane pane = new JScrollPane(text);
frame.getContentPane().add(pane);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.show();
}
在消息中包含圖片
另一方面,如果您的HTML消息中嵌入了作為消息一部分的圖片,并且您想保持消息內(nèi)容的完整,就必須把圖片看作附件,并用特殊的通信標(biāo)識符URL引用該圖片,該通信標(biāo)識符引用的是圖片附件的內(nèi)容ID報文。
嵌入圖片的處理與附加一個文件到消息上非常相似,惟一的不同之處在于:您必須區(qū)分MimeMultipart中,哪些部分是在構(gòu)造器(或通過setSubType()方法)通過設(shè)置其子類型而使之相關(guān)的,以及將圖片的內(nèi)容ID報頭設(shè)置成任意字符串,它將在img標(biāo)記中用作圖片的源路徑。下面顯示了一個完整的示例:
String file = ...;
// Create the message
Message message = new MimeMessage(session);
// Fill its headers
message.setSubject("Embedded Image");
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
String htmlText = "<H1>Hello</H1>" +
"<img src=\"cid:memememe\">";
messageBodyPart.setContent(htmlText, "text/html");
// Create a related multi-part to combine the parts
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);
// Create part for the image
messageBodyPart = new MimeBodyPart();
// Fetch the image and associate to part
DataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","memememe");
// Add part to multi-part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message.setContent(multipart);
練習(xí)
發(fā)送帶有圖片的 HTML 消息
用SearchTerm搜索
JavaMail API包含一種可用于創(chuàng)建SearchTerm(搜索條件)的篩選機制,它可以在javax.mail.search包中找到。一旦創(chuàng)建了SearchTerm,您就可以詢問某個文件夾匹配的消息,并檢索出消息對象數(shù)組:
SearchTerm st = ...;
Message[] msgs = folder.search(st);
有22種不同的類可用于幫助創(chuàng)建搜索條件。
· AND條件(AndTerm類)
· OR條件(OrTerm類)
· NOT條件(NotTerm類)
· SENT DATE條件(SentDateTerm類)
· CONTENT條件(BodyTerm類)
· HEADER條件(FromTerm / FromStringTerm, RecipientTerm / RecipientStringTerm, SubjectTerm, etc.)
本質(zhì)上,您可以為匹配的消息創(chuàng)建一個邏輯表達(dá)式,然后進(jìn)行搜索。例如,下面顯示了一條消息的條件搜索示例,該消息帶有(部分帶有)一個ADV主題字符串,其發(fā)送者字段為friend@public.com。您可能考慮定期運行該查詢,并自動刪除任何返回的消息。
SearchTerm st =
new OrTerm(
new SubjectTerm("ADV:"),
new FromStringTerm("friend@public.com"));
Message[] msgs = folder.search(st);
  

小卓 2009-05-04 13:44 發(fā)表評論
]]>
HttpClient入門http://www.aygfsteel.com/table/articles/256220.html小卓小卓Mon, 23 Feb 2009 06:43:00 GMThttp://www.aygfsteel.com/table/articles/256220.htmlhttp://www.aygfsteel.com/table/comments/256220.htmlhttp://www.aygfsteel.com/table/articles/256220.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/256220.htmlhttp://www.aygfsteel.com/table/services/trackbacks/256220.html
文檔選項
將打印機的版面設(shè)置成橫向打印模式

打印本頁

將此頁作為電子郵件發(fā)送

將此頁作為電子郵件發(fā)送

未顯示需要 JavaScript 的文檔選項


級別: 高級

發(fā)華 金, 軟件工程師,IBM CSDL, IBM
樟洪 陳, 軟件工程師,IBM CSDL, IBM

2005 年 11 月 10 日

HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。本文首先介紹 HTTPClient,然后根據(jù)作者實際工作經(jīng)驗給出了一些常見問題的解決方法。

HttpClient簡介

HTTP 協(xié)議可能是現(xiàn)在 Internet 上使用得最多、最重要的協(xié)議了,越來越多的 Java 應(yīng)用程序需要直接通過 HTTP 協(xié)議來訪問網(wǎng)絡(luò)資源。雖然在 JDK 的 java.net 包中已經(jīng)提供了訪問 HTTP 協(xié)議的基本功能,但是對于大部分應(yīng)用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協(xié)議的客戶端編程工具包,并且它支持 HTTP 協(xié)議最新的版本和建議。HttpClient 已經(jīng)應(yīng)用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient,更多使用 HttpClient 的應(yīng)用可以參見http://wiki.apache.org/jakarta-httpclient/HttpClientPowered。HttpClient 項目非常活躍,使用的人還是非常多的。目前 HttpClient 版本是在 2005.10.11 發(fā)布的 3.0 RC4 。





回頁首


HttpClient 功能介紹

以下列出的是 HttpClient 提供的主要的功能,要知道更多詳細(xì)的功能可以參見 HttpClient 的主頁。

  • 實現(xiàn)了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
  • 支持自動轉(zhuǎn)向
  • 支持 HTTPS 協(xié)議
  • 支持代理服務(wù)器等

下面將逐一介紹怎樣使用這些功能。首先,我們必須安裝好 HttpClient。





回頁首


HttpClient 基本功能的使用

GET 方法

使用 HttpClient 需要以下 6 個步驟:

1. 創(chuàng)建 HttpClient 的實例

2. 創(chuàng)建某種連接方法的實例,在這里是 GetMethod。在 GetMethod 的構(gòu)造函數(shù)中傳入待連接的地址

3. 調(diào)用第一步中創(chuàng)建好的實例的 execute 方法來執(zhí)行第二步中創(chuàng)建好的 method 實例

4. 讀 response

5. 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接

6. 對得到后的內(nèi)容進(jìn)行處理

根據(jù)以上步驟,我們來編寫用GET方法來取得某網(wǎng)頁內(nèi)容的代碼。

  • 大部分情況下 HttpClient 默認(rèn)的構(gòu)造函數(shù)已經(jīng)足夠使用。
    HttpClient httpClient = new HttpClient();
                    

  • 創(chuàng)建GET方法的實例。在GET方法的構(gòu)造函數(shù)中傳入待連接的地址即可。用GetMethod將會自動處理轉(zhuǎn)發(fā)過程,如果想要把自動處理轉(zhuǎn)發(fā)過程去掉的話,可以調(diào)用方法setFollowRedirects(false)。
    GetMethod getMethod = new GetMethod("http://www.ibm.com/");
                    

  • 調(diào)用實例httpClient的executeMethod方法來執(zhí)行g(shù)etMethod。由于是執(zhí)行在網(wǎng)絡(luò)上的程序,在運行executeMethod方法的時候,需要處理兩個異常,分別是HttpException和IOException。引起第一種異常的原因主要可能是在構(gòu)造getMethod的時候傳入的協(xié)議不對,比如不小心將"http"寫成"htp",或者服務(wù)器端返回的內(nèi)容不正常等,并且該異常發(fā)生是不可恢復(fù)的;第二種異常一般是由于網(wǎng)絡(luò)原因引起的異常,對于這種異常 (IOException),HttpClient會根據(jù)你指定的恢復(fù)策略自動試著重新執(zhí)行executeMethod方法。HttpClient的恢復(fù)策略可以自定義(通過實現(xiàn)接口HttpMethodRetryHandler來實現(xiàn))。通過httpClient的方法setParameter設(shè)置你實現(xiàn)的恢復(fù)策略,本文中使用的是系統(tǒng)提供的默認(rèn)恢復(fù)策略,該策略在碰到第二類異常的時候?qū)⒆詣又卦?次。executeMethod返回值是一個整數(shù),表示了執(zhí)行該方法后服務(wù)器返回的狀態(tài)碼,該狀態(tài)碼能表示出該方法執(zhí)行是否成功、需要認(rèn)證或者頁面發(fā)生了跳轉(zhuǎn)(默認(rèn)狀態(tài)下GetMethod的實例是自動處理跳轉(zhuǎn)的)等。
    //設(shè)置成了默認(rèn)的恢復(fù)策略,在發(fā)生異常時候?qū)⒆詣又卦?次,在這里你也可以設(shè)置成自定義的恢復(fù)策略
                    getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                    new DefaultHttpMethodRetryHandler());
                    //執(zhí)行g(shù)etMethod
                    int statusCode = client.executeMethod(getMethod);
                    if (statusCode != HttpStatus.SC_OK) {
                    System.err.println("Method failed: " + getMethod.getStatusLine());
                    }
                    

  • 在返回的狀態(tài)碼正確后,即可取得內(nèi)容。取得目標(biāo)地址的內(nèi)容有三種方法:第一種,getResponseBody,該方法返回的是目標(biāo)的二進(jìn)制的byte流;第二種,getResponseBodyAsString,這個方法返回的是String類型,值得注意的是該方法返回的String的編碼是根據(jù)系統(tǒng)默認(rèn)的編碼方式,所以返回的String值可能編碼類型有誤,在本文的"字符編碼"部分中將對此做詳細(xì)介紹;第三種,getResponseBodyAsStream,這個方法對于目標(biāo)地址中有大量數(shù)據(jù)需要傳輸是最佳的。在這里我們使用了最簡單的getResponseBody方法。
    byte[] responseBody = method.getResponseBody();
                    

  • 釋放連接。無論執(zhí)行方法是否成功,都必須釋放連接。
    method.releaseConnection();
                    

  • 處理內(nèi)容。在這一步中根據(jù)你的需要處理內(nèi)容,在例子中只是簡單的將內(nèi)容打印到控制臺。
    System.out.println(new String(responseBody));
                    

下面是程序的完整代碼,這些代碼也可在附件中的test.GetSample中找到。


package test;
            import java.io.IOException;
            import org.apache.commons.httpclient.*;
            import org.apache.commons.httpclient.methods.GetMethod;
            import org.apache.commons.httpclient.params.HttpMethodParams;
            public class GetSample{
            public static void main(String[] args) {
            //構(gòu)造HttpClient的實例
            HttpClient httpClient = new HttpClient();
            //創(chuàng)建GET方法的實例
            GetMethod getMethod = new GetMethod("http://www.ibm.com");
            //使用系統(tǒng)提供的默認(rèn)的恢復(fù)策略
            getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
            new DefaultHttpMethodRetryHandler());
            try {
            //執(zhí)行g(shù)etMethod
            int statusCode = httpClient.executeMethod(getMethod);
            if (statusCode != HttpStatus.SC_OK) {
            System.err.println("Method failed: "
            + getMethod.getStatusLine());
            }
            //讀取內(nèi)容
            byte[] responseBody = getMethod.getResponseBody();
            //處理內(nèi)容
            System.out.println(new String(responseBody));
            } catch (HttpException e) {
            //發(fā)生致命的異常,可能是協(xié)議不對或者返回的內(nèi)容有問題
            System.out.println("Please check your provided http address!");
            e.printStackTrace();
            } catch (IOException e) {
            //發(fā)生網(wǎng)絡(luò)異常
            e.printStackTrace();
            } finally {
            //釋放連接
            getMethod.releaseConnection();
            }
            }
            }
            

POST方法

根據(jù)RFC2616,對POST的解釋如下:POST方法用來向目的服務(wù)器發(fā)出請求,要求它接受被附在請求后的實體,并把它當(dāng)作請求隊列(Request-Line)中請求URI所指定資源的附加新子項。POST被設(shè)計成用統(tǒng)一的方法實現(xiàn)下列功能:

  • 對現(xiàn)有資源的注釋(Annotation of existing resources)
  • 向電子公告欄、新聞組,郵件列表或類似討論組發(fā)送消息
  • 提交數(shù)據(jù)塊,如將表單的結(jié)果提交給數(shù)據(jù)處理過程
  • 通過附加操作來擴展數(shù)據(jù)庫

調(diào)用HttpClient中的PostMethod與GetMethod類似,除了設(shè)置PostMethod的實例與GetMethod有些不同之外,剩下的步驟都差不多。在下面的例子中,省去了與GetMethod相同的步驟,只說明與上面不同的地方,并以登錄清華大學(xué)BBS為例子進(jìn)行說明。

  • 構(gòu)造PostMethod之前的步驟都相同,與GetMethod一樣,構(gòu)造PostMethod也需要一個URI參數(shù),在本例中,登錄的地址是http://www.newsmth.net/bbslogin2.php。在創(chuàng)建了PostMethod的實例之后,需要給method實例填充表單的值,在BBS的登錄表單中需要有兩個域,第一個是用戶名(域名叫id),第二個是密碼(域名叫passwd)。表單中的域用類NameValuePair來表示,該類的構(gòu)造函數(shù)第一個參數(shù)是域名,第二參數(shù)是該域的值;將表單所有的值設(shè)置到PostMethod中用方法setRequestBody。另外由于BBS登錄成功后會轉(zhuǎn)向另外一個頁面,但是HttpClient對于要求接受后繼服務(wù)的請求,比如POST和PUT,不支持自動轉(zhuǎn)發(fā),因此需要自己對頁面轉(zhuǎn)向做處理。具體的頁面轉(zhuǎn)向處理請參見下面的"自動轉(zhuǎn)向"部分。代碼如下:
    String url = "http://www.newsmth.net/bbslogin2.php";
                    PostMethod postMethod = new PostMethod(url);
                    // 填入各個表單域的值
                    NameValuePair[] data = { new NameValuePair("id", "youUserName"),
                    new NameValuePair("passwd", "yourPwd") };
                    // 將表單的值放入postMethod中
                    postMethod.setRequestBody(data);
                    // 執(zhí)行postMethod
                    int statusCode = httpClient.executeMethod(postMethod);
                    // HttpClient對于要求接受后繼服務(wù)的請求,象POST和PUT等不能自動處理轉(zhuǎn)發(fā)
                    // 301或者302
                    if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
                    statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    // 從頭中取出轉(zhuǎn)向的地址
                    Header locationHeader = postMethod.getResponseHeader("location");
                    String location = null;
                    if (locationHeader != null) {
                    location = locationHeader.getValue();
                    System.out.println("The page was redirected to:" + location);
                    } else {
                    System.err.println("Location field value is null.");
                    }
                    return;
                    }
                    

完整的程序代碼請參見附件中的test.PostSample





回頁首


使用HttpClient過程中常見的一些問題

下面介紹在使用HttpClient過程中常見的一些問題。

字符編碼

某目標(biāo)頁的編碼可能出現(xiàn)在兩個地方,第一個地方是服務(wù)器返回的http頭中,另外一個地方是得到的html/xml頁面中。

  • 在http頭的Content-Type字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個頭信息表明該頁的編碼是UTF-8,但是服務(wù)器返回的頭信息未必與內(nèi)容能匹配上。比如對于一些雙字節(jié)語言國家,可能服務(wù)器返回的編碼類型是UTF-8,但真正的內(nèi)容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁面的編碼信息;但是如果服務(wù)器返回的編碼不是UTF-8,而是具體的一些編碼,比如gb2312等,那服務(wù)器返回的可能是正確的編碼信息。通過method對象的getResponseCharSet()方法就可以得到http頭中的編碼信息。
  • 對于象xml或者h(yuǎn)tml這樣的文件,允許作者在頁面中直接指定編碼類型。比如在html中會有<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>這樣的標(biāo)簽;或者在xml中會有<?xml version="1.0" encoding="gb2312"?>這樣的標(biāo)簽,在這些情況下,可能與http頭中返回的編碼信息沖突,需要用戶自己判斷到底那種編碼類型應(yīng)該是真正的編碼。

自動轉(zhuǎn)向

根據(jù)RFC2616中對自動轉(zhuǎn)向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當(dāng)返回的是301,則表示請求的資源已經(jīng)被移到一個固定的新地方,任何向該地址發(fā)起請求都會被轉(zhuǎn)到新的地址上。302表示暫時的轉(zhuǎn)向,比如在服務(wù)器端的servlet程序調(diào)用了sendRedirect方法,則在客戶端就會得到一個302的代碼,這時服務(wù)器返回的頭信息中l(wèi)ocation的值就是sendRedirect轉(zhuǎn)向的目標(biāo)地址。

HttpClient支持自動轉(zhuǎn)向處理,但是象POST和PUT方式這種要求接受后繼服務(wù)的請求方式,暫時不支持自動轉(zhuǎn)向,因此如果碰到POST方式提交后返回的是301或者302的話需要自己處理。就像剛才在POSTMethod中舉的例子:如果想進(jìn)入登錄BBS后的頁面,必須重新發(fā)起登錄的請求,請求的地址可以在頭字段location中得到。不過需要注意的是,有時候location返回的可能是相對路徑,因此需要對location返回的值做一些處理才可以發(fā)起向新地址的請求。

另外除了在頭中包含的信息可能使頁面發(fā)生重定向外,在頁面中也有可能會發(fā)生頁面的重定向。引起頁面自動轉(zhuǎn)發(fā)的標(biāo)簽是:<meta http-equiv="refresh" content="5; url=http://www.ibm.com/us">。如果你想在程序中也處理這種情況的話得自己分析頁面來實現(xiàn)轉(zhuǎn)向。需要注意的是,在上面那個標(biāo)簽中url的值也可以是一個相對地址,如果是這樣的話,需要對它做一些處理后才可以轉(zhuǎn)發(fā)。

處理HTTPS協(xié)議

HttpClient提供了對SSL的支持,在使用SSL之前必須安裝JSSE。在Sun提供的1.4以后的版本中,JSSE已經(jīng)集成到JDK中,如果你使用的是JDK1.4以前的版本則必須安裝JSSE。JSSE不同的廠家有不同的實現(xiàn)。下面介紹怎么使用HttpClient來打開Https連接。這里有兩種方法可以打開https連接,第一種就是得到服務(wù)器頒發(fā)的證書,然后導(dǎo)入到本地的keystore中;另外一種辦法就是通過擴展HttpClient的類來實現(xiàn)自動接受證書。

方法1,取得證書,并導(dǎo)入本地的keystore:

  • 安裝JSSE (如果你使用的JDK版本是1.4或者1.4以上就可以跳過這一步)。本文以IBM的JSSE為例子說明。先到IBM網(wǎng)站上下載JSSE的安裝包。然后解壓開之后將ibmjsse.jar包拷貝到<java-home>\lib\ext\目錄下。
  • 取得并且導(dǎo)入證書。證書可以通過IE來獲得:

    1. 用IE打開需要連接的https網(wǎng)址,會彈出如下對話框:



    2. 單擊"View Certificate",在彈出的對話框中選擇"Details",然后再單擊"Copy to File",根據(jù)提供的向?qū)纱L問網(wǎng)頁的證書文件



    3. 向?qū)У谝徊剑瑲g迎界面,直接單擊"Next",



    4. 向?qū)У诙剑x擇導(dǎo)出的文件格式,默認(rèn),單擊"Next",



    5. 向?qū)У谌剑斎雽?dǎo)出的文件名,輸入后,單擊"Next",



    6. 向?qū)У谒牟剑瑔螕?Finish",完成向?qū)?/p>

    7. 最后彈出一個對話框,顯示導(dǎo)出成功



  • 用keytool工具把剛才導(dǎo)出的證書倒入本地keystore。Keytool命令在<java-home>\bin\下,打開命令行窗口,并到<java-home>\lib\security\目錄下,運行下面的命令:

    keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
                    

    其中參數(shù)alias后跟的值是當(dāng)前證書在keystore中的唯一標(biāo)識符,但是大小寫不區(qū)分;參數(shù)file后跟的是剛才通過IE導(dǎo)出的證書所在的路徑和文件名;如果你想刪除剛才導(dǎo)入到keystore的證書,可以用命令:

    keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
                    

  • 寫程序訪問https地址。如果想測試是否能連上https,只需要稍改一下GetSample例子,把請求的目標(biāo)變成一個https地址。
    GetMethod getMethod = new GetMethod("https://www.yourdomain.com");
                    

    運行該程序可能出現(xiàn)的問題:

    1. 拋出異常java.net.SocketException: Algorithm SSL not available。出現(xiàn)這個異常可能是因為沒有加JSSEProvider,如果用的是IBM的JSSE Provider,在程序中加入這樣的一行:

     if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)
                    Security.addProvider(new IBMJSSEProvider());
                    

    或者也可以打開<java-home>\lib\security\java.security,在行

    security.provider.1=sun.security.provider.Sun
                    security.provider.2=com.ibm.crypto.provider.IBMJCE
                    

    后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider

    2. 拋出異常java.net.SocketException: SSL implementation not available。出現(xiàn)這個異常可能是你沒有把ibmjsse.jar拷貝到<java-home>\lib\ext\目錄下。

    3. 拋出異常javax.net.ssl.SSLHandshakeException: unknown certificate。出現(xiàn)這個異常表明你的JSSE應(yīng)該已經(jīng)安裝正確,但是可能因為你沒有把證書導(dǎo)入到當(dāng)前運行JRE的keystore中,請按照前面介紹的步驟來導(dǎo)入你的證書。

方法2,擴展HttpClient類實現(xiàn)自動接受證書

因為這種方法自動接收所有證書,因此存在一定的安全問題,所以在使用這種方法前請仔細(xì)考慮您的系統(tǒng)的安全需求。具體的步驟如下:

  • 提供一個自定義的socket factory(test.MySecureProtocolSocketFactory)。這個自定義的類必須實現(xiàn)接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory,在實現(xiàn)接口的類中調(diào)用自定義的X509TrustManager(test.MyX509TrustManager),這兩個類可以在隨本文帶的附件中得到
  • 創(chuàng)建一個org.apache.commons.httpclient.protocol.Protocol的實例,指定協(xié)議名稱和默認(rèn)的端口號
    Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
                    

  • 注冊剛才創(chuàng)建的https協(xié)議對象
    Protocol.registerProtocol("https ", myhttps);
                    

  • 然后按照普通編程方式打開https的目標(biāo)地址,代碼請參見test.NoCertificationHttpsGetSample

處理代理服務(wù)器

HttpClient中使用代理服務(wù)器非常簡單,調(diào)用HttpClient中setProxy方法就可以,方法的第一個參數(shù)是代理服務(wù)器地址,第二個參數(shù)是端口號。另外HttpClient也支持SOCKS代理。


httpClient.getHostConfiguration().setProxy(hostName,port);
            





回頁首


結(jié)論

從上面的介紹中,可以知道HttpClient對http協(xié)議支持非常好,使用起來很簡單,版本更新快,功能也很強大,具有足夠的靈活性和擴展性。對于想在Java應(yīng)用中直接訪問http資源的編程人員來說,HttpClient是一個不可多得的好工具。



參考資料

  • Commons logging包含了各種各樣的日志API的實現(xiàn),讀者可以通過站點http://jakarta.apache.org/commons/logging/得到詳細(xì)的內(nèi)容

  • Commons codec包含了一些一般的解碼/編碼算法。包含了語音編碼、十六進(jìn)制、Base64和URL編碼等,通過http://jakarta.apache.org/commons/codec/可以得到詳細(xì)的內(nèi)容

  • rfc2616是關(guān)于HTTP/1.1的文檔,可以在http://www.faqs.org/rfcs/rfc2616.html上得到詳細(xì)的內(nèi)容,另外rfc1945是關(guān)于HTTP/1.0的文檔,通過http://www.faqs.org/rfcs/rfc1945.html可以得到詳細(xì)內(nèi)容

  • SSL――SSL 是由 Netscape Communications Corporation 于 1994 年開發(fā)的,而 TLS V1.0 是由 Internet Engineering Task Force(IETF)定義的標(biāo)準(zhǔn),它基于 SSL V3.0,并且在使用的加密算法上與其有些許的不同。例如,SSL 使用 Message Authentication Code(MAC)算法來生成完整性校驗值,而 TLS 應(yīng)用密鑰的 Hashing for Message Authentication Code(HMAC)算法。

  • IBM JSSE提供了SSL(Secure Sockets Layer)和TLS(Transport Layer Security)的java實現(xiàn),在http://www-03.ibm.com/servers/eserver/zseries/software/java/jsse.html中可以得到詳細(xì)的信息

  • Keytool是一個管理密鑰和證書的工具。關(guān)于它詳細(xì)的使用信息可以在http://www.doc.ic.ac.uk/csg/java/1.3.1docs/tooldocs/solaris/keytool.html上得到

  • HTTPClient的主頁是http://jakarta.apache.org/commons/httpclient/,你可以在這里得到關(guān)于HttpClient更加詳細(xì)的信息


作者簡介

 

金發(fā)華是一名工作在 IBM CSDL 的軟件工程師。他喜歡鉆研各種新的技術(shù),在 Java 網(wǎng)絡(luò)開發(fā)和 Web 開發(fā)方面頗有經(jīng)驗。


 

陳樟洪是一位 IBM CSDL 的軟件工程師,目前從事企業(yè)電子商務(wù)應(yīng)用的開發(fā)。



小卓 2009-02-23 14:43 發(fā)表評論
]]>
鞏固java基礎(chǔ)之值傳遞和引用傳遞http://www.aygfsteel.com/table/articles/246910.html小卓小卓Wed, 17 Dec 2008 08:57:00 GMThttp://www.aygfsteel.com/table/articles/246910.htmlhttp://www.aygfsteel.com/table/comments/246910.htmlhttp://www.aygfsteel.com/table/articles/246910.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246910.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246910.html

值傳遞:方法調(diào)用時,實際參數(shù)把它的值傳遞給對應(yīng)的形式參數(shù),方法執(zhí)行中形式參數(shù)值的改變不影響實際參 數(shù)的值。
引用傳遞:也稱為傳地址。方法調(diào)用時,實際參數(shù)的引用(地址,而不是參數(shù)的值)被傳遞給方法中相對應(yīng)的形式參數(shù),在方法執(zhí)行中,對形式參數(shù)的操作實際上就是對實際參數(shù)的操作,方法執(zhí)行中形式參數(shù)值的改變將會影響實際參數(shù)的值。
下面舉例說明:
傳值---傳遞基本數(shù)據(jù)類型參數(shù)

Java代碼 復(fù)制代碼
  1. public    class           PassValue{   
  2.     static void exchange(int a, int b){//靜態(tài)方法,交換a,b的值   
  3.         int temp;   
  4.         temp = a;   
  5.         a = b;   
  6.         b = temp;   
  7.     }   
  8.     public static void main(String[] args){   
  9.        int i = 10;   
  10.        int j = 100;   
  11.        System.out.println("before call: " + "i=" + i + "\t" + "j = " + j);//調(diào)用前   
  12.         exchange(i, j);                                                                    //值傳遞,main方法只能調(diào)用靜態(tài)方法   
  13.         System.out.println("after call: " + "i=" + i + "\t" + "j = " + j);//調(diào)用后   
  14.     }   
  15. }  

 
 

運行結(jié)果:
        before call: i = 10        j = 100
        after    call: i = 10        j = 100
說明:調(diào)用exchange(i, j)時,實際參數(shù)i,j分別把值傳遞給相應(yīng)的形式參數(shù)a,b,在執(zhí)行方法exchange()時,形式參數(shù)a,b的值的改變不影響實際參數(shù)i和j的值,i和j的值在調(diào)用前后并沒改變。
引用傳遞---對象作為參數(shù)
如果在方法中把對象(或數(shù)組)作為參數(shù),方法調(diào)用時,參數(shù)傳遞的是對象的引用(地址),即在方法調(diào)用時,實際參數(shù)把對對象的引用(地址)傳遞給形式參數(shù)。這是實際參數(shù)與形式參數(shù)指向同一個地址,即同一個對象(數(shù)組),方法執(zhí)行時,對形式參數(shù)的改變實際上就是對實際參數(shù)的改變,這個結(jié)果在調(diào)用結(jié)束后被保留了下來。

Java代碼 復(fù)制代碼
  1. class Book{   
  2.     String name;   
  3.     private folat price;   
  4.     Book(String n,    float ){                //構(gòu)造方法   
  5.         name = n;   
  6.         price = p;   
  7.     }   
  8.     static  void  change(Book a_book,    String n,    float p){    //靜態(tài)方法,對象作為參數(shù)   
  9.             a_book.name = n;   
  10.             a_book.price = p;   
  11.     }   
  12.     public void output(){        //實例方法,輸出對象信息   
  13.         System.out.println("name: " + name + "\t" + "price: " + price);   
  14.     }   
  15. }   
  16.  public class PassAddr{   
  17.     public static void main(String [] args){   
  18.         Book b = new Book("java2",    32.5f);   
  19.         System.out.print("before call:\t");        //調(diào)用前   
  20.         b.output();   
  21.         b.change(b,    "c++",    45.5f);            //引用傳遞,傳遞對象b的引用,修改對象b的值   
  22.         System.out.print("after call:\t");            //調(diào)用后   
  23.         b.output();   
  24.     }   
  25. }  

 

運行結(jié)果:
        before    call:    name:java2        price:32.5
        after       call:    name:c++          price:45.5
說明:調(diào)用change(b,"c++",45.5f)時,對象b作為實際參數(shù),把引用傳遞給相應(yīng)的形式參數(shù)a_book,實際上a_book也指向同一個對象,即該對象有兩個引用名:b和a_book。在執(zhí)行方法change()時,對形式參數(shù)a_book操作就是對實際參數(shù)b的操作。

 



小卓 2008-12-17 16:57 發(fā)表評論
]]>
SimpleDateFormat格式化日期 http://www.aygfsteel.com/table/articles/246908.html小卓小卓Wed, 17 Dec 2008 08:54:00 GMThttp://www.aygfsteel.com/table/articles/246908.htmlhttp://www.aygfsteel.com/table/comments/246908.htmlhttp://www.aygfsteel.com/table/articles/246908.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246908.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246908.html
Java代碼 復(fù)制代碼
  1. import java.text.SimpleDateFormat;   
  2. import java.util.Date;   
  3. public class test {   
  4.  public static void main(String []aa){   
  5.   SimpleDateFormat dateformat1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");   
  6.   String a1=dateformat1.format(new Date());   
  7.   System.out.println("時間2:"+a1);   
  8.   System.out.println(new Date().getYear()+1900);   
  9.      
  10.   SimpleDateFormat dateformat2=new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒 E ");      
  11.         String a2=dateformat2.format(new Date());   
  12.         System.out.println("時間2:"+a2);    
  13.  }   
  14. }  

 


執(zhí)行結(jié)果:
時間2:2006-12-21 14:40:59 星期四
2006
時間2:2006年12月21日 14時40分59秒 星期四


Java代碼 復(fù)制代碼
  1. java.util.Calendar對于日期的處理非常的方便,如newDate.set(Calendar.MONTH, 12); //加12個月,newDate.set(Calendar.DATE, -1); //前一天   
  2. import java.text.SimpleDateFormat;   
  3. import java.util.Date;   
  4. import java.util.Calendar;   
  5. public class calendartest {   
  6.  /**  
  7.   * @param args  
  8.   */  
  9.  public static void main(String[] args) {   
  10.   SimpleDateFormat dateformat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");   
  11.      
  12.   System.out.println("今天  :"+dateformat.format(new Date()));   
  13.      
  14.   Calendar c=Calendar.getInstance();     
  15.      
  16.   c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);   
  17.   Date d1=new Date(c.getTimeInMillis());   
  18.   System.out.println("星期一:"+dateformat.format(d1));   
  19.      
  20.      
  21.   c.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);   
  22.   Date d2=new Date(c.getTimeInMillis());   
  23.   System.out.println("星期日:"+dateformat.format(d2));     
  24.      
  25.      
  26.  }   
  27.   
  28. }  

 

執(zhí)行結(jié)果:
今天  :2006-12-21 16:39:03 星期四
星期一:2006-12-18 16:39:03 星期一
星期日:2006-12-17 16:39:03 星期日



小卓 2008-12-17 16:54 發(fā)表評論
]]>
java如何遠(yuǎn)程訪問一個共享目錄http://www.aygfsteel.com/table/articles/246906.html小卓小卓Wed, 17 Dec 2008 08:52:00 GMThttp://www.aygfsteel.com/table/articles/246906.htmlhttp://www.aygfsteel.com/table/comments/246906.htmlhttp://www.aygfsteel.com/table/articles/246906.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246906.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246906.html由于工作需要讀取局域網(wǎng)中一臺機器的 共享目錄中的文件,需要jcifs-1.1.11.jar的支持,使用SMB協(xié)議協(xié)議,以下是實現(xiàn)了遠(yuǎn)程讀取并復(fù)制到本地,然后刪除本地文件的功能:

 

Java代碼 復(fù)制代碼
  1. import java.io.BufferedInputStream;   
  2. import java.io.BufferedOutputStream;   
  3. import java.io.File;   
  4. import java.io.FileOutputStream;   
  5. import java.io.IOException;   
  6. import java.io.InputStream;   
  7. import java.io.OutputStream;   
  8. import java.util.Date;   
  9.   
  10. import jcifs.smb.SmbFile;   
  11. import jcifs.smb.SmbFileInputStream;   
  12.   
  13. public class TestReadSmb {   
  14.     public static void main(String[] args) ...{   
  15.             String smbMachine="smb://10.108.23.200/temp/說明文件.txt";   
  16.             String localPath="D:/temp";   
  17.             File file=readFromSmb(smbMachine,localPath);   
  18.             removeFile(file);   
  19.     }   
  20.   
  21.     /** ***  
  22.      * 從smbMachine讀取文件并存儲到localpath指定的路徑  
  23.     *   
  24.      * @param smbMachine  
  25.      *            共享機器的文件,如smb://xxx:xxx@10.108.23.112/myDocument/測試文本.txt,xxx:xxx是共享機器的用戶名密碼  
  26.    * @param localpath  
  27.      *            本地路徑  
  28.    * @return  
  29.      */  
  30. public static File readFromSmb(String smbMachine,String localpath){   
  31.         File localfile=null;   
  32.         InputStream bis=null;   
  33.         OutputStream bos=null;   
  34.         try ...{   
  35.             SmbFile rmifile = new SmbFile(smbMachine);   
  36.             String filename=rmifile.getName();   
  37.             bis=new BufferedInputStream(new SmbFileInputStream(rmifile));   
  38.             localfile=new File(localpath+File.separator+filename);   
  39.             bos=new BufferedOutputStream(new FileOutputStream(localfile));   
  40.             int length=rmifile.getContentLength();   
  41.             byte[] buffer=new byte[length];   
  42.             Date date=new Date();   
  43.             bis.read(buffer);   
  44.             bos.write(buffer);               
  45.             Date end=new Date();   
  46.             int time= (int) ((end.getTime()-date.getTime())/1000);   
  47.             if(time>0)   
  48.                 System.out.println("用時:"+time+"秒 "+"速度:"+length/time/1024+"kb/秒");               
  49.         } catch (Exception e) ...{   
  50.             // TODO Auto-generated catch block   
  51.             System.out.println(e.getMessage());   
  52.                
  53.         }finally{   
  54.             try {   
  55.                 bos.close();   
  56.                 bis.close();   
  57.             } catch (IOException e) {   
  58. //                // TODO Auto-generated catch block   
  59.                 e.printStackTrace();   
  60.             }               
  61.         }   
  62.         return localfile;   
  63.     }   
  64.     public static boolean removeFile(File file) {   
  65.         return file.delete();   
  66.     }   
  67. }  


小卓 2008-12-17 16:52 發(fā)表評論
]]>
處理clob大字段相關(guān)配置http://www.aygfsteel.com/table/articles/246338.html小卓小卓Mon, 15 Dec 2008 02:36:00 GMThttp://www.aygfsteel.com/table/articles/246338.htmlhttp://www.aygfsteel.com/table/comments/246338.htmlhttp://www.aygfsteel.com/table/articles/246338.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246338.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246338.html在hibernate映射文件里默認(rèn)String類型改為ClobStringType--
<property name="content" type="org.springframework.orm.hibernate3.support.ClobStringType" lazy="true" length="1048576000">
    <column name="CONTENT" />
</property>
===================================================
在sessionFactory里添加--
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
   <!-- 為處理Clob類型字段的句柄聲明 //-->
   <property name="lobHandler" ref="lobHandler" />
</bean>

<bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor" lazy-init="true" />
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
   <property name="nativeJdbcExtractor">
    <ref local="nativeJdbcExtractor" />
   </property>
</bean>



小卓 2008-12-15 10:36 發(fā)表評論
]]>
jsp導(dǎo)出word、excelhttp://www.aygfsteel.com/table/articles/246336.html小卓小卓Mon, 15 Dec 2008 02:34:00 GMThttp://www.aygfsteel.com/table/articles/246336.htmlhttp://www.aygfsteel.com/table/comments/246336.htmlhttp://www.aygfsteel.com/table/articles/246336.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246336.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246336.html把對象或者列表傳到Jsp頁面上;

頭部加入:word為<%@ page contentType="application/vnd.ms-word; charset=gb2312" %>,Excel為<%@ page contentType="application/msexcel; charset=gb2312" %>

加入此句,給導(dǎo)出的文件命名:<%response.setHeader("Content-disposition","attachment; filename="+"txl"+".xls");%>或者response.setHeader("Content-disposition","attachment; filename=print_tmp.doc");

即可,注意的是,導(dǎo)出的文件命名用中文出錯,對word排版最好用div,用table即使border=0在word上還是有虛框出來。



小卓 2008-12-15 10:34 發(fā)表評論
]]>
java中格式化輸出數(shù)字http://www.aygfsteel.com/table/articles/246327.html小卓小卓Mon, 15 Dec 2008 02:08:00 GMThttp://www.aygfsteel.com/table/articles/246327.htmlhttp://www.aygfsteel.com/table/comments/246327.htmlhttp://www.aygfsteel.com/table/articles/246327.html#Feedback0http://www.aygfsteel.com/table/comments/commentRss/246327.htmlhttp://www.aygfsteel.com/table/services/trackbacks/246327.html 主要使用的類:java.text.DecimalFormat
1。實例化對象,可以用如下兩種方法:
    DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();
    DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
   因為DecimalFormat繼承自NumberFormat。
2。設(shè)定小數(shù)位數(shù)
系統(tǒng)默認(rèn)小數(shù)位數(shù)為3,如:
   DecimalFormat df=(DecimalFormat)NumberFormat.getInstance();
   System.out.println(df.format(12.3456789));
輸出:12.346
現(xiàn)在可以通過如下方法把小數(shù)為設(shè)為兩位:
df.setMaximumFractionDigits(2);
   System.out.println(df.format(12.3456789));
則輸出為:12.35
3。將數(shù)字轉(zhuǎn)化為百分比輸出,有如下兩種方法:
(1)
df.applyPattern("##.##%");
   System.out.println(df.format(12.3456789));
   System.out.println(df.format(1));
   System.out.println(df.format(0.015));
輸出分別為:1234.57% 100%    1.5%
(2)
df.setMaximumFractionDigits(2);
   System.out.println(df.format(12.3456789*100)+"%");
   System.out.println(df.format(1*100)+"%");
   System.out.println(df.format(0.015*100)+"%");
輸出分別為:
1,234.57%   100%   1.5%
4。設(shè)置分組大小
   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
df1.setGroupingSize(2);
   System.out.println(df1.format(123456789));
輸出:1,23,45,67,89
還可以通過df1.setGroupingUsed(false);來禁用分組設(shè)置,如:
   DecimalFormat df1=(DecimalFormat) DecimalFormat.getInstance();
   df1.setGroupingSize(2);
df1.setGroupingUsed(false);
   System.out.println(df1.format(123456789));
輸出:123456789
5。設(shè)置小數(shù)為必須為2位
DecimalFormat df2=(DecimalFormat) DecimalFormat.getInstance();
  df2.applyPattern("0.00");
   System.out.println(df2.format(1.2));
輸出:1.20

小卓 2008-12-15 10:08 發(fā)表評論
]]>
主站蜘蛛池模板: 甘洛县| 泸水县| 遂川县| 汾西县| 西和县| 兴国县| 集贤县| 阿克| 沭阳县| 恭城| 桐乡市| 江西省| 高陵县| 高清| 丹东市| 工布江达县| 海原县| 成安县| 卓资县| 德化县| 将乐县| 南安市| 延庆县| 时尚| 三江| 沁水县| 波密县| 台中县| 修文县| 阳新县| 天门市| 连南| 长泰县| 疏勒县| 长岭县| 云浮市| 图们市| 应城市| 阿合奇县| 阜平县| 读书|