当前位置:

首页 > 编程开发 > Java怎么提供给第三方使用接口

Java怎么提供给第三方使用接口

接口Controller在写接口前一定要签名做签名校验,我的签名方式做了特殊处理,因为接口是对外开放的,这个是为了避免恶意调用接口做的处理,叫做签名的混淆值,这个签名混淆值的作用是就算别人知道了接口,并且知道签名方式也不能被攻击,是为了避免被恶意篡改数据,签名混淆值就是一组特定加密后的数据。@PostMapping("refundDeductionPoints")publicResultrefundDeductionPoints(@RequestParamMapparams){Resultresult=n

本文目录

    接口Controller

    在写接口前一定要签名做签名校验,我的签名方式做了特殊处理,因为接口是对外开放的,这个是为了避免恶意调用接口做的处理,叫做签名的混淆值,这个签名混淆值的作用是就算别人知道了接口,并且知道签名方式也不能被攻击,是为了避免被恶意篡改数据,签名混淆值就是一组特定加密后的数据。

    	@PostMapping("refundDeductionPoints")
    	public Result refundDeductionPoints (@RequestParam Map params){
    		Result result = new Result();
    		try {
    			//签名校验
    			String msgDigest = params.get("msgDigest");//签名
    			String msgData = params.get("msgData");
    			String timeStamp = params.get("timeStamp");
    			String secret = params.get("secret");//	秘钥
    			String sign = SignUtil.sign(msgData+"wf8la1tw7p9o2xz",timeStamp);//wf8la1tw7p9o2xz为签名混淆值
    			if (!msgDigest.equals(sign)) {
    				return result.setCode(1006).setReason("数字签名无效");
    			}
    			if (Common.isEmpty(secret)) {//先签名后幂等校验
    				return result.setCode(1001).setReason("密钥不能为空");
    			}
    			/**
    			 * 幂等校验
    			 * 1.同一个用户操作同一个退货单一分钟内操作该单据视为重复操作(此秘钥已通过特殊处理)
    			 */
    			String value = redistempalte.opsForValue().get(secret);
    			if (Common.isNotEmpty(value)) {	
    				logger.error("重复请求 secret={}",value);
    				return result.setCode(1007).setReason("重复请求");	
    			}
    			redistempalte.opsForValue().set(secret, "1",60,TimeUnit.SECONDS);//设置缓存一分钟
    			return service.refundDeductionPoints(params);
    		} catch (Exception e) {
    			logger.error("添加积分流水异常", e);
    			return result.setCode(ErrorCodes.BUSINESS_ERROR).setReason("生成积分流水失败");
    		}
    	}

    接口幂等性校验

    此接口做幂等性校验,幂等性校验常见解决方案有很多,可以自行根据实际情况选择,

    说到幂等首先要先了解什么是幂等

    概念:

    幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。

    在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。

    这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.

    幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次,比如:

    订单接口, 不能多次创建订单

    支付接口, 重复支付同一笔订单只能扣一次钱

    支付宝回调接口, 可能会多次回调, 必须处理重复回调

    普通表单提交接口, 因为网络超时等原因多次点击提交, 只能成功一次

    等等

    解决方案常见的几种方式

    唯一索引 – 防止新增脏数据

    token机制 – 防止页面重复提交

    悲观锁 – 获取数据的时候加锁(锁表或锁行)

    乐观锁 – 基于版本号version实现, 在更新数据那一刻校验数据

    分布式锁 – redis(jedis、redisson)或zookeeper实现

    状态机 – 状态变更, 更新数据时判断状态

    如果有小伙伴不理解什么是幂等可以看看官方是解释

    实现类ServiceImpl

    	@Transactional
    	@Override
    	public Result refundDeductionPoints(Map params) {
    		String msgData = params.get("msgData");
    		ParamIntegral entity = new Gson().fromJson(msgData, ParamIntegral.class);
    		if (Common.isNull(entity)) {
    			return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("请求参数不能为空");
    		}
    		if (Common.isEmpty(entity.getBitems())) {
    			return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("请求参数不能为空");
    		}
    		int row = 0;
    		for (ParamIntegral bitem : entity.getBitems()) {
    			if (Common.isEmpty(bitem.getDdh())) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("订单号为必传参数");
    			}
    			if (null == bitem.getJfz()) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("扣减积分不能为空");
    			}
    			List orderInfo = mallOrderInfoMapper.selectByDdh(bitem.getDdh());
    			if (orderInfo == null) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR)
    						.setReason("订单号为" + bitem.getDdh() + "没有此订单请联系客服核对信息。");
    			}
    			if (orderInfo != null && orderInfo.size() > 1) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR)
    						.setReason("订单号为" + bitem.getDdh() + "有多个相同订单请联系客服核对信息。");
    			}
    			if (!"E".equals(orderInfo.get(0).getDdzt())) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR)
    						.setReason("订单号为" + bitem.getDdh() + "未确认收货还没产生积分不允许退货。");
    			}
    			SysIntegral integral = Common.first(integralMapper.selectByMdbm(orderInfo.get(0).getMdbm()));
    			if (integral == null) {
    				return new Result().setCode(ErrorCodes.INNER_ERROR)
    						.setReason("门店编码为" + orderInfo.get(0).getMdbm() + "积分汇总没有找到此门店,请联系客服核实");
    			}
    			BigDecimal kyjf = BigDecimal.ZERO;
    			if (entity.getReturnGoods() == true) {
    				// 可用积分小于扣减积分不够扣ERP使用前抵扣
    				if (bitem.getJfz().compareTo(integral.getKyjf()) == 1) {
    					kyjf = BigDecimal.ZERO;					
    				} else {
    					// 可用积分 = 当前可用积分-扣减积分
    					kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz());
    				}
    			} else {
    				// 可用积分 = 当前可用积分+退还积分
    				kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz());				
    			}		
    			// 更新积分汇总
    			SysIntegral dataMap = new SysIntegral();
    			dataMap.setIntegralId(integral.getIntegralId());
    			dataMap.setMdbm(integral.getMdbm());
    			dataMap.setKyjf(kyjf);
    			dataMap.setUpdateTime(new Date());
    			dataMap.setUpdateUser(entity.getUserName());
    			dataMap.setUpdateUserid(entity.getUserId().intValue());		
    			row = integralMapper.updateByPrimaryKeySelective(dataMap);
    			if (row == 0) {
    				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    				return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("更新积分失败");
    			}
    			//推送到ERP门店信息
    			 BdMdxxH mdxx =new BdMdxxH();
    			 mdxx.setMdbm(integral.getMdbm());
    			 mdxx.setMdjf(kyjf);
    			com.lkfs.cw.common.Result bdMdxxh = dataBaseServiceApi.updateStorePoints(mdxx);
    			if (!bdMdxxh.isComplete()) {
    				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    				return new Result().setCode(bdMdxxh.getCode()).setReason(bdMdxxh.getReason());
    			}		
    			SysIntegralStatement statement = new SysIntegralStatement();
    			if (entity.getReturnGoods() == true) {
    				statement.setJfz(bitem.getJfz().negate());// 消费的积分值
    				if (bitem.getJfz().compareTo(integral.getKyjf()) == 1) {// 可用积分小于扣减积分不够扣ERP使用前抵扣
    					statement.setTzhjfz(BigDecimal.ZERO);// 调整后积分值
    				} else {
    					statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz()));// 调整后积分值
    				}
    				statement.setJfxflx("E");// 积分支出
    				statement.setXxsm("退货扣减积分(订单号为:" + bitem.getDdh() + "," + "退货单号为:" + entity.getDjh() + ")" + "已扣除:"
    						+ bitem.getJfz().negate() + ":积分");
    			} else {// 取消退货
    				statement.setJfxflx("I");// 积分收入
    				statement.setJfz(bitem.getJfz());// 取消退货把积分赠送回来
    				statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz()));// 调整后积分值
    				statement.setXxsm("取消退货(订单号为:" + bitem.getDdh() + "," + "退货单号为:" + entity.getDjh() + ")" + "已退还:"
    						+ bitem.getJfz() + ":积分");
    			}
    			statement.setIntegralId(integral.getIntegralId());// 该门店积分编码
    			statement.setTzqjfz(integral.getKyjf());// 调整前积分值
    			statement.setDdh(entity.getDdh());
    			statement.setCreateTime(new Date());// 流水生成时间
    			statement.setCreateUser(entity.getUserName());
    			statement.setCreateUserid(entity.getUserId().intValue());
    			statement.setJftz("T");// 积分扣减为T
    			statement.setZt("Y");// 状态 Y:有效
    			row = mapper.insert(statement);
    			if (row == 0) {
    				TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    				return new Result().setCode(ErrorCodes.INNER_ERROR).setReason("插入积分流水失败");
    			}
    		}
    		return new Result().setCode(ErrorCodes.SUCCESS).setReason("操作成功");
    	}

    第三方调用接口Api实现类

    模拟第三方合作方调用接口

    //此方式以上已写了封装信息就不一一展示了,可以根据实际情况自行操作
    	private void pushIntegral(Long djlsh) {
    		FiSjysjsH fiSjysjsh = mapper.selectByPrimaryKey(djlsh);				
    		//订单退货调用某某退货扣减积分接口
    		List sjysjsbList = bmapper.selectByKhddh(djlsh);
    		if (sjysjsbList != null && sjysjsbList.size() > 0) {
    			List list = new ArrayList();
    			for (FiSjysjsB bitem : sjysjsbList) {
    				ParamIntegral temp = new ParamIntegral();
    				temp.setDdh(bitem.getKhddh());
    				temp.setJfz(bitem.getJfz());			
    				list.add(temp);
    			}
    			ParamIntegral param = new ParamIntegral();
    			param.setBitems(list);
    			param.setDjh(fiSjysjsh.getDjh());
    			param.setUserId(AppRealm.getCurrentUser().getUserId());
    			param.setUserName(AppRealm.getCurrentUser().getUserName());
    			if (new Short("1").equals(fiSjysjsh.getLocked())) {
    				param.setReturnGoods(true);
    			}else {
    				param.setReturnGoods(false);
    			}
    			String msgData = new Gson().toJson(param).toString();
    			Map params = new HashMap();
    			String timeStamp = String.valueOf(System.currentTimeMillis());//时间戳
    			params.put("timeStamp", timeStamp);
    			params.put("msgData", msgData);	
    			params.put("msgDigest", SignUtil.sign(msgData+"wf8la1tw7p9o2xz", timeStamp));//生成签名第二个值暂定(wf8la1tw7p9o2xz签名混淆值)
    			params.put("secret",IDEMPOTENT_SECRET_PREFIX + fiSjysjsh.getDjh() + AppRealm.getCurrentUser().getUserId()+param.getReturnGoods() );//自定义密钥 做幂等校验
    			String result = HttpCilent.post(B2B_URL, params); //发送http post请求
    			B2bIntegralResponse res = new Gson().fromJson(result, B2bIntegralResponse.class);
    			if (null == res) {
    				throw new RuntimeException("调用积分接口系统异常");
    			}
    			if (res.getCode() != 0) {//接口返回失败异常代码提示
    				throw new RuntimeException("调用积分接口发生异常,异常代码为:"+res.getCode()+"异常信息为:"+res.getReason());	
    			}
    		}
    	}

    生成签名工具类

    package com.cy.xgsm.util;
    
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.google.gson.JsonElement;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;
    
    /**
     * 
     * @author Dylan
     *
     */
    public class SignUtil {
    	
    private static final Logger log = LoggerFactory.getLogger(SignUtil.class);
    	
    	/**
    	 * 
    	 */
    	public static String sign(String str, String secret) {
    		StringBuilder enValue = new StringBuilder();
    		enValue.append(secret);
    		enValue.append(str);
    		enValue.append(secret);
    		return encryptByMD5(enValue.toString());
    	}
    
    	private static String encryptByMD5(String data) {
    		String re_md5 = new String();
    		try {
    			MessageDigest md = MessageDigest.getInstance("MD5");
    			md.update(data.getBytes());
    			byte b[] = md.digest();
    			int i;
    			StringBuffer buf = new StringBuffer();
    			for (int offset = 0; offset < b.length; offset++) {
    				i = b[offset];
    				if (i < 0)
    					i += 256;
    				if (i < 16)
    					buf.append("0");
    				buf.append(Integer.toHexString(i));
    			}
    			re_md5 = buf.toString();
    		} catch (NoSuchAlgorithmException e) {
    			e.printStackTrace();
    		}
    		return re_md5.toUpperCase();
    	}
    
    	public static String compare(String jsonStr,String secret ){
    
    		JsonParser jsonParser = new JsonParser();
    		JsonObject jsonObject = jsonParser.parse(jsonStr).getAsJsonObject();
    		String sign1 = "null";
    		JsonElement signElement = jsonObject.remove("sign");
    		if( signElement != null ){
    			sign1 = signElement.getAsString();
    		}
    		log.info("sign1: " + sign1);
    		StringBuilder enValue = new StringBuilder();
    		enValue.append(secret);
    		enValue.append(jsonObject.toString());
    		enValue.append(secret);
    		String sign2 = encryptByMD5(enValue.toString());
    		jsonObject.addProperty("sign", sign2);
    		return jsonObject.toString();
    	}
    }

    HttpCilent工具类

    这个工具类在我之前的文章也有但是没有把这个方式的放上去,如果有需要用到可直接把一下代码复制到这个Http工具类(见文末) 最后即可直接使用。

       /**
         * 发送post请求
         * @param url 目的url
         * @param parameters 参数
         * @return
         */
        public static String post(String url, Map parameters) {
        	String result = "";// 返回的结果
        	BufferedReader in = null;// 读取响应输入流
        	PrintWriter out = null;
        	StringBuffer sb = new StringBuffer();// 处理请求参数
        	String params = "";// 编码之后的参数
        	try {
        	// 编码请求参数
        	if (parameters.size() == 1) {
        	for (String name : parameters.keySet()) {
        	sb.append(name)
        	.append("=")
        	.append(java.net.URLEncoder.encode(
        	parameters.get(name), "UTF-8"));
        	}
        	params = sb.toString();
        	} else {
        	for (String name : parameters.keySet()) {
        	sb.append(name)
        	.append("=")
        	.append(java.net.URLEncoder.encode(
        	parameters.get(name), "UTF-8")).append("&");
        	}
        	String temp_params = sb.toString();
        	params = temp_params.substring(0, temp_params.length() - 1);
        	}
        	// 创建URL对象
        	java.net.URL connURL = new java.net.URL(url);
        	// 打开URL连接
        	java.net.HttpURLConnection httpConn = (java.net.HttpURLConnection) connURL
        	.openConnection();
        	// 设置通用属性
        	httpConn.setRequestProperty("Accept", "*/*");
        	httpConn.setRequestProperty("Connection", "Keep-Alive");
        	httpConn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
        	httpConn.setRequestProperty("User-Agent",
        	"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
        	// 设置POST方式
        	httpConn.setDoInput(true);
        	httpConn.setDoOutput(true);
        	// 获取HttpURLConnection对象对应的输出流
        	out = new PrintWriter(httpConn.getOutputStream());
        	// 发送请求参数
        	out.write(params);
        	// flush输出流的缓冲
        	out.flush();
        	// 定义BufferedReader输入流来读取URL的响应,设置编码方式
        	in = new BufferedReader(new InputStreamReader(
        	httpConn.getInputStream(), "UTF-8"));
        	String line;
        	// 读取返回的内容
        	while ((line = in.readLine()) != null) {
        	result += line;
        	}
        	} catch (Exception e) {
        	e.printStackTrace();
    
        	} finally {
        	try {
        	if (out != null) {
        	out.close();
        	}
        	if (in != null) {
        	in.close();
        	}
        	} catch (IOException ex) {
        	ex.printStackTrace();
        	}
        	}
        	return result;
        	}

    附:分享一个获取IP工具类

    在日常开发中,经常会遇到需要记录IP的需求,接下来分享一个获取IP工具类,希望对小伙伴有帮助

    package com.cy.xgsm.util;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 获取IP工具类
     * @author Dylan
     *
     */
    public class IpUtils {
    	
    	private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
    
    	/**
    	 * 获取IP地址
    	 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
    	 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
    	 */
        public static String getIpAddr(HttpServletRequest request)
        {
            if (request == null)
            {
                return "unknown";
            }
            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            {
                ip = request.getHeader("X-Forwarded-For");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            {
                ip = request.getHeader("X-Real-IP");
            }
    
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
            {
                ip = request.getRemoteAddr();
            }
    
            return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
        }
    
        public static boolean internalIp(String ip)
        {
            byte[] addr = textToNumericFormatV4(ip);
            return internalIp(addr) || "127.0.0.1".equals(ip);
        }
    
        private static boolean internalIp(byte[] addr)
        {
            if (StringUtils.isNull(addr) || addr.length < 2)
            {
                return true;
            }
            final byte b0 = addr[0];
            final byte b1 = addr[1];
            final byte section1 = 0x0A;
            final byte section2 = (byte) 0xAC;
            final byte section3 = (byte) 0x10;
            final byte section4 = (byte) 0x1F;
            final byte section5 = (byte) 0xC0;
            final byte section6 = (byte) 0xA8;
            switch (b0)
            {
                case section1:
                    return true;
                case section2:
                    if (b1 >= section3 && b1 <= section4)
                    {
                        return true;
                    }
                case section5:
                    switch (b1)
                    {
                        case section6:
                            return true;
                        default:
                            return false;    
                    }
                default:
                    return false;
            }
        }
    
        /**
         * 将IPv4地址转换成字节
         * 
         * @param text IPv4地址
         * @return byte 字节
         */
        public static byte[] textToNumericFormatV4(String text)
        {
            if (text.length() == 0)
            {
                return null;
            }
    
            byte[] bytes = new byte[4];
            String[] elements = text.split("\\.", -1);
            try
            {
                long l;
                int i;
                switch (elements.length)
                {
                    case 1:
                        l = Long.parseLong(elements[0]);
                        if ((l < 0L) || (l > 4294967295L)){
                            return null;
                        }
                        bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                        bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                        bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                        bytes[3] = (byte) (int) (l & 0xFF);
                        break;
                    case 2:
                        l = Integer.parseInt(elements[0]);
                        if ((l < 0L) || (l > 255L)){
                            return null;
                        }
                        bytes[0] = (byte) (int) (l & 0xFF);
                        l = Integer.parseInt(elements[1]);
                        if ((l < 0L) || (l > 16777215L)){
                            return null;
                        }
                        bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                        bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                        bytes[3] = (byte) (int) (l & 0xFF);
                        break;
                    case 3:
                        for (i = 0; i < 2; ++i)
                        {
                            l = Integer.parseInt(elements[i]);
                            if ((l < 0L) || (l > 255L)){
                                return null;
                            }
                            bytes[i] = (byte) (int) (l & 0xFF);
                        }
                        l = Integer.parseInt(elements[2]);
                        if ((l < 0L) || (l > 65535L)){
                            return null;
                        }
                        bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                        bytes[3] = (byte) (int) (l & 0xFF);
                        break;
                    case 4:
                        for (i = 0; i < 4; ++i)
                        {
                            l = Integer.parseInt(elements[i]);
                            if ((l < 0L) || (l > 255L)){
                                return null;
                            }
                            bytes[i] = (byte) (int) (l & 0xFF);
                        }
                        break;
                    default:
                        return null;
                }
            }
            catch (NumberFormatException e)
            {
                return null;
            }
            return bytes;
        }
    
        public static String getHostIp()
        {
            try
            {
                return InetAddress.getLocalHost().getHostAddress();
            }
            catch (UnknownHostException e)
            {
            }
            return "127.0.0.1";
        }
    
        public static String getHostName()
        {
            try
            {
                return InetAddress.getLocalHost().getHostName();
            }
            catch (UnknownHostException e)
            {
            }
            return "未知";
        }
    }
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。