Java日常开发踩坑

一: 日期YYYY格式设置的坑

解析:设置的2019-12-31,可结果显示的2020-12-31,因为YYYY是基于周来计算年的,它指向当天所在周属于的年份,一周从周日开始算起,周六结束,只要本周跨年,那么这一周就算下一年的了。

正确姿势:使用yyyy格式。(实际使用时尽可能是用yyyy)

二:金额计算精度丢失

解析:发现以上结果还是不对,使用 BigDecimal 表示和计算浮点数,必须使用「字符串的构造方法」来初始化 BigDecimal

正确姿势:(实际操作中使用BigDecimal注意加引号)

三:FileReader默认编码导致乱码问题

解析:系统默认编码是GBK,文件内容的编码是UTF-8,demo中读取出来,出现乱码了,FileReader 是以当「前机器的默认字符集」来读取文件的,如果希望指定字符集的话,需要直接使用 InputStreamReader 和 FileInputStream。

正确姿势:使用InputStreamReader指定要读文件的编码格式(实际使用时在读包含中文的文件时注意文件的编码格式,保持一致)

四:Integer缓存的坑

解析:编译器会把 Integer a = 127 转换为 Integer.valueOf(127),通过翻源码我们发现:

i在一定范围内,是会返回缓存的。默认情况这个缓存的区间大小是[-128, 127],还有呢,设置 JVM 参数加上 -XX:AutoBoxCacheMax=1000,是可以调整这个区间参数的。

正确姿势:(包装类使用equals比较内容,==用于比较地址,及时是Integer会自动装箱、拆箱)

五:static静态变量依赖spring实例化变量,可能导致初始化出错

解析:这个静态的smsService有可能获取不到的,因为类加载顺序不是确定的

正确姿势:(现在用的少了)

六:使用ThreadLocal,线程重用导致信息错乱

解析:按理说每次获取的before应该都是null,但是呢,程序运行在 Tomcat 中,执行程序的线程是 Tomcat 的工作线程,而 Tomcat 的工作线程是基于线程池的,线程池会重用固定的几个线程,一旦线程重用,那么很可能首次从 ThreadLocal 获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal 中的用户信息就是其他用户的信息。

用户1,请求过来,会有以下结果,【符合预期】:

用户2请求过来,会有以下结果,不符合预期

正确姿势:(在finally代码块中删除ThreadLocal中的数据,确保数据不串)

七: Arrays.asList的几个坑

解析:基本类型不能作为 Arrays.asList方法的参数,否则会被当做一个参数。

Arrays.asList源码:

7.2:Arrays.asList 返回的 List 不支持增删操作

解析:Arrays.asList 返回的 List 并不是我们期望的 java.util.ArrayList,而是 Arrays 的内部类 ArrayList。内部类的ArrayList没有实现add方法,而用的是父类的add方法,参数不对,是会抛出异常。

7.3:使用Arrays.asList的时候,对原始数组的修改会影响到我们获得的那个List

正确姿势:实际如果需要可以用new ArrayList(Arrays.asList(arr))包一下( java.util包下的)

八:生产环境异常打印常见的坑

解析:只保留了异常消息,栈没有记录啦

解析:因为它占用太多内存,造成锁死,并且,日志交错混合,也不易读

正确姿势:

8.2:线程池里的 throw new RuntimeException会被吞掉

运行结果:

正确姿势:任务代码使用try/catch捕获异常

九:使用Executors声明线程池,newFixedThreadPool的OOM问题

解析:看下源码,其实newFixedThreadPool使用的是无界队列!

newFixedThreadPool线程池的核心线程数是固定的,它使用了近乎于无界的LinkedBlockingQueue阻塞队列。当核心线程用完后,任务会入队到阻塞队列,如果任务执行的时间比较长,没有释放,会导致越来越多的任务堆积到阻塞队列,最后导致机器的内存使用不停的飙升,造成JVM OOM。

正确姿势:使用 Semaphore(信号量)控制进入阻塞队列的数据,在线程池里的任务每执行完一个任务释放一个信号量,此外需要注意:

为了避免出现问题,在主线程中可以使用semaphore.availablePermits()+while true来判断子线程是否结束,尤其是定时任务+数据量大+定时任务执行时间周期短,防止在本次子线程还未结束,下一次主线程进来又一次执行子线程的逻辑,造成子线程的逻辑重复执行,数据库出现重复数据。 

十:先查询,再更新/删除的并发一致性问题

正确姿势:

一种情况:

二种情况:使用事务

十一:事务未生效的坑

1、在非public修饰的方法使用

2、本类方法直接调用

3、异常被try...catch吃了,导致事务失效

十二:Mysql引号用错位置,线上数据某字段全部变成0

原表数据:

执行以下sql:

UPDATE student set name="bbbb where name"="bb"   name的字段值全部变成了0

解析:这种sql虽然说单条执行错误很容易被看出来,但是如果你是批量执行的时候就很容易出错,或者是在线上执行的时候,语法不错全表更新,心态崩了!!!!

由于SELECT "bbbb where name"="bb"的结果是0,sql服务器将

UPDATE student set name="bbbb where name"="bb"     解析成  

UPDATE student set name=("bbbb where name"="bb") 进而解析成

UPDATE student set name-0  执行全表扫描

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 阿里巴巴 JAVA 开发手册 1 / 32 Java 开发手册 版本号 制定团队 更新日期 备 注 1.0.0 阿...
    糖宝_阅读 7,638评论 0 5
  • 一、 编程规约 (一) 命名风格 【强制】抽象类命名使用Abstract或Base开头;异常类命名使用Except...
    chen_chen_chen_阅读 249评论 0 0
  • 一、编程规约 (一)命名规约 【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。反...
    喝咖啡的蚂蚁阅读 1,523评论 0 2
  • 目录 一、 编程规约..................................................
    owen_he阅读 4,975评论 0 4
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,551评论 16 22