1. 程式人生 > >28個Java常用的工具類

28個Java常用的工具類

package com.cucpay.tradeportal.util;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.mina.core.buffer.IoBuffer;

/**
 * 交易前置系統專用工具類
 * @create Aug 15, 2012 12:16:49 PM
 * @update Sep 27, 2012 3:07:09 PM
 * @author 玄玉<http://blog.csdn/net/jadyer>
 * @version v2.0
 * @history v1.7.2-->新增<code>getHexSign()</code>通過指定演算法簽名字串方法
 * @history v1.7.2-->新增<code>getString()</code>位元組陣列轉為字串方法
 * @history v1.7.3-->修改<code>getSysJournalNo()</code>實現細節為<code>java.util.UUID.randomUUID()</code>
 * @history v1.7.4-->新增<code>getHexSign()</code>根據指定的簽名金鑰和演算法簽名Map<String,String>
 * @history v1.7.5-->新增<code>getStringSimple()</code>獲取一個字串的簡明效果,返回的字串格式類似於"abcd***hijk"
 * @history v2.0-->區域性的StringBuffer一律StringBuilder之(本思路提示自坦克<
[email protected]
>) */ public class TradePortalUtil { private TradePortalUtil(){} /** * 獲取系統流水號 * @see 若欲指定返回值的長度or是否全由數字組成等,you can call {@link TradePortalUtil#getSysJournalNo(int, boolean)} * @return 長度為20的全數字 */ public static String getSysJournalNo(){ return getSysJournalNo(20, true); } /** * 獲取系統流水號 * @param length 指定流水號長度 * @param toNumber 指定流水號是否全由數字組成 */ public static String getSysJournalNo(int length, boolean isNumber){ //replaceAll()之後返回的是一個由十六進位制形式組成的且長度為32的字串 String uuid = UUID.randomUUID().toString().replaceAll("-", ""); if(uuid.length() > length){ uuid = uuid.substring(0, length); }else if(uuid.length() < length){ for(int i=0; i<length-uuid.length(); i++){ uuid = uuid + Math.round(Math.random()*9); } } if(isNumber){ return uuid.replaceAll("a", "1").replaceAll("b", "2").replaceAll("c", "3").replaceAll("d", "4").replaceAll("e", "5").replaceAll("f", "6"); }else{ return uuid; } } /** * 判斷輸入的字串引數是否為空 * @return boolean 空則返回true,非空則flase */ public static boolean isEmpty(String input) { return null==input || 0==input.length() || 0==input.replaceAll("\\s", "").length(); } /** * 判斷輸入的位元組陣列是否為空 * @return boolean 空則返回true,非空則flase */ public static boolean isEmpty(byte[] bytes){ return null==bytes || 0==bytes.length; } /** * 從org.apache.mina.core.buffer.IoBuffer中讀取字串 * @see 該方法預設以GBK解碼 * @see 若想自己指定字符集,可以使用<code>getStringFromIoBuffer(IoBuffer buffer, int size, String charset)</code>方法 * @param size 所要讀取的位元組數 */ public static String getStringFromIoBuffer(IoBuffer buffer, int size){ return getStringFromIoBuffer(buffer, size, "GBK"); } /** * 從org.apache.mina.core.buffer.IoBuffer中讀取字串 * @param size 所要讀取的位元組數 * @param charset 解碼的字符集 */ public static String getStringFromIoBuffer(IoBuffer buffer, int size, String charset){ String result = null; try { result = buffer.getString(size, Charset.forName(charset).newDecoder()); } catch (CharacterCodingException e) { LogUtil.getLogger().error("字元解碼異常,自動切換第二種解碼方式,本次的堆疊資訊如下", e); try { result = new String(buffer.array(), charset); } catch (UnsupportedEncodingException ee) { LogUtil.getLogger().error("字元解碼異常,系統不支援該字符集[" + charset + "],本次的堆疊資訊如下", ee); } } return result; } /** * 獲取實體類中的屬性 * @see 本方法用到了反射,其適用於所有的屬性型別均為byte[]的JavaBean * @return String field11=value11 field22=value22 field33=value33 */ public static String getStringFromObjectForByte(Object obj){ StringBuilder sb = new StringBuilder(); //區域性的StringBuffer一律StringBuilder之 sb.append(obj.getClass().getName()).append("@").append(obj.hashCode()).append("["); for(Field field : obj.getClass().getDeclaredFields()){ String methodName = "get" + StringUtils.capitalize(field.getName()); //構造getter方法 Object fieldValue = null; try{ fieldValue = obj.getClass().getDeclaredMethod(methodName).invoke(obj); //執行getter方法,獲取其返回值 }catch(Exception e){ //一旦發生異常,便將屬性值置為UnKnown,故此處沒必要一一捕獲所有異常 sb.append("\n").append(field.getName()).append("=UnKnown"); continue; } if(fieldValue == null){ sb.append("\n").append(field.getName()).append("=null"); }else{ sb.append("\n").append(field.getName()).append("=").append(new String((byte[])fieldValue)); } } return sb.append("\n]").toString(); } /** * 獲取Map中的屬性 * @see 由於Map.toString()打印出來的引數值對,是橫著一排的...引數多的時候,不便於檢視各引數值 * @see 故此仿照commons-lang.jar中的ReflectionToStringBuilder.toString()編寫了本方法 * @return String key11=value11 \n key22=value22 \n key33=value33 \n...... */ public static String getStringFromMap(Map<String, String> map){ StringBuilder sb = new StringBuilder(); sb.append(map.getClass().getName()).append("@").append(map.hashCode()).append("["); for(Map.Entry<String,String> entry : map.entrySet()){ sb.append("\n").append(entry.getKey()).append("=").append(entry.getValue()); } return sb.append("\n]").toString(); } /** * 獲取Map中的屬性 * @see 該方法的引數適用於列印Map<String, byte[]>的情況 * @return String key11=value11 \n key22=value22 \n key33=value33 \n...... */ public static String getStringFromMapForByte(Map<String, byte[]> map){ StringBuilder sb = new StringBuilder(); sb.append(map.getClass().getName()).append("@").append(map.hashCode()).append("["); for(Map.Entry<String,byte[]> entry : map.entrySet()){ sb.append("\n").append(entry.getKey()).append("=").append(new String(entry.getValue())); } return sb.append("\n]").toString(); } /** * 獲取Map中的屬性 * @see 該方法的引數適用於列印Map<String, Object>的情況 * @return String key11=value11 \n key22=value22 \n key33=value33 \n...... */ public static String getStringFromMapForObject(Map<String, Object> map){ StringBuilder sb = new StringBuilder(); sb.append(map.getClass().getName()).append("@").append(map.hashCode()).append("["); for(Map.Entry<String,Object> entry : map.entrySet()){ sb.append("\n").append(entry.getKey()).append("=").append(entry.getValue().toString()); } return sb.append("\n]").toString(); } /** * 金額元轉分 * @see 注意:該方法可處理貳仟萬以內的金額,且若有小數位,則不限小數位的長度 * @see 注意:如果你的金額達到了貳仟萬以上,則不推薦使用該方法,否則計算出來的結果會令人大吃一驚 * @param amount 金額的元進位制字串 * @return String 金額的分進位制字串 */ public static String moneyYuanToFen(String amount){ if(isEmpty(amount)){ return amount; } //傳入的金額字串代表的是一個整數 if(-1 == amount.indexOf(".")){ return Integer.parseInt(amount) * 100 + ""; } //傳入的金額字串裡面含小數點-->取小數點前面的字串,並將之轉換成單位為分的整數表示 int money_fen = Integer.parseInt(amount.substring(0, amount.indexOf("."))) * 100; //取到小數點後面的字串 String pointBehind = (amount.substring(amount.indexOf(".") + 1)); //amount=12.3 if(pointBehind.length() == 1){ return money_fen + Integer.parseInt(pointBehind)*10 + ""; } //小數點後面的第一位字串的整數表示 int pointString_1 = Integer.parseInt(pointBehind.substring(0, 1)); //小數點後面的第二位字串的整數表示 int pointString_2 = Integer.parseInt(pointBehind.substring(1, 2)); //amount==12.03,amount=12.00,amount=12.30 if(pointString_1 == 0){ return money_fen + pointString_2 + ""; }else{ return money_fen + pointString_1*10 + pointString_2 + ""; } } /** * 金額元轉分 * @see 該方法會將金額中小數點後面的數值,四捨五入後只保留兩位....如12.345-->12.35 * @see 注意:該方法可處理貳仟萬以內的金額 * @see 注意:如果你的金額達到了貳仟萬以上,則非常不建議使用該方法,否則計算出來的結果會令人大吃一驚 * @param amount 金額的元進位制字串 * @return String 金額的分進位制字串 */ public static String moneyYuanToFenByRound(String amount){ if(isEmpty(amount)){ return amount; } if(-1 == amount.indexOf(".")){ return Integer.parseInt(amount) * 100 + ""; } int money_fen = Integer.parseInt(amount.substring(0, amount.indexOf("."))) * 100; String pointBehind = (amount.substring(amount.indexOf(".") + 1)); if(pointBehind.length() == 1){ return money_fen + Integer.parseInt(pointBehind)*10 + ""; } int pointString_1 = Integer.parseInt(pointBehind.substring(0, 1)); int pointString_2 = Integer.parseInt(pointBehind.substring(1, 2)); //下面這種方式用於處理pointBehind=245,286,295,298,995,998等需要四捨五入的情況 if(pointBehind.length() > 2){ int pointString_3 = Integer.parseInt(pointBehind.substring(2, 3)); if(pointString_3 >= 5){ if(pointString_2 == 9){ if(pointString_1 == 9){ money_fen = money_fen + 100; pointString_1 = 0; pointString_2 = 0; }else{ pointString_1 = pointString_1 + 1; pointString_2 = 0; } }else{ pointString_2 = pointString_2 + 1; } } } if(pointString_1 == 0){ return money_fen + pointString_2 + ""; }else{ return money_fen + pointString_1*10 + pointString_2 + ""; } } /** * 金額分轉元 * @see 注意:如果傳入的引數中含小數點,則直接原樣返回 * @see 該方法返回的金額字串格式為<code>00.00</code>,其整數位有且至少有一個,小數位有且長度固定為2 * @param amount 金額的分進位制字串 * @return String 金額的元進位制字串 */ public static String moneyFenToYuan(String amount){ if(isEmpty(amount)){ return amount; } if(amount.indexOf(".") > -1){ return amount; } if(amount.length() == 1){ return "0.0" + amount; }else if(amount.length() == 2){ return "0." + amount; }else{ return amount.substring(0, amount.length()-2) + "." + amount.substring(amount.length()-2); } } /** * 位元組陣列轉為字串 * @see 該方法預設以ISO-8859-1轉碼 * @see 若想自己指定字符集,可以使用<code>getString(byte[] data, String charset)</code>方法 */ public static String getString(byte[] data){ return getString(data, "ISO-8859-1"); } /** * 位元組陣列轉為字串 * @see 如果系統不支援所傳入的<code>charset</code>字符集,則按照系統預設字符集進行轉換 */ public static String getString(byte[] data, String charset){ if(isEmpty(data)){ return ""; } if(isEmpty(charset)){ return new String(data); } try { return new String(data, charset); } catch (UnsupportedEncodingException e) { LogUtil.getLogger().error("將byte陣列[" + data + "]轉為String時發生異常:系統不支援該字符集[" + charset + "]"); return new String(data); } } /** * 獲取一個字串的簡明效果 * @return String 返回的字串格式類似於"abcd***hijk" */ public static String getStringSimple(String data){ return data.substring(0,4) + "***" + data.substring(data.length()-4); } /** * 字串轉為位元組陣列 * @see 該方法預設以ISO-8859-1轉碼 * @see 若想自己指定字符集,可以使用<code>getBytes(String str, String charset)</code>方法 */ public static byte[] getBytes(String data){ return getBytes(data, "ISO-8859-1"); } /** * 字串轉為位元組陣列 * @see 如果系統不支援所傳入的<code>charset</code>字符集,則按照系統預設字符集進行轉換 */ public static byte[] getBytes(String data, String charset){ data = (data==null ? "" : data); if(isEmpty(charset)){ return data.getBytes(); } try { return data.getBytes(charset); } catch (UnsupportedEncodingException e) { LogUtil.getLogger().error("將字串[" + data + "]轉為byte[]時發生異常:系統不支援該字符集[" + charset + "]"); return data.getBytes(); } } /** * 根據指定的簽名金鑰和演算法簽名Map<String,String> * @see 方法內部首先會過濾Map<String,String>引數中的部分鍵值對 * @see 過濾規則:移除鍵名為"cert","hmac","signMsg"或者鍵值為null或者鍵值長度為零的鍵值對 * @see 過濾結果:過濾完Map<String,String>後會產生一個字串,其格式為[key11=value11|key22=value22|key33=value33] * @see And the calls {@link TradePortalUtil#getHexSign(String,String,String,boolean)}進行簽名 * @param param 待簽名的Map<String,String> * @param charset 簽名時轉碼用到的字符集 * @param algorithm 簽名時所使用的演算法,從業務上看,目前其可傳入兩個值:MD5,SHA-1 * @param signKey 簽名用到的金鑰 * @return String algorithm digest as a lowerCase hex string */ public static String getHexSign(Map<String, String> param, String charset, String algorithm, String signKey){ StringBuilder sb = new StringBuilder(); List<String> keys = new ArrayList<String>(param.keySet()); Collections.sort(keys); for(int i=0; i<keys.size(); i++){ String key = keys.get(i); String value = param.get(key); if(key.equalsIgnoreCase("cert") || key.equalsIgnoreCase("hmac") || key.equalsIgnoreCase("signMsg") || value==null || value.length()==0){ continue; } sb.append(key).append("=").append(value).append("|"); } sb.append("key=").append(signKey); return getHexSign(sb.toString(), charset, algorithm, true); } /** * 通過指定演算法簽名字串 * @see Calculates the algorithm digest and returns the value as a hex string * @see If system dosen't support this <code>algorithm</code>, return "" not null * @see It will Calls {@link TradePortalUtil#getBytes(String str, String charset)} * @see 若系統不支援<code>charset</code>字符集,則按照系統預設字符集進行轉換 * @see 若系統不支援<code>algorithm</code>演算法,則直接返回""空字串 * @see 另外,commons-codec.jar提供的DigestUtils.md5Hex(String data)與本方法getHexSign(data, "UTF-8", "MD5", false)效果相同 * @param data Data to digest * @param charset 字串轉碼為byte[]時使用的字符集 * @param algorithm 目前其有效值為<code>MD5,SHA,SHA1,SHA-1,SHA-256,SHA-384,SHA-512</code> * @param toLowerCase 指定是否返回小寫形式的十六進位制字串 * @return String algorithm digest as a lowerCase hex string */ public static String getHexSign(String data, String charset, String algorithm, boolean toLowerCase){ char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; //Used to build output as Hex char[] DIGITS = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; //get byte[] from {@link TradePortalUtil#getBytes(String, String)} byte[] dataBytes = getBytes(data, charset); byte[] algorithmData = null; try { //get an algorithm digest instance algorithmData = MessageDigest.getInstance(algorithm).digest(dataBytes); } catch (NoSuchAlgorithmException e) { LogUtil.getLogger().error("簽名字串[" + data + "]時發生異常:System doesn't support this algorithm[" + algorithm + "]"); return ""; } char[] respData = new char[algorithmData.length << 1]; //two characters form the hex value for(int i=0,j=0; i<algorithmData.length; i++){ respData[j++] = DIGITS[(0xF0 & algorithmData[i]) >>> 4]; respData[j++] = DIGITS[0x0F & algorithmData[i]]; } return new String(respData); } /** * 字元編碼 * @see 該方法預設會以UTF-8編碼字串 * @see 若想自己指定字符集,可以使用<code>encode(String chinese, String charset)</code>方法 */ public static String encode(String chinese){ return encode(chinese, "UTF-8"); } /** * 字元編碼 * @see 該方法通常用於對中文進行編碼 * @see 若系統不支援指定的編碼字符集,則直接將<code>chinese</code>原樣返回 */ public static String encode(String chinese, String charset){ chinese = (chinese==null ? "" : chinese); try { return URLEncoder.encode(chinese, charset); } catch (UnsupportedEncodingException e) { LogUtil.getLogger().error("編碼字串[" + chinese + "]時發生異常:系統不支援該字符集[" + charset + "]"); return chinese; } } /** * 字元解碼 * @see 該方法預設會以UTF-8解碼字串 * @see 若想自己指定字符集,可以使用<code>decode(String chinese, String charset)</code>方法 */ public static String decode(String chinese){ return decode(chinese, "UTF-8"); } /** * 字元解碼 * @see 該方法通常用於對中文進行解碼 * @see 若系統不支援指定的解碼字符集,則直接將<code>chinese</code>原樣返回 */ public static String decode(String chinese, String charset){ chinese = (chinese==null ? "" : chinese); try { return URLDecoder.decode(chinese, charset); } catch (UnsupportedEncodingException e) { LogUtil.getLogger().error("解碼字串[" + chinese + "]時發生異常:系統不支援該字符集[" + charset + "]"); return chinese; } } /** * 字串右補空格 * @see 該方法預設採用空格(其ASCII碼為32)來右補字元 * @see 若想自己指定所補字元,可以使用<code>rightPadForByte(String str, int size, int padStrByASCII)</code>方法 */ public static String rightPadForByte(String str, int size){ return rightPadForByte(str, size, 32); } /** * 字串右補字元 * @see 若str對應的byte[]長度不小於size,則按照size擷取str對應的byte[],而非原樣返回str * @see 所以size引數很關鍵..事實上之所以這麼處理,是由於支付處理系統介面文件規定了欄位的最大長度 * @see 若對普通字串進行右補字元,建議org.apache.commons.lang.StringUtils.rightPad(...) * @param size 該引數指的不是字串長度,而是字串所對應的byte[]長度 * @param padStrByASCII 該值為所補字元的ASCII碼,如32表示空格,48表示0,64表示@等 */ public static String rightPadForByte(String str, int size, int padStrByASCII){ byte[] srcByte = str.getBytes(); byte[] destByte = null; if(srcByte.length >= size){ //由於size不大於原陣列長度,故該方法此時會按照size自動擷取,它會在陣列右側填充'(byte)0'以使其具有指定的長度 destByte = Arrays.copyOf(srcByte, size); }else{ destByte = Arrays.copyOf(srcByte, size); Arrays.fill(destByte, srcByte.length, size, (byte)padStrByASCII); } return new String(destByte); } /** * 字串左補空格 * @see 該方法預設採用空格(其ASCII碼為32)來左補字元 * @see 若想自己指定所補字元,可以使用<code>leftPadForByte(String str, int size, int padStrByASCII)</code>方法 */ public static String leftPadForByte(String str, int size){ return leftPadForByte(str, size, 32); } /** * 字串左補字元 * @see 若str對應的byte[]長度不小於size,則按照size擷取str對應的byte[],而非原樣返回str * @see 所以size引數很關鍵..事實上之所以這麼處理,是由於支付處理系統介面文件規定了欄位的最大長度 * @param padStrByASCII 該值為所補字元的ASCII碼,如32表示空格,48表示0,64表示@等 */ public static String leftPadForByte(String str, int size, int padStrByASCII){ byte[] srcByte = str.getBytes(); byte[] destByte = new byte[size]; Arrays.fill(destByte, (byte)padStrByASCII); if(srcByte.length >= size){ System.arraycopy(srcByte, 0, destByte, 0, size); }else{ System.arraycopy(srcByte, 0, destByte, size-srcByte.length, srcByte.length); } return new String(destByte); } /** * 獲取前一天日期yyyyMMdd * @see 經測試,針對閏年02月份或跨年等情況,該程式碼仍有效。測試程式碼如下 * @see calendar.set(Calendar.YEAR, 2013); * @see calendar.set(Calendar.MONTH, 0); * @see calendar.set(Calendar.DATE, 1); * @see 測試時,將其放到<code>calendar.add(Calendar.DATE, -1);</code>前面即可 * @return 返回的日期格式為yyyyMMdd */ public static String getYestoday(){ Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE, -1); return new SimpleDateFormat("yyyyMMdd").format(calendar.getTime()); } /** * 獲取當前的日期yyyyMMdd */ public static String getCurrentDate(){ return new SimpleDateFormat("yyyyMMdd").format(new Date()); } /** * 獲取當前的時間yyyyMMddHHmmss */ public static String getCurrentTime(){ return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); } /** * HTML字元轉義 * @see 對輸入引數中的敏感字元進行過濾替換,防止使用者利用JavaScript等方式輸入惡意程式碼 * @see String input = <img src='http://t1.baidu.com/it/fm=0&gp=0.jpg'/> * @see HtmlUtils.htmlEscape(input); //from spring.jar * @see StringEscapeUtils.escapeHtml(input); //from commons-lang.jar * @see 儘管Spring和Apache都提供了字元轉義的方法,但Apache的StringEscapeUtils功能要更強大一些 * @see StringEscapeUtils提供了對HTML,Java,JavaScript,SQL,XML等字元的轉義和反轉義 * @see 但二者在轉義HTML字元時,都不會對單引號和空格進行轉義,而本方法則提供了對它們的轉義 * @return String 過濾後的字串 */ public static String htmlEscape(String input) { if(isEmpty(input)){ return input; } input = input.replaceAll("&", "&amp;"); input = input.replaceAll("<", "&lt;"); input = input.replaceAll(">", "&gt;"); input = input.replaceAll(" ", "&nbsp;"); input = input.replaceAll("'", "&#39;"); //IE暫不支援單引號的實體名稱,而支援單引號的實體編號,故單引號轉義成實體編號,其它字元轉義成實體名稱 input = input.replaceAll("\"", "&quot;"); //雙引號也需要轉義,所以加一個斜線對其進行轉義 input = input.replaceAll("\n", "<br/>"); //不能把\n的過濾放在前面,因為還要對<和>過濾,這樣就會導致<br/>失效了 return input; } }