|
|
@@ -0,0 +1,388 @@
|
|
|
+package common.utils.hyg;
|
|
|
+
|
|
|
+import com.jfinal.kit.StrKit; // 仍然保留,因为您有 JFinal 框架
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.*;
|
|
|
+import java.security.interfaces.RSAPrivateKey;
|
|
|
+import java.security.interfaces.RSAPublicKey;
|
|
|
+import java.security.spec.PKCS8EncodedKeySpec;
|
|
|
+import java.security.spec.X509EncodedKeySpec;
|
|
|
+import java.util.*; // 导入 Java 8+ 的标准 Base64 类
|
|
|
+
|
|
|
+public class RSAUtils {
|
|
|
+ // 密钥算法
|
|
|
+ public static final String KEY_ALGORITHM = "RSA";
|
|
|
+ // 签名算法
|
|
|
+ public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
|
|
|
+ // 加密/解密算法及填充方式, RSA默认使用PKCS1Padding
|
|
|
+ public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
|
|
|
+
|
|
|
+ // 密钥长度,这里使用1024位,也可以是2048等
|
|
|
+ private static final int KEY_SIZE = 1024;
|
|
|
+
|
|
|
+ // 存储公钥和私钥的Map键
|
|
|
+ private static final String PUBLIC_KEY = "RSAPublicKey";
|
|
|
+ private static final String PRIVATE_KEY = "RSAPrivateKey";
|
|
|
+
|
|
|
+ // RSA加密最大分段大小 (对于1024位RSA密钥,PKCS1Padding填充,最大加密长度为128-11=117字节)
|
|
|
+ private static final int MAX_ENCRYPT_BLOCK = KEY_SIZE / 8 - 11; // 1024/8 - 11 = 128 - 11 = 117
|
|
|
+ // RSA解密最大分段大小 (对于1024位RSA密钥,解密时最大分段大小为128字节)
|
|
|
+ private static final int MAX_DECRYPT_BLOCK = KEY_SIZE / 8; // 1024/8 = 128
|
|
|
+
|
|
|
+ // 私有构造器,防止实例化
|
|
|
+ private RSAUtils() {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成RSA密钥对
|
|
|
+ *
|
|
|
+ * @return 包含公钥和私钥的Map,公钥键为"RSAPublicKey",私钥键为"RSAPrivateKey"
|
|
|
+ * @throws Exception 如果生成密钥对失败
|
|
|
+ */
|
|
|
+ public static Map<String, Key> genKeyPair() throws Exception {
|
|
|
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
|
|
+ keyPairGen.initialize(KEY_SIZE); // 初始化密钥长度
|
|
|
+ KeyPair keyPair = keyPairGen.generateKeyPair();
|
|
|
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
|
|
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
|
|
+ Map<String, Key> keyMap = new HashMap<>(2);
|
|
|
+ keyMap.put(PUBLIC_KEY, publicKey);
|
|
|
+ keyMap.put(PRIVATE_KEY, privateKey);
|
|
|
+ return keyMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对数据进行数字签名
|
|
|
+ *
|
|
|
+ * @param data 待签名的数据字节数组
|
|
|
+ * @param privateKey Base64编码的私钥字符串
|
|
|
+ * @return Base64编码的签名字符串
|
|
|
+ * @throws Exception 如果签名过程中发生错误
|
|
|
+ */
|
|
|
+ public static String sign(byte[] data, String privateKey) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(privateKey); // 改用 java.util.Base64
|
|
|
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
|
|
+ Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
|
|
+ signature.initSign(privateK);
|
|
|
+ signature.update(data);
|
|
|
+ return Base64.getEncoder().encodeToString(signature.sign()); // 改用 java.util.Base64
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证数字签名
|
|
|
+ *
|
|
|
+ * @param data 原始数据字节数组
|
|
|
+ * @param publicKey Base64编码的公钥字符串
|
|
|
+ * @param sign Base64编码的签名字符串
|
|
|
+ * @return true表示签名有效,false表示签名无效
|
|
|
+ * @throws Exception 如果验证签名过程中发生错误
|
|
|
+ */
|
|
|
+ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(publicKey); // 改用 java.util.Base64
|
|
|
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ PublicKey publicK = keyFactory.generatePublic(keySpec);
|
|
|
+ Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
|
|
+ signature.initVerify(publicK);
|
|
|
+ signature.update(data);
|
|
|
+ return signature.verify(Base64.getDecoder().decode(sign)); // 改用 java.util.Base64
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 私钥解密
|
|
|
+ *
|
|
|
+ * @param encryptedData 待解密的密文字节数组
|
|
|
+ * @param privateKey Base64编码的私钥字符串
|
|
|
+ * @return 解密后的原始数据字节数组
|
|
|
+ * @throws Exception 如果解密过程中发生错误
|
|
|
+ */
|
|
|
+ public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(privateKey); // 改用 java.util.Base64
|
|
|
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
|
|
+
|
|
|
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 明确指定算法和填充方式
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, privateK); // 解密模式
|
|
|
+
|
|
|
+ int inputLen = encryptedData.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+ byte[] cache;
|
|
|
+ // 对数据分段解密
|
|
|
+ while (inputLen - offSet > 0) {
|
|
|
+ if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
|
|
|
+ } else {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ offSet += cache.length; // 实际解密字节数
|
|
|
+ }
|
|
|
+ byte[] decryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return decryptedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公钥解密(在实际应用中,通常是私钥加密,公钥解密;或公钥加密,私钥解密。公钥解密不常见,除非是验证私钥签名)
|
|
|
+ * 注意:RSA公钥解密通常是用来验证由私钥进行加密(签名)的数据,并非用于加密数据的机密性。
|
|
|
+ *
|
|
|
+ * @param encryptedData 待解密的密文字节数组
|
|
|
+ * @param publicKey Base64编码的公钥字符串
|
|
|
+ * @return 解密后的原始数据字节数组
|
|
|
+ * @throws Exception 如果解密过程中发生错误
|
|
|
+ */
|
|
|
+ public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(publicKey); // 改用 java.util.Base64
|
|
|
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ Key publicK = keyFactory.generatePublic(x509KeySpec);
|
|
|
+
|
|
|
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 明确指定算法和填充方式
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, publicK); // 解密模式
|
|
|
+
|
|
|
+ int inputLen = encryptedData.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+ byte[] cache;
|
|
|
+ // 对数据分段解密
|
|
|
+ while (inputLen - offSet > 0) {
|
|
|
+ if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
|
|
|
+ } else {
|
|
|
+ cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ offSet += cache.length; // 实际解密字节数
|
|
|
+ }
|
|
|
+ byte[] decryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return decryptedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 公钥加密
|
|
|
+ *
|
|
|
+ * @param data 待加密的原始数据字节数组
|
|
|
+ * @param publicKey Base64编码的公钥字符串
|
|
|
+ * @return 加密后的密文字节数组
|
|
|
+ * @throws Exception 如果加密过程中发生错误
|
|
|
+ */
|
|
|
+ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(publicKey); // 改用 java.util.Base64
|
|
|
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ Key publicK = keyFactory.generatePublic(x509KeySpec);
|
|
|
+
|
|
|
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 明确指定算法和填充方式
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, publicK); // 加密模式
|
|
|
+
|
|
|
+ int inputLen = data.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+ byte[] cache;
|
|
|
+ // 对数据分段加密
|
|
|
+ while (inputLen - offSet > 0) {
|
|
|
+ if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
|
|
|
+ // 如果剩余数据大于最大加密块,加密一个完整块
|
|
|
+ cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
|
|
|
+ } else {
|
|
|
+ // 加密剩余的所有数据
|
|
|
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ offSet += MAX_ENCRYPT_BLOCK; // 每次偏移一个加密块大小
|
|
|
+ }
|
|
|
+ byte[] encryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return encryptedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 私钥加密(在实际应用中,通常用于签名,而非加密数据机密性)
|
|
|
+ * 注意:RSA私钥加密通常用于生成数字签名,而不是为了数据机密性。
|
|
|
+ *
|
|
|
+ * @param data 待加密的原始数据字节数组
|
|
|
+ * @param privateKey Base64编码的私钥字符串
|
|
|
+ * @return 加密后的密文字节数组
|
|
|
+ * @throws Exception 如果加密过程中发生错误
|
|
|
+ */
|
|
|
+ public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
|
|
|
+ byte[] keyBytes = Base64.getDecoder().decode(privateKey); // 改用 java.util.Base64
|
|
|
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
|
|
+ KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
|
|
+ Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
|
|
+
|
|
|
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 明确指定算法和填充方式
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, privateK); // 加密模式
|
|
|
+
|
|
|
+ int inputLen = data.length;
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ int offSet = 0;
|
|
|
+ byte[] cache;
|
|
|
+ // 对数据分段加密
|
|
|
+ while (inputLen - offSet > 0) {
|
|
|
+ if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
|
|
|
+ cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
|
|
|
+ } else {
|
|
|
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
|
|
|
+ }
|
|
|
+ out.write(cache, 0, cache.length);
|
|
|
+ offSet += MAX_ENCRYPT_BLOCK; // 每次偏移一个加密块大小
|
|
|
+ }
|
|
|
+ byte[] encryptedData = out.toByteArray();
|
|
|
+ out.close();
|
|
|
+ return encryptedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从密钥Map中获取Base64编码的私钥字符串
|
|
|
+ *
|
|
|
+ * @param keyMap 包含私钥的Map
|
|
|
+ * @return Base64编码的私钥字符串
|
|
|
+ * @throws Exception 如果获取私钥失败
|
|
|
+ */
|
|
|
+ public static String getPrivateKey(Map<String, Key> keyMap) throws Exception {
|
|
|
+ Key key = keyMap.get(PRIVATE_KEY);
|
|
|
+ return Base64.getEncoder().encodeToString(key.getEncoded()); // 改用 java.util.Base64
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从密钥Map中获取Base64编码的公钥字符串
|
|
|
+ *
|
|
|
+ * @param keyMap 包含公钥的Map
|
|
|
+ * @return Base64编码的公钥字符串
|
|
|
+ * @throws Exception 如果获取公钥失败
|
|
|
+ */
|
|
|
+ public static String getPublicKey(Map<String, Key> keyMap) throws Exception {
|
|
|
+ Key key = keyMap.get(PUBLIC_KEY);
|
|
|
+ return Base64.getEncoder().encodeToString(key.getEncoded()); // 改用 java.util.Base64
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将参数名称按照ASCII排序并拼接成字符串
|
|
|
+ * 用于生成待签名字符串,通常用于接口请求参数签名校验
|
|
|
+ *
|
|
|
+ * @param paramMap 包含参数的Map
|
|
|
+ * @return 排序并拼接后的字符串
|
|
|
+ */
|
|
|
+ public static String sortParam(Map<String, Object> paramMap) {
|
|
|
+ if (paramMap == null || paramMap.isEmpty()) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ // 将Map的Entry转换为List,以便排序
|
|
|
+ List<Map.Entry<String, Object>> list = new ArrayList<>(paramMap.entrySet());
|
|
|
+
|
|
|
+ // 使用Comparator按照Key的ASCII值进行排序
|
|
|
+ Collections.sort(list, Comparator.comparing(Map.Entry::getKey));
|
|
|
+
|
|
|
+ // 拼接字符串
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (Map.Entry<String, Object> item : list) {
|
|
|
+ // 过滤空值,通常规则是空值不参与签名
|
|
|
+ // StrKit.isBlank() 会检查 null, "", " "
|
|
|
+ if (!StrKit.isBlank(item.getKey()) && item.getValue() != null) { // value不能是null
|
|
|
+ // 如果值是空字符串,根据业务需要决定是否拼接
|
|
|
+ // 这里按原逻辑,如果值是空字符串,也会拼接
|
|
|
+ sb.append(item.getKey()).append("=").append(item.getValue()).append("&");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 移除末尾多余的'&'
|
|
|
+ if (sb.length() > 0) {
|
|
|
+ return sb.substring(0, sb.length() - 1);
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ try {
|
|
|
+ // 1. 生成RSA密钥对
|
|
|
+ Map<String, Key> keyMap = genKeyPair();
|
|
|
+ String publicKey = getPublicKey(keyMap);
|
|
|
+ String privateKey = getPrivateKey(keyMap);
|
|
|
+
|
|
|
+ System.out.println("--- RSA 密钥对生成 ---");
|
|
|
+ System.out.println("公钥 (Base64编码):\n" + publicKey);
|
|
|
+ System.out.println("私钥 (Base64编码):\n" + privateKey);
|
|
|
+ System.out.println("----------------------\n");
|
|
|
+
|
|
|
+ String originalContent = "这是一段需要使用RSA加密和签名的中文文本,包含较长的内容,以测试分段处理。Hello World! 1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
+ System.out.println("原始数据: " + originalContent);
|
|
|
+ byte[] dataBytes = originalContent.getBytes(StandardCharsets.UTF_8);
|
|
|
+
|
|
|
+ // 2. 公钥加密,私钥解密 (实现数据机密性)
|
|
|
+ System.out.println("--- 公钥加密,私钥解密 ---");
|
|
|
+ byte[] encryptedByPublicKey = encryptByPublicKey(dataBytes, publicKey);
|
|
|
+ System.out.println("公钥加密后的数据 (Base64编码): " + Base64.getEncoder().encodeToString(encryptedByPublicKey));
|
|
|
+
|
|
|
+ byte[] decryptedByPrivateKey = decryptByPrivateKey(encryptedByPublicKey, privateKey);
|
|
|
+ String decryptedString = new String(decryptedByPrivateKey, StandardCharsets.UTF_8);
|
|
|
+ System.out.println("私钥解密后的数据: " + decryptedString);
|
|
|
+ System.out.println("验证一致性: " + originalContent.equals(decryptedString));
|
|
|
+ System.out.println("----------------------\n");
|
|
|
+
|
|
|
+ // 3. 私钥加密,公钥解密 (实现数字签名,通常不用于传输数据)
|
|
|
+ System.out.println("--- 私钥加密,公钥解密 (常用于签名验证) ---");
|
|
|
+ byte[] encryptedByPrivateKey = encryptByPrivateKey(dataBytes, privateKey);
|
|
|
+ System.out.println("私钥加密后的数据 (Base64编码): " + Base64.getEncoder().encodeToString(encryptedByPrivateKey));
|
|
|
+
|
|
|
+ byte[] decryptedByPublicKey = decryptByPublicKey(encryptedByPrivateKey, publicKey);
|
|
|
+ String decryptedString2 = new String(decryptedByPublicKey, StandardCharsets.UTF_8);
|
|
|
+ System.out.println("公钥解密后的数据: " + decryptedString2);
|
|
|
+ System.out.println("验证一致性: " + originalContent.equals(decryptedString2));
|
|
|
+ System.out.println("----------------------\n");
|
|
|
+
|
|
|
+ // 4. 数字签名与验证 (主流用法)
|
|
|
+ System.out.println("--- 数字签名与验证 ---");
|
|
|
+ String sign = sign(dataBytes, privateKey);
|
|
|
+ System.out.println("生成的签名 (Base64编码): " + sign);
|
|
|
+
|
|
|
+ // 验证正确的签名
|
|
|
+ boolean verified = verify(dataBytes, publicKey, sign);
|
|
|
+ System.out.println("验证签名结果 (正确数据与签名): " + verified);
|
|
|
+
|
|
|
+ // 验证篡改后的数据
|
|
|
+ byte[] tamperedData = "这是被篡改的数据".getBytes(StandardCharsets.UTF_8);
|
|
|
+ boolean tamperedVerified = verify(tamperedData, publicKey, sign);
|
|
|
+ System.out.println("验证签名结果 (篡改数据与原签名): " + tamperedVerified);
|
|
|
+
|
|
|
+ // 验证错误的签名
|
|
|
+ String wrongSign = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
|
|
+ try {
|
|
|
+ boolean wrongSignVerified = verify(dataBytes, publicKey, wrongSign);
|
|
|
+ System.out.println("验证签名结果 (正确数据与错误签名): " + wrongSignVerified);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ // java.util.Base64.getDecoder().decode() 在遇到非法Base64字符串时会抛出 IllegalArgumentException
|
|
|
+ System.out.println("验证签名结果 (正确数据与错误签名): Base64解码错误,签名非法。");
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("----------------------\n");
|
|
|
+
|
|
|
+ // 5. sortParam 方法测试
|
|
|
+ System.out.println("--- sortParam 方法测试 ---");
|
|
|
+ Map<String, Object> params = new TreeMap<>(); // 使用TreeMap方便观察排序
|
|
|
+ params.put("appKey", "123");
|
|
|
+ params.put("timestamp", System.currentTimeMillis());
|
|
|
+ params.put("paramA", "valueA");
|
|
|
+ params.put("paramC", "valueC");
|
|
|
+ params.put("paramB", null); // 测试null值
|
|
|
+ params.put("paramD", ""); // 测试空字符串
|
|
|
+
|
|
|
+ String sortedAndConcatenated = sortParam(params);
|
|
|
+ System.out.println("原始参数Map: " + params);
|
|
|
+ System.out.println("排序拼接结果: " + sortedAndConcatenated);
|
|
|
+
|
|
|
+ System.out.println("----------------------\n");
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("发生了一个错误: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|