001.从合理使用工具开始

最近尝试在团队里发起一个技术探讨会

尝试推动大家每次选一个小的技术点,进行分享,然后落地成开发规范、code review List


开篇总要说下背景,所以免不了啰嗦几句


首先,代码是程序猿的脸面,怎么能不上心

不久前,看了篇知乎上的文章,顿时菊花一紧,心想,此时难不成在某个地方也有人在吐槽哥的代码

出来混总是要还的,这话还真应验了,前段时间,接触的系统,那个惨不忍睹


喘口气,擦下汗,代码设计这个事情,还真马虎不了,还是多上点心好


知乎文章地址在此,感兴趣可自行阅读

http://www.zhihu.com/question/32039226?from=timeline&isappinstalled=1


另外也由于目前团队存在些问题,让我去思考怎么更好地提升自己的同时,也让团队的小伙伴去提升


前段时间线上出过问题:


一个在线服务硬是把redis的CPU给耗尽了,临时加了好多个实例才缓过来

找了半天,原来是代码里有redis的危险操作,keys*操作


想来想去,除了可以加强流程规范外,可以尝试弄几轮技术探讨,然后落地成开发规范,code review list

所以就有下面要开始的这个系列主题内容《技术练级》

主要涉及代码规范、系统设计、参数配置优化这些内容


技术成长就如同升级打怪,没有捷径,只有不断的攒经验

磨刀不误砍柴工,大家一起坚持


废话有点多,各位大侠见谅,下面进入正题把


———分割线———


第一期的分享,就从简单的开发实践规范开始,主题是:从合理使用工具开始


合理的选择第三方工具,能够极大的提高我们的开发效率,避免重复造轮子

这些工具经过严格的测试与验证,比自己写得,在性能与稳定性上定然会好很多的


今天要分享的工具为:

guava(Google)

fastjson(阿里)

joda-time(Google)


part1,参数校验

对于参数校验,一般都会习惯这样写

if (StringUtils.isBlank(userName)) {
throw new RuntimeException("名字为空");
}
if (0L == startTime || (0L == endTime)) {
throw new RuntimeException("时间为空");
}

如果if条件比较多,那很可能整屏都是这样的参数校验,对于有洁癖的你肯定在想,有没有更优雅的方式呢

答案当然是有的,guava该出场了

现在可以这样写


try {
    Preconditions. checkArgument(userId >  || userId ==  0 "userId  %s <= 0" userId) ;
    Preconditions. checkArgument(startTime < endTime "startTime %s >= endTime %s" startTime endTime) ;
    Preconditions. checkNotNull(userName "userName is null") ;
    Preconditions. checkElementIndex(index size "out of bound") ;

    // check pass , good news
    // just continue to run

catch ( IllegalArgumentException e) {
    // handle log
catch ( NullPointerException e) {
    // handle log
catch ( IndexOutOfBoundsException e) {
    // handle log
}

Preconditions.checkArgument 可以根据传进来的条件进行判断,不满足会抛出 IllegalArgumentException,可以传入异常的信息,我们可以catch住然后进一步处理

Preconditions.checkNotNull 非空判断简单如此,传入要校验的对象以及异常信息,对象为空就抛空指针异常

Preconditions.checkElementIndex 这个可以作为越界的校验,具体就不说了,一看就懂


part2,类型结构增强

新建一个list,一般会这样写

List<String> list = new ArrayList<String>();

而使用 guava ,则可以这样写

List<String> list1 = Lists.newArrayList();

当然从JDK7开始,也可以这样写了,新增了个diamond的类型符

List<String> list2 = new ArrayList<>();

如果要创建的同时进行初始化,可以用guava这样写

List<String> list3 = Lists.newArrayList("alpha", "beta", "gamma");

虽然可以直接使用JDK的Arrays方法,但感觉guava的更好理解

List<String> list4 = Arrays.asList("a", "b");

如果知道未来要使用的空间大小,也可以这样

List<String> exactly100 = Lists.newArrayListWithCapacity(100);


字符串拼接分割处理,是我们也经常会遇到的

guava提供了非常便利的API接口


List< Integer> appIds =  Lists. newArrayList( 1 2 3 4 5) ;
String str =  Joiner. on( "|").skipNulls().join(appIds) ;
System. out.println(str) ; //1|2|3|4|5
List< String> appIdStr =  Splitter. on( "|").splitToList(str) ;
System. out.println(appIdStr) ;

Map< String String> maps = ImmutableMap. of( "1" "11" "2" "22" "3" "33") ;
String mapStr =  Joiner. on( "|").withKeyValueSeparator( ":").join(maps) ;
System. out.println(mapStr) ; //1:11|2:22|3:33
Map< String String> maps2 =  Splitter. on( "|").withKeyValueSeparator( ":").split(mapStr) ;
System. out.println(maps2) ;

part3,localcache

有时候我们会需要在本地JVM构建一个缓存,来存放可能会频繁访问的数据

一般情况都会采用像ConcurrentHashMap这样的数据结构来存储数据,用它的好处也比较明显,一来对API接口都比较熟悉,二是使用起来也简单容易理解

不好的地方就是,需要我们自己控制缓存的大小,有时候可能还会存在一些已经过期的数据,但还占用着内存,而这个过期数据的清除也没有很好的方法,自己写的话,需要花点精力还特别容易出问题


对此,guava给我们提供了一个选择,带有数据量限制与过期数据清除功能的本地缓存API

使用guava提供的API,可以方便的构建出本地缓存

我们可以设置最大的存储量限制、数据多久失效

这个cache内部也是基于Map实现的,使用上就跟普通的hashmap一样,直接调用  get 方法来获取数据即可;cache内部的并发控制也采用了类似ConcurrentHashMap的分段锁的机制

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(5)
.expireAfterAccess(3, TimeUnit.SECONDS)
.removalListener(new MyListener())
.build(
new CacheLoader<String, String>() {
public String load(String key) throws Exception {
System.out.println("load "+key);
return key + new DateTime().toString(" yyyy-MM-dd HH:mm:ss");

}
}
);
 
 
static class MyListener implements RemovalListener {
@Override
public void onRemoval(RemovalNotification notification) {
System.out.println("key value was removed: "+notification.getKey()+" "+notification.getValue());
}
}
cache.get(key);



guava官方文档在此: https://github.com/google/guava/wiki

英文翻译版: http://ifeve.com/google-guava/


part4,json

我们使用json的场景蛮多,如:

在网络传输上,使用json格式是个比较方便好阅读的方式

部分存储系统的里数据,会使用json格式,然后读出来会直接反序列化成对象来使用,甚是方便


至于json工具呢,这里推荐使用fastjson

官方称 fastjson性能比目前的json工具都快

fastjson API使用起来简单好理解


直接上官方的例子,我加了个日期字段,User的一个属性没有赋值,然后使用的序列化方法换成JSON.toJSONStringWithDateFormat

Group group = new Group();
group.setId(0L);
group.setName("admin");

User guestUser = new User();
guestUser.setId(2L);
guestUser.setName("guest");
guestUser.setDate(new Date());

User rootUser = new User();
rootUser.setId(3L);
// rootUser.setName("root");

group.addUser(guestUser);
group.addUser(rootUser);


结果输出如下:


//fastjson 序列化
String jsonString = JSON.toJSONStringWithDateFormat(group, "yyyy-MM-dd HH:mm:ss.SSS");
//{"id":0,"name":"admin","users":[{"date":"2015-12-16 21:00:30.592","id":2,"name":"guest"},{"id":3}]}
System.out.println(jsonString);
//fastjson 反序列化
Group newGroup = JSON.parseObject(jsonString, Group.class);
//Group{id=0, name='admin', users=[User{id=2, name='guest', date=Wed Dec 16 21:00:30 CST 2015}, User{id=3, name='null', date=null}]}
System.out.println(newGroup);

可以看到,对于有日期的对象,可以序列化成固定格式

同时,反序列化能够自动识别日期的数据


这里要注意下,反序列化时,对于没有值得字符串属性,会默认赋值 “null”


fastjson 官方地址: https://github.com/alibaba/fastjson


part5,日期时间

我们经常需要获取一个日期时间,并以一个比较容易阅读的格式来展示

好像用fastjson的API也可以

String jsonDateStr = JSON.toJSONStringWithDateFormat(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
System.out.println(jsonDateStr);//2015-12-16 22:06:10.330

不过更加专业的方法当然是使用时间相关API了,这里介绍 joda-time

DateTime dt = new DateTime();
System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));//2015-12-17 21:59:12

创建一个日期时间对象,并格式化输出,好简单

也可以创建固定时间的日期对象

DateTime dateTime=new DateTime(2015, 12, 15, 18, 23, 55);

可以处理时间字符串,转化成日期对象,如下:

DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dateTime2 = DateTime.parse("2015-12-17 23:22:45", format);

对获取的时间可以进行操作,加一天,加一月,减小时、分钟,就是一个方法调用这么简单

dateTime = dateTime.plusDays(1) // 增加天
.plusYears(1)// 增加年
.plusMonths(1)// 增加月
.plusWeeks(1)// 增加星期
.minusMinutes(1)// 减分钟
.minusHours(1)// 减小时
.minusSeconds(1);// 减秒数

获取当前具体的某天某点,可以这样取,dateTime本身提供非常便利的接口

System.out.println(dateTime.getYear());
System.out.println(dateTime.getMonthOfYear());
System.out.println(dateTime.getDayOfMonth());
System.out.println(dateTime.getDayOfWeek());
System.out.println(dateTime.getHourOfDay());
System.out.println(dateTime.getMinuteOfHour());

时间的比较直接调用接口即可,如下:如当前时间比较或者两个时间比较

dateTime.isBeforeNow();
dateTime.isAfter(dateTime2);


joda-time与JDK能够简单方便的转换

Date date = new Date();
DateTime fromJDKDate = new DateTime(date);
Date toJDKDate = dateTime.toDate();


“麻麻再也不用担心我的’时间管理’了”


JDK8之前,JDK的时间API接口特别不好用,但JDK8之后,一切都不一样了

原来新的时间API接口负责人就是joda-time的开发者

在JDK8里,我们也终于能够好好的跟时间玩耍了

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(localDateTime.getHour());
System.out.println(localDateTime.getDayOfMonth());

localDateTime = LocalDateTime.of(2015, 12, 20, 20, 20, 20);
System.out.println(localDateTime);

DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(localDateTime.format(f));

localDateTime = LocalDateTime.parse("2015-12-17 23:22:45", f);
System.out.println(localDateTime.format(f));


在没有升级到JDK8前,还是用joda-time把

joda-time 官方地址: http://www.joda.org/joda-time/index.html


另外还有经常使用的工具如Apache common包,Apache HttpComponents包(替换HTTPclient)


忠告:

这些工具用好了就是一把利剑

而要用好他们还是得稍稍了解他们的用法及API的大致原理,避免踩坑




大家如果有更好的工具,或者工具的一些特别有意思的用法,欢迎一起交流 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值