为什么你要使用Java 8


本篇文章的作者dream,是我dota群中的一位大神成员(群里最低天梯分数线1800),他对Java服务器技术有着深刻的理解,同时还是公司的董事会成员。今天他带来了一篇关于Java 8新特性的文章,希望大家能够有所收获。


dream 的博客地址:

http://www.jsondream.com


Java 8优势


相信对于Java 8 这个字眼大家都已经不陌生了,但是对于Java 8 的了解和使用很多人还不是很清楚,甚至很多人还在犹豫着要不要用 Java 8,那么我写这篇文章的目的就是告诉你,你一定要使用Java 8 以及 你为什么要使用Java 8


lambda


Java 7 以及之前的代码里,为了实现带一个方法的接口,往往需要定义一个匿名类并复写接口方法,代码显得很臃肿。


比如我们用来给数组排序的 Comparator 接口:




然而对于这种只有一个方法的接口,在 Java 8 里面,我们可以把它视为一个函数,用 lambda表示式 简化如下的操作:


String[] str = "aa、bb、cc、dd".split("");
Arrays.sort(str, (s1, s2) -> {
   return s1.toLowerCase().compareTo(s2.toLowerCase()); });


这样我们的代码看着就简洁了很多,或许单单看这一个例子大家还体会不到 lambda 那神奇的魔力。


如果接触过 jedis 的朋友,相信都知道在使用 jedis 的时候会有获取连接,进行操作,释放连接这几个步骤。


大家能看出来,除了进行操作这个步骤是不同的,连接的获取和释放代码是相同的,那么这时候我们就可以考虑用 lambda表达式 把他封装起来,具体的实现可以去我的github上看,仓库地址:

https://github.com/wgd12389/redisses/tree/master/redisses-client


stream


集合类新增的 stream()方法 用于把一个集合变成 Stream,然后,通过 filter()、map()等 实现 Stream 的变换。Stream 还有一个 forEach() 来完成每个元素的迭代。


例如我原来要遍历一个 list,要对这个 list 做一个for遍历,代码是这样的:


List<String> a = new ArrayList<String>();
for (String o : a) {
   System.out.println(o) }


而我用了 stream 之后,代码却简洁成这个样子:


List<String> a = new ArrayList<>();
a.stream().forEach(c-> System.out.println(c));


在比如我想取得这个 list 的前10项:


List<String> list = oldList.limit(10).collect(Collectors.toList());


那么如果我想取得数列的第20~30项,可以这样做:


List<String> list = oldList.skip(20).limit(10).collect(Collectors.toList());


而且 Stream 串行并行 两种,串行Stream 上的操作是在一个线程中依次完成,而 并行Stream 则是在多个线程上同时执行。


stream 提供了 parallelStream 使用多线程进行操作,加大了运算效率。


Stream 中还有 fifter、sorted、Match、map、Reduce 这一类的api,大家可以在日后的使用中慢慢去体会他的强大之处。


optional


Optional 不是函数是接口,这是个用来 防止 NullPointerException异常 的辅助类型,这是下一届中将要用到的重要概念,他最初源自于google出的框架包 guava 之中,于1.8正式引入到jdk中使用。


Optional 被定义为一个简单的容器,其值 可能是null或者不是null。在之前一般某个函数应该返回非空对象但是偶尔却可能返回了 null,然后这样的结果或许可能会让你的程序出现 NullPointerException,会造成程序的崩溃等不可预估的问题,而在 Java 8 中,不推荐你返回 null 而是返回 Optional


Optional<String> optional = Optional.of("bam");
// true
optional.isPresent();
// "bam"
optional.get();
// "bam"
optional.orElse("fallback");
// "b"
optional.ifPresent((s) -> System.out.println(s.charAt(0)));


构造函数和方法引用


构造函数


Java 8之前 我们新建一个对象都是这样的:


User u = new User();


而在 Java 8中 我们可以这么写代码:


User u = User::new;


方法引用


我经常把方法引用和 lambda 合在一起使用。比如我们有一个业务需要把集合里每个元素做处理,那么我们根据业务规范要把这个处理的事件写成一个业务方法,然后遍历集合元素,并传递元素调用该方法。


一般我们在使用 Java 8以前 都是这么写的:




那么在 Java 8 中,上面的东西我很简单的就搞定了:


list.stream().foreach(item->doSomeThing(item));


我用方法引用再简化一下上面的代码,就变成了现在这个样:


list.stream().foreach(this:: doSomeThing);


很牛逼是不是!


HashMap的优化


  • 如果在 hash冲突 的时候,链表长度大于默认因子数8的时候,会变更为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,用以提高效率。


  • JDK1.7 rehash 的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,JDK1.8不会倒置


详细的优化请参考美团技术团队写的这篇文章:


Java 8系列之重新认识HashMap

http://tech.meituan.com/java-hashmap.html


concurrentHashMap的优化


concurrentHashMap 变成了 cas无锁模式,只有在扩容的时候才会用 sync 进行锁,cas的无锁模式 使得 concurrentHashMap 在吞吐量上有了一定等级的提升。


JVM方面的优化


内存模型换成红黑树,有利于gc,减少内存泄露。


java内存分带进行了改进,取消了永久代,变成了metaSpace


内存分带改进metaSpace


Java8内存分带的改进之后带来哪些优势?


你的 metaSpace 使用了机器的堆内存,metaSpace 是自动扩展的,但是你可以给他设置一个固定的最大容量,但是其实你是无需关系他的大小,让你不用像以前一样担心 permGen 溢出的问题。


在永久代被发明的时候那时候还没有 spi、osgi 这类的动态类加载机制,所以一个类一旦被JVM加载之后他就一直在内存之中,一直到JVM结束运行才会释放(其实这个时候说道释放也没有什么意义了),而现在的阶段类在JVM的生命周期变得不确定了,可以灵活的加载和释放,所以 Java 8 中把永久代变为 metaSpace 的意义其实也是为了应对现在类机制灵活的变化。


metaSpace的缺点


上面我们谈到了内存分带改进的好处,但是 metaSpace 也有他的缺点:


他的缺点是什么呢?


  • 拿class实例来讲,无论是永久代还是metaSpace都会存在类加载泄露的风险.唯一的区别是metaSpace的默认设置(自动调整metaSpace空间大小),这个看来是改进的地方,会让你在类加载泄露的问题变得更难发现。


  • 另一方面,如果metaSpace的东西占用的空间更大了之后,他是有可能耗尽操作系统的内存,这种情况的发生比耗尽JVM永久代的后果更严重,虽然你可以设置一个metaSpace的最大值,但是这个最大值的大小又会变成调优的另一个新问题了。


无论你是在 JVM 里使用 metaSpace 还是永久代,如果你正在使用动态类卸载,你应该采取措施来检测和防止类加载泄露。


参考的文章:


What is the difference between PermGen and Metaspace?

http://stackoverflow.com/questions/27131165/what-is-the-difference-between-permgen-and-metaspace






如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。


欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值