java编码陷阱

java类型转换

下面代码如果不加L,将导致int类型超范围,不会自动升级为Long,从而导致结果与实际不符。

    public static boolean isValidity(String faceResultTime, int internalHour) {
        boolean valid = false;
        if (StringUtils.isNotEmpty(faceResultTime)) {
            valid = (System.currentTimeMillis() - DateUtils.parseDateTime(faceResultTime).getTime()) > internalHour * 3600 * 1000L ? false : true;
        }
        return valid;
    }

请求头过大

在这里插入图片描述
解决方式

#端口配置
server:
  port: 8090
  tomcat:
    # 配置tomct的基本目录(用于存放临时文件等数据)
    basedir: /home/credit/xx/runtime
  #配置http传输支持压缩  
  compression:
    enabled: true
    mime-types: application/json,application/xml,text/html,text/xml,text/plai
  max-http-header-size: 10000000

公司固话

    /**
     * 公司固定电话
     * 0-9的数字或-,且11位或12位
     */
    @NotBlank(message = "公司固定电话不能为空")
    @Pattern(regexp = "(0\\d{2,3}-[1-9]\\d{6,7})|(^[1][3,4,5,7,8][0-9]{9}$)", message = "公司固定电话不满足电话格式")
    private String companyLandline;

关于方法是抛出异常还是包装后返回

在京东时,针对已知的异常,比如参数校验错误、用户操作错误、登录密码错误。这些通过校验可以知道的异常,是通过在service封装返回的。如果是不知道的异常,则会报错,只要报错,就会通过切面的方式将错误信息推送给我们开发人员手机上。所以,报错就是我们未知的错误或者是代码的BUG。这也是alibaba编码规范中的要求。

但是在美的,很多人习惯用throw new Exception()的方式进行抛出异常,只要不满足要求,就会抛出来。

基本类型包装类比较

包装类对象不可使用“==”符做比较运算,如果要进行比较运算时,最好使用java类库中的compareTo方法。
注意不要用不同类型的数值比较。
如下:
    String a = new String("1");
    Integer b = new Integer(1);
    System.out.println(a.equals(b));

jdk8中groupingBy的用法

/**
     * 
     *
     * @param clientId
     * @param applyNo
     * @return
     */
    private VoucherInfo2Vo getVoucherInfo(String clientId, String applyNo) {
        VoucherInfo2Vo voucherInfoVo = new VoucherInfo2Vo();
        XdImageFile voucherImage = ymImageStoreService.getApplyFileByType(clientId, applyNo, ImageFileType.IMG_CONSUMER_VOUCHERS.getCode());
        List<XdImageFile> otherMaterialListFile = getApplyFileListByType(clientId, applyNo, TCLDict.FILE_STYLE_OTHER_MATERIAL);

        if (voucherImage != null) {
            ConsumeProofVo consumeProofVo = new ConsumeProofVo();
            consumeProofVo.setFileId(voucherImage.getId());
            consumeProofVo.setFileType(voucherImage.getFileType());
            consumeProofVo.setImageThumbUrl(ymImageStoreService.getImgUrl(voucherImage.getThumbBatchNo(), voucherImage.getThumbFileName()));
            consumeProofVo.setImageUrl(ymImageStoreService.getImgUrl(voucherImage.getOriginBatchNo(), voucherImage.getOriginFileName()));
            voucherInfoVo.setConsumeProof(consumeProofVo);
        }

        if (CollectionUtils.isNotEmpty(otherMaterialListFile)) {
            Map<String, List<XdImageFile>> map = otherMaterialListFile.stream().collect(
                    Collectors.groupingBy(e -> e.getFileType()));

            List<OtherProofVo> otherProofVoList = new ArrayList<>();

            for (Map.Entry<String, List<XdImageFile>> entry : map.entrySet()) {
                OtherProofVo otherProofVo = new OtherProofVo();
                otherProofVo.setProofType(entry.getKey());
                otherProofVo.setProofTypeDesc(ImageFileType.parse(entry.getKey()).getDesc());

                List<ConsumeProofVo> consumeProofVoList = new ArrayList<>();
                List<XdImageFile> xdImageFileList = entry.getValue();
                for (XdImageFile f : xdImageFileList) {
                    ConsumeProofVo consumeProofVo = new ConsumeProofVo();
                    consumeProofVo.setFileId(f.getId());
                    consumeProofVo.setFileType(f.getFileType());
                    consumeProofVo.setImageThumbUrl(ymImageStoreService.getImgUrl(f.getThumbBatchNo(), f.getThumbFileName()));
                    consumeProofVo.setImageUrl(ymImageStoreService.getImgUrl(f.getOriginBatchNo(), f.getOriginFileName()));
                    consumeProofVoList.add(consumeProofVo);
                }
                otherProofVo.setProofFileList(consumeProofVoList);
                otherProofVoList.add(otherProofVo);
            }
            voucherInfoVo.setOtherProof(otherProofVoList);

        }
        return voucherInfoVo;
    }

null的输出

        String ss = null;
        System.out.println(ss);//null
        System.out.println(ss + "") ;//null
        System.out.println(ss + " ") ;//null  (前面有空格)

关于java包装类型比较

低于128的时候,IntegerCache会缓存。

        Integer a = null;
        System.out.println(a == 512);//NullPointerException
        Integer b = new Integer(1024);
        Integer c = new Integer(1024);
        System.out.println(b == c);//false
        System.out.println(b.equals(c));//true

为什么equals可以,请看Integer类equals源码。

Integer.java
public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

身份证尾号为x

#face++人脸时,忽略身份证大小写对比
xdBasecust.getCertNo().equalsIgnoreCase(idCardORCResult.getString("id_card_number")

联系人姓名不能含有表情符及个人邮箱

 /**
         * 联系人姓名
         */
        @NotBlank(message = "联系人姓名不能为空")
        @Size(max = 20)
        @Pattern(regexp = "(?!([\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]))", message = "居住地址中不能含有表情符")
        private String name;
        /**
         * 联系人手机号
         */
        @NotBlank(message = "联系人手机号不能为空")
        @Pattern(regexp = "^1[0-9]{10}$", message = "联系人手机号不满足手机格式")
        private String mobileNo;
            /**
     * 公司固定电话
     * 0-9的数字或-,且11位或12位
     */
    @NotBlank(message = "公司固定电话不能为空")
    @Pattern(regexp = "(^0([0-9]|[-]){6,13}$)|(^[1][3,4,5,7,8,9][0-9]{9}$)", message = "请输入正确的公司电话")
    private String companyLandline;
    /**
     * 个人邮箱
     */
    @NotBlank(message = "个人邮箱不能为空")
    @Pattern(regexp = "^[A-Za-z0-9(\\.)+\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", message = "个人邮箱不满足邮箱格式")
    private String personalMailbox;

在大多数JVM中,Object.hashCode()与内存位置无关

对于OpenJDK和HotSpot JVM,hashCode()是按需生成的,并存储在对象的标头中。 使用Unsafe,您可以查看是否已设置hashCode。

重写equals方法的时候为什么需要重写hashcode

equals()方法和hashcode()方法是Object类的两个方法。从下面的代码中可以看到equals方法比较的是内存地址是否相等,因此一般情况下也无法满足两个对象值的比较。

  1. 如果要满足两个对象的值是否相等,需要重写equals方法。
  2. 如果在使用HashMap等容器类时,用对象当做key进行比较时,如下代码,此时,需要重写hashCode是为了让同一个Class对象的两个具有相同值的对象的Hash值相等。
  3. 同时重写hashCode()与equals()是为了满足HashSet、HashMap等此类集合的相同对象的不重复存储。
//Object.java
public native int hashCode();
 
public boolean equals(Object obj) {
        return (this == obj);
    }

//Test.java
        Student s1 = new Student(1,"June");
        Student s2 = new Student(1,"June");
 
        Map<Object,String> testMap = new HashMap<Object,String>();
        testMap.put(s1, "美国学生");
        System.out.println(s1.equals(s2));//对象比较

那该如何重写该对象呢?如下仅仅是个示例。大家要根据自己的对象进行改写。


public class Student {
    private int age;
    private String name;


@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(age, name);
    }
}

double的使用

double a = 0.0d;
        double b = 0d;
        if(a == b) {//这两者是相等的
            System.out.println("test");
        }

java代码不能实现对jar包中的配置文件改写

原本打算读取IO流后写入流的方式进行重写jar包中的配置文件。但是这种方式在jar包中配置文件根本就不支持写。

@PostConstruct
    public void initEAP() throws Exception {
        org.springframework.core.io.Resource resource = new ClassPathResource("/conf/eap-ssapi.conf");
        InputStream is = resource.getInputStream();
        Properties configs = new Properties();
        configs.load(is);
        Config.getInstance().load("/conf/eap-ssapi.conf");
        Config.getInstance().get("test");
        configs.setProperty("loan", loanAddr);
        String key = "loan";
        MyProperties myProps = new MyProperties(configs);
        FileOutputStream out = null;
        FileLock lock = null;
        is.close();
        try {
            out = new FileOutputStream(resource.getFile());
            FileChannel channel = out.getChannel();
            lock = channel.tryLock();
            if (lock == null) {
                throw new Exception("file is being edited: " + resource.getFile());
            }

            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            Date date = new Date();
            myProps.store(out, "Last edit time: " + format.format(date));
        } finally {
            try {
                out.close();
            } catch (Exception var28) {
            }

            try {
                lock.release();
            } catch (Exception var27) {
            }

        }
        Util.storeProperties2(resource.getURI().getPath(), configs, "loan");
    }

mybatis标签selectkey无法返回主键值

在这里插入图片描述

too many connections 解决方法

  1. show processlist;
    查看连接数,可以发现有很多连接处于sleep状态,这些其实是暂时没有用的,所以可以kill掉

  2. show variables like “max_connections”;
    查看最大连接数,应该是与上面查询到的连接数相同,才会出现too many connections的情况

  3. set GLOBAL max_connections=1000;
    修改最大连接数,但是这不是一劳永逸的方法,应该要让它自动杀死那些sleep的进程。

  4. show global variables like ‘wait_timeout’;
    这个数值指的是mysql在关闭一个非交互的连接之前要等待的秒数,默认是28800s

  5. set global wait_timeout=300;
    修改这个数值,这里可以随意,最好控制在几分钟内

  6. set global interactive_timeout=500;
    修改这个数值,表示mysql在关闭一个连接之前要等待的秒数,至此可以让mysql自动关闭那些没用的连接,但要注意的是,正在使用的连接到了时间也会被关闭,因此这个时间值要合适

  7. 批量kill之前没用的sleep连接,在网上搜索的方法对我都不奏效,因此只好使用最笨的办法,一个一个kill

select concat('KILL ',id,';') from information_schema.processlist where user='root'; 

(1)先把要kill的连接id都查询出来
(2)复制中间的kill id;内容到word文档
(3)替换掉符号“|”和回车符(在word中查询^p即可查询到回车符)
(4)把修改过的内容复制回终端,最后按回车执行!

项目中建议不使用mybatis plus插件

因Mybatis-plus存在SQL注入漏洞,不建议使用,如果要使用,请使用如下代码进行处理。

解决方案

@Configuration(proxyBeanMethods = false)
public class MybatisAutoConfiguration implements WebMvcConfigurer {

	/**
	 * SQL 过滤器避免SQL 注入
	 * @param argumentResolvers
	 */
	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		argumentResolvers.add(new SqlFilterArgumentResolver());
	}

	/**
	 * 分页插件, 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型
	 */
	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
		return interceptor;
	}

}

过滤核心代码:

/**
 *
 * 解决Mybatis Plus Order By SQL注入问题
 */
@Slf4j
public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {

	private final static String[] KEYWORDS = { "master", "truncate", "insert", "select", "delete", "update", "declare",
			"alter", "drop", "sleep" };

	/**
	 * 判断Controller是否包含page 参数
	 * @param parameter 参数
	 * @return 是否过滤
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.getParameterType().equals(Page.class);
	}

	/**
	 * @param parameter 入参集合
	 * @param mavContainer model 和 view
	 * @param webRequest web相关
	 * @param binderFactory 入参解析
	 * @return 检查后新的page对象
	 * <p>
	 * page 只支持查询 GET .如需解析POST获取请求报文体处理
	 */
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {

		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);

		String[] ascs = request.getParameterValues("ascs");
		String[] descs = request.getParameterValues("descs");
		String current = request.getParameter("current");
		String size = request.getParameter("size");

		Page<?> page = new Page<>();
		if (StrUtil.isNotBlank(current)) {
			page.setCurrent(Long.parseLong(current));
		}

		if (StrUtil.isNotBlank(size)) {
			page.setSize(Long.parseLong(size));
		}

		List<OrderItem> orderItemList = new ArrayList<>();
		Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(
				Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
		Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(
				Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
		page.addOrder(orderItemList);

		return page;
	}

	/**
	 * 判断用户输入里面有没有关键字
	 * @return Predicate
	 */
	private Predicate<String> sqlInjectPredicate() {
		return sql -> {
			for (String keyword : KEYWORDS) {
				if (StrUtil.containsIgnoreCase(sql, keyword)) {
					return false;
				}
			}
			return true;
		};
	}

}

本知识点参考:http://www.hackdig.com/05/hack-362820.htm

提交springboot启动速度

Spring 5.2.0+的版本,建议你的配置类均采用Lite模式去做,即显示设置proxyBeanMethods = false。Spring Boot在2.2.0版本(依赖于Spring 5.2.0)起就把它的所有的自动配置类的此属性改为了false,即@Configuration(proxyBeanMethods = false),提高Spring启动速度。

@FeignClient注解 中属性 contextId使用

比如我们有个user服务,但user服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中。这个记得要升级下springboot的依赖包,低版本不支持。

Client 1
@FeignClient(name = "optimization-user")
public interface UserRemoteClient {
	@GetMapping("/user/get")
	public User getUser(@RequestParam("id") int id);
}

Client 2
@FeignClient(name = "optimization-user")
public interface UserRemoteClient2 {
	@GetMapping("/user2/get")
	public User getUser(@RequestParam("id") int id);
}

解决方案

解决方案可以增加下面的配置,作用是允许出现beanName一样的BeanDefinition。(不建议使用)
spring.main.allow-bean-definition-overriding=true

另一种解决方案就是为每个Client手动指定不同的contextId,这样就不会冲突了。
上面给出了Bean名称冲突后的解决方案,下面来分析下contextId在Feign Client的作用,在注册Feign Client Configuration的时候需要一个名称,名称是通过getClientName方法获取的:

本知识点参考:http://www.imooc.com/article/details/id/299213

Java获取菜单树

不建议嵌套层级超过4层。即建议最多3层。可以参考如下代码进行改写。

 /**
     * 获取树结构
     *
     * @param words
     * @return
     */
    public List<SoftwareTypeVO> getSwClassifyList(String words) {
        //获取所有满足条件的行业
        List<SwClassify> swClassifyList = swClassifyMapper.querySwClassifyByWords(words);

        if (CollectionUtils.isEmpty(swClassifyList)) {
            return null;
        }

        // 转化不含嵌套层级的对象
        List<SoftwareTypeVO> softwareTypeVOList = new ArrayList<>();
        swClassifyList.stream().forEach(e -> {
            SoftwareTypeVO v = new SoftwareTypeVO();
            v.setId(e.getSwTypeId());
            v.setLevel(e.getCurLevel());
            v.setPId(e.getParentNo());
            v.setTypeName(e.getSwTypeName());
            softwareTypeVOList.add(v);
        });


        //返回的树列表
        List<SoftwareTypeVO> softwareTypeVOListRet = new ArrayList<>();
        //  先找到所有的一级列表,放入树列表中
        softwareTypeVOList.forEach(e -> {
            if (e.getLevel().equals(new Integer(1))) {
                softwareTypeVOListRet.add(e);
            }
        });

        //遍历一级,开始查找子层级
        softwareTypeVOListRet.forEach(er -> {
            er.setChildren(getChildren(er.getId(), softwareTypeVOList));
        });

        //屏蔽掉第一级菜单

        return softwareTypeVOListRet;
    }

    private List<SoftwareTypeVO> getChildren(Integer id, List<SoftwareTypeVO> softwareTypeList) {
        //  查找二级子层级
        List<SoftwareTypeVO> childList = new ArrayList<>();

        softwareTypeList.forEach(e -> {
            if (e.getPId().compareTo(id.longValue()) == 0) {
                childList.add(e);
            }
        });
        //递归查找查找二级的子层级
        childList.forEach(e -> {
            e.setChildren(getChildren(e.getId().intValue(), softwareTypeList));
        });
        return childList;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值