Guava常用工具类总结
原文链接:https://www.cnblogs.com/cuzzz/p/16609775.html
“我想写得更优雅,可是没人告诉我怎么写得更优雅”
“Null的含糊语义让人很不舒服。Null很少可以明确地表示某种语义,例如,Map.get(key)返回Null时,可能表示map中的值是null,亦或map中没有key对应的值。Null可以表示失败、成功或几乎任何情况。使用Null以外的特定值,会让你的逻辑描述变得更清晰。”
此文档只是Guava最常用工具介绍,guava存在更多本文档没有介绍的api
OptionalOptional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。
这里讲的optional 也是指jdk中的optional,其实二者类似,但是编码使用gauva的optional,阿里巴巴编程规范会提醒换成jdk自带的optio ...
图灵考核路线
本文介绍图灵智能创新团队各方向学习路线和考核内容
防坑集锦
谨慎使用@Builder大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder 并不是链式编程的最佳实践,它会额外创建内部类,存在继承关系时还需要使用 @SuperBuilder 注解,设置默认值时也需要额外的 @Builder.Default 去设置默认值,无疑增加了很多不必要的复杂度。
为什么(1)@Builder 生成的构造器不是完美的,它不能区分哪些参数是必须的,哪些是可选的。如果没有提供必须的参数,构造器可能会创建出不完整或者不合法的对象。
@Builder 注解产生的 Builder 类的构造方法默认并不能限定必传参数。
(2)很多人 喜欢 @Builder 和 @Data 搭配使用,导致生成的构造器是可变的,它允许使用 setter 方法修改构造器的状态。这违反了构造器模式的原则,构造器应该是不可变的,一旦创建就不能被修改。
如果非要使用 @Builder ,那么不要用 @Data ,要用 @Getter。相对来说,反而 @Accessors 的行为更符合这个要求。
(3)@Builder 生成的构造器不适合用于短暂的对象,它会增加代码的复 ...
(四)提升系统的“读”性能
缓存本地缓存
比如Caffeine
缓存不太容易变化的数据(保障缓存一致性)
缓存数据量较小的数据(不会受到内存的限制)
如元数据、配置数据:启动的时候,从DB加载到内存的缓存块里。这样每次读这些数据的时候,就不用访问DB了
中心化缓存
比如Redis
针对用户请求相关数据的缓存
用户流量触发的DB的数据(查询)
业务计算的数据
返回结果的数据
缓存更新策略cache aside pattern(经典缓存更新策略)采用的是写数据时删除缓存中旧值,读数据时更新新值
但如果有一个读请求和一个写请求几乎同时到达DB
读请求先访问数据库,将(旧)值写入缓存时有些延迟
而写请求后访问数据库(新值),但把缓存删完了,读请求才开始写入缓存
这就造成缓存数据是旧的
解决方案
保证即使把旧值更新到cache的情况,在后面的某一个时间点也能淘汰掉有问题的缓存
sequenceDiagram
Server->>Database: 1.写入数据
Server->>Cache: 2.删除缓存
Server->>Datab ...
(三)如何提高代码的扩展性
先来看一段代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445/** * 转账服务 * * @param payer 付款方 * @param payee 收款方 * @param money 转账金额 * * @return 是否转账成功 */public boolean transfer(String payer, String payee, String money) { Log.info("transfer start, payer = " + payer + ", payee = " + payee + ", money = " + money); // 1. 检查参数 if (!isValidUser(payer) || !isValidUser(payee) || !isValidMoney(money)) { return false; & ...
(二)搭完架子串珠子——流程引擎
单用模板方法带来的问题1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950public UserInfoDTO getUserInfo(@RequestParam("userId") String userId) { return (new ServiceTemplate<String>() { @Override public void validParam(String request) { // 1. 校验入参 if (userId == null || userId.isEmpty()) { throw new RuntimeException("param UserId is invalid"); } ...
(一)搭系统先搭架子——模板方法模式
引例——抽取工具类先来看看这段代码:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455@GetMapping(path = "/getUserInfo")public UserInfoDTO getUserInfo(@RequestParam("userId") String userId) { // 1. 打印入口日志 logger.info("userId: " + userId + " visit path: /getUserInfo"); try { // 2. 检验入参合理性,userId是否以字母'u'开头。正确的uid示例:u001 if (userId == null || !userId.startsWith("u")) { ...
代码美学
在代码中命名避免使用单个字母命名数学家以简洁为荣,他们爱把表达式浓缩成最精简的模式。
问题在于你不用编辑数学式子,但需要编辑代码
因为一个字母无法体现一个变量的相关信息
绝对不要缩写缩写的含意依赖于上下文,而你并不总是了解这个上下文。这就会造成读代码的时间可能比写代码的时间还多
因此强行让自己去理解各种不同的命名方式,只会让阅读陌生代码更加困难
既然现在我们有了会自动补全的IDE和大屏幕,为什么不把名字写完整些?
在变量名中带上单位比如说你有一个接收延迟时间为参数的函数
1void execute(int delay)
如果这个值的单位为秒
1void execute(int delaySeconds)
这样这个函数的用户就能明白,应该传入秒数为参数
同时也让编辑这个类的人能够明白,这个函数用的是什么单位
当然,更好的做法是使用枚举,这样用户就不用关心具体的单位
12345678@Getterpublic enum Delay { SECONDS_1(1), SECONDS_5(5), SECONDS_10(10), private fin ...
后端入门
本文主要介绍了后端的概念和Java学习路线的指导
(一)Go基本语法
变量变量声明显式声明
如果我们未为一个变量初始化,则必须显式指定变量类型
如果声明时不指定具体的值,则会被赋值为默认值。
12var str string // str的值为""(空字符串)var num int // num的值为0
快速声明赋值
Go 支持变量类型自动推断,也就是说,当我们立即为一个变量进行初始化时,其类型是可以省略的
可以通过 := 符号以一种简单的方式(也是实际上最常用的方式)声明一个变量
12a, b := 1, 2// Go 支持多个变量在同一行同时声明str := "我是字符串。"
常量声明可以使用 const 关键字代替 var 关键字来创建一个常量(不可变变量)
12345const str string = "我是字符串"const num int = 1const a, b = 3, 4num = 2 // 若更改会报错
流程控制if
Go的if语句和C、Java类似,只不过if后面没有括号,但是加上括号也能正确运行
if后面的开始大括号必须跟if在同一行,不能换行 ...