写Java代码的一些小技巧

2018-12-06  本文已影响91人  九里

写Java代码有两年多了,遇到过很多坑,也有一些小小的心得。特地分享出来供各位学习交流。这些技巧主要涉及谷歌Guava工具类的使用、Java 8新特性的使用、DSL风格开发、代码封装等技巧。

一、null的判断

对于Java Developer来说空指针异常让我们深恶痛绝。我们进行单元测试的时候很多的时间就是在消除空指针异常,一个容易报空指针的代码肯定称不上是合格的代码。当然,完全消除空指针绝非易事,但我们可以最大限度地让空指针异常变得可控。只要我们知道代码何时会出现null,那么我们就成功一大半了。

1. String中的空指针异常

String是使用很多的类型,但是使用频繁的往往也更容易出现null的问题。

public String test(String id){
    String result=userService.findNameById(id);
    return result.toUpperCase();
}

上面这段代码有几个不确定的地方,因为id可能为null,与此同时dinNameById的方法也可能返回null,那么最后result.toUpperCase()这个方法就有可能出现空指针异常。这个问题的解决方法很简单,我稍微改进了一点代码

public String test( String id ){
    if ( id != null && !"".equals(id)){
        String result = userService.findNameById( id );
        if ( result != null ){
            return result.toUpperCase();
        }
        return null;
    }
    return null;
}

我们对Id进行了null判断和empty判断,确保id是合法的。对查找的结果进行null判断,确保我们不会对null对象进行任何操作,这是因为.操作是空指针的重灾区。那么这个方法返回null这个操作是安全的吗?

答案是安全的。显示地返回null,可以让其他人使用这个方法的时候有意地进行null判断,从而避免出现空指针的问题。还是那句话避免空指针异常并不是不使用null,而是让null变得可控。

2.使用Guava工具包的Strings类

上面的代码里我们有一行代码是这样写的

if ( id != null && !"".equals(id))

如果大量这么写的话我们会觉得十分繁琐,这种重复的工作是可以简化的。
比如我们可以定义一个StringUtil类,里面写一个公共方法来判断null或empty。Guava工具包的Strings类已经为我们写好了,我十分建议使用稳定可靠的Guava工具类来代替自己手写的工具类。使用Guava我们只需要这样写:

if(!Strings.isNullOrEmpty(id))

比之前简单了很多吧。

二、如何判断相等

对于基本类型我们用==判断就可以,如果是String类型我们使用equals,这个是很基础的知识了。那么我们怎么判断两个对象是否相等呢?

对于集合类的对象,我们可以遍历对象中的每个数据,逐一判断是否相等,这是简单粗暴的方式。那么如果我们判断两个class是否相等该怎么做呢?答案是用hashcode。

if(obj1.toString().hashCode()==obj2.toString().hashCode())

这里的重点是你比较的对象必须先转成String串,然后比较String串的hashcode。因为直接比较对象的hashcode那是肯定不一样的。

三、灵活使用Stream

Stream是JDK8的新特性,任何标注了@FunctionalInterface的接口都能使用Stream流来处理数据。
很多同学知道函数式编程很强大,也知道怎么用,但是实际应用起来总会摸不着头脑。

Stream操作中最常用的就是用来遍历了,只要了解了Stream的使用基本都会用它来遍历。但是什么时候用map,什么时候用forEach,这是个问题。

其实实际项目中我们遍历一个集合类,无非是两类操作。第一种是对集合中元素的本身进行操作(如字段值的修改),第二种是消费集合中的元素,比如打印每个元素的某个值,或者让另一个方法使用元素。当然,还有第三种,就是过滤、聚合、排序,这些都是比较简单的,我们这里不谈。

针对第一种我们应该把这种使用成为Function,这是一种给定T对象,返回R的函数式接口。详情请看我之前的文章Function接口的使用。第二种其实是Consumer,也就是消费者。这是给定T,但不返回值的函数式接口。


其实我们只看map里面的参数也知道,第一类的操作用map就行了。

第二种显然直接forEach就可以了。

这个技巧是通用的,如果你想实现什么操作,但不知道该用什么接口的时候,直接看文档就知道该用哪个接口了。

四、键值对的key和value互换一下?

        List<String> DBTypes = Arrays.asList("Oracle", "SYBASE","MYSQl子对象");
        List<String> JMXTypes = Arrays.asList( "消息中间件", "JBoss子对象", "Tomcat子对象",  "Apache子对象");
        List<String> HOSTTypes = Arrays.asList("HOST", "主机子对象");
        List<String> WMITypes = Arrays.asList("Windows单进程", "Windows");

像上面这样的场景你肯定遇到过。几个子类型,对应一个父类型,这可能有多个集合。那么如果我需要根据一个子类型,找到它对应的父类型,我就要遍历每个集合的子类型。而且由于HashMap的键不能重复,因此这种数据没有办法转成Map来操作。

这时候我们可以调换下思路,HashMap中的键不能重复,但是值是可以重复的。我们完全可以把上面所有的子类型当做key,其父类型当做value,存储到一个Map中去。下面我使用stream来把List转为Map:

        Map<String, String> map = Maps.newHashMap();
        map.putAll(DBTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "JDBC")));
        map.putAll(JMXTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "JMX")));
        map.putAll(HOSTTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "HOST")));
        map.putAll(WMITypes.stream().collect(Collectors.toMap(Function.identity(), i -> "WMI")));

于是所有的子类型和父类型的对应关系就都存到一个Map中去了,我们找起来就太方便了,一行代码搞定!

   String parentType=map.get(name);
上一篇下一篇

猜你喜欢

热点阅读