Java 业务开发常见错误 100 例 -- 学习笔记 -- 2

11 | 空值处理:分不清楚的null和恼人的空指针

Optional类
Optional 类是一个可以为null的容器对象,可用于处理空指针异常问题。
常见方法,可以有效地解决空指针问题:

//返回一个指定非null值的Optional。
static <T> Optional<T> of(T value)
//如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
static <T> Optional<T> ofNullable(T value)
//如果存在该值,返回值, 否则返回 other。
T orElse(T other)

如何定位空指针问题
阿里开源的 Java 故障诊断神器Arthas

12 | 异常处理:别让自己在出问题的时候变为瞎子

捕获和处理异常容易犯的错

  1. 不建议在框架层面进行异常的自动、统一处理,尤其不要随意捕获异常
    但,框架可以做兜底工作。如果异常上升到最上层逻辑还是无法处理的话,可以以统一的方式进行异常转换
  2. 捕获了异常后不可以直接生吞
  3. 不要丢弃异常的原始信息
  4. 抛出异常时不指定任何消息

小心 finally 中的异常
当使用finally释放资源时,如果finally中也有抛出异常时,由于一个方法不能出现两个异常,所以finally抛出的异常有可能会把try中的异常覆盖,从而导致更难发现问题。可以使用try-with-resources模式来解决问题

异常不可以为静态变量
把异常定义为静态变量会导致异常信息固化,这就和异常的栈一定是需要根据当前调用来动态获取相矛盾。

没看懂,先码上(确保正确处理了线程池中任务的异常,如果任务通过 execute 提交,那么出现异常会导致线程退出,大量的异常会导致线程重复创建引起性能问题,我们应该尽可能确保任务不出异常,同时设置默认的未捕获异常处理程序来兜底;如果任务通过 submit 提交意味着我们关心任务的执行结果,应该通过拿到的 Future 调用其 get 方法来获得任务运行结果和可能出现的异常,否则异常可能就被生吞了。)

###13 | 日志:日志记录真没你想象的那么简单

14 | 文件IO:实现高效正确的文件读写并非易事

为什么会有乱码问题
是因为电脑的默认编码格式不同,常见的有UTF-8和GBK,如果写入和读取方式不一致就会出现类似“锟斤拷烫烫烫”的问题。解决方似也非常的简单:直接使用InputStreamReader和 FilelnputStream来指定字符集就可以了。
使用 Files 类静态方法进行文件操作注意释放文件句柄
使用Files类的一些流式处理操作,不会自动关闭底层句柄,有时会造成OOM。解决方案是利用前面提到的try-with-resources方式来配合,确保流的close方法可以调用释放资源。
注意读写文件要考虑设置缓冲区
每读取一个字节、每写入一个字节都进行一次 IO 操作,代价太大了。解决方案就是,考虑使用缓冲区作为过渡,一次性从原文件读取一定数量的数据到缓冲区,一次性写入一定数量的数据到目标文件。
IO的各个方法中,最慢的是单字节读写文件流的方式,最快的是 FileChannel.transferTo 方式进行流转发的方式

15 | 序列化:一来一回你还是原来的你吗?

序列化乱码问题
要确保序列化和反序列化算法的一致性。因为,不同序列化算法输出必定不同,要正确处理序列化后的数据就要使用相同的反序列化算法。
使用RedisTemplate出现ClassCastException的问题
还是后面再看看吧,不想复制粘贴写流水账

16 | 用好Java 8的日期时间类,少踩一些“老三样”的坑

初始化日期时间
可以选择使用 Calendar 类来初始化时间
使用 Calendar 改造,初始化时年参数直接使用当前年即可,不过月需要注意是从 0 到 11。当然,你也可以直接使用 Calendar.DECEMBER 来初始化月份,更不容易犯错。
日期时间格式化和解析(SimpleDateFormat)

  1. 没有特殊需求,针对年份的日期格式化,应该一律使用 “y” 而非 “Y”
  2. 定义的 static 的 SimpleDateFormat 可能会出现线程安全问题。原因就是SimpleDateFormat在进行定义解析和格式化时间时并不是线程安全的一次性工作。因此只能在同一个线程复用 SimpleDateFormat,比较好的解决方式是,通过 ThreadLocal 来存放 SimpleDateFormat
  3. 当需要解析的字符串和格式不匹配的时候,SimpleDateFormat还能得到结果但是并不正确
    使用DateTimeFormatter可以避免上面SimpleDateFormat的三个坑
    日期时间的计算
    手动计算时间戳来加减日期可能会出现int溢出问题,改成Long可以解决部分问题,但是手动在时间戳上进行计算操作的方式非常容易出错。对于 Java 8 之前的代码,建议使用 Calendar;使用 Java 8 的日期时间类型,可以直接进行各种计算,更加简洁和方便

17 | 别以为“自动挡”就不可能出现OOM

太多份相同的对象导致 OOM
不管是程序的实现不合理,还是因为各种框架对数据的重复处理、加工和转换,相同的数据在内存中不一定只占用一份空间。例如书中举的例子,我们每次需要一个DTO对象的时候都是新建了对象,为了减少重复对象的存在,可以引入HashSet来去重
使用 WeakHashMap 不等于不会 OOM
WeakHashMap 的 Key 虽然是弱引用,但是其 Value 却持有 Key 中对象的强引用,Value 被 Entry 引用,Entry 被 WeakHashMap 引用,最终导致 Key 无法回收。解决方案一是让 Value 变为弱引用,二是还有一种办法就是,让 Value 不再引用 Key,而是重新 new 出一个新的对象赋值给Value
Tomcat 参数配置不合理导致 OOM

18 | 当反射、注解和泛型遇到OOP时,会有哪些坑?

反射调用方法不是以传参决定重载
反射调用方法,是以反射获取方法时传入的方法名称和参数类型而不是调用方法时的参数类型,来确定调用方法

getDeclaredMethod(“age”, Integer.TYPE)//对应age(int age)
getDeclaredMethod(“age”, Integer.class)//对应 age(Integerage)

泛型经过类型擦除多出桥接方法的坑
没看懂
注解可以继承吗?
@Inherited 只能实现类上的注解继承;
Spring 提供了 AnnotatedElementUtils 类,来方便我们处理注解的继承问题。这个类的 findMergedAnnotation 工具方法,可以帮助我们找出父类和接口、父类方法和接口方法上的注解,并可以处理桥接方法,实现一键找到继承链的注解

19 | Spring框架:IoC和AOP是扩展的核心

单例的 Bean 如何注入 Prototype 的 Bean?
如果有两个父类相同的子类,同时注册为bean,且父类是带有状态的,就有可能出现线程安全问题,例如:两个子类进行链表元素添加操作时会添加到同一个父类的链表中。原因是bean的自动装配是单例模式的,解决这个问题也比较简单,在子类添加@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)注解,这时自动装配不再是默认的单例模式。
但是也会有其他情况,比如在service上添加了Scope注解,但是controller仍是单例的自动装配,那么由于controller对象只有一个,它对应的service也就只有一个,还是会出现线程安全问题。修复方式是,让 Service 以代理方式注入。这样虽然 Controller 本身是单例的,但每次都能从代理获取 Service。这样一来,prototype 范围的配置才能真正生效:@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
监控切面因为顺序问题导致 Spring 事务失效
使用@Order为自定义的切面设置优先级
两个不同优先级切面中 @Before、@After 和 @Around 三种增强的执行顺序(书上的图片):
请添加图片描述

20 | Spring框架:框架帮我们做了很多工作也带来了复杂度

Feign AOP 切不到的诡异案例
只能说跟看天书似的,就看懂了最后一句总结:
如果我们要扩展 Spring 的组件,那么只有清晰了解 Spring 自动装配的运作方式,才能鉴别运行时对象在 Spring 容器中的情况,不能想当然认为代码中能看到的所有 Spring 的类都是 Bean。
Spring 程序配置的优先级问题
PropertySource配置优先级︰系统属性->环境变量->配置文件
在配置文件自定义配置的时候尽量避免因为相同的Key而出现配置冲突

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值