Java8新特性

Java8(又称为JDK.8)是2014年3月发布的一个重要版本,新增了许多重要特性,如Lambda表达式,方法引用,函数式接口,Stream API,Date Time API,新工具等。下面将简要介绍。

Lambda表达式

在使用1.8以上级别的编译器时,如果可以使用Lambda表达式,IDE会有提示。举个栗子如下:

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world!");
            }
        });
        t1.start();

//        Thread t2 = new Thread(() -> System.out.println("hello world!"));
//        t2.start();
    }

Thread构造方法里面的参数可以使用lambda表达式,即() -> System.out.println(“hello world!”)

不是所有的接口实例都能用lambda表达式来代替。当接口只有一个抽象方法时,该接口实现才能用lambda表达式代替,这样的接口也叫函数式接口。

lambda表达式的由来

假设我们按照最原始的写法,那就是定义一个实现了Runnable接口的类并复写run方法。然后实例化一个该类的对象,将其传给Thread类的构造方法,用来生成一个线程对象。

这一套写起来非常麻烦,而且这个Runnable接口的实现类,仅仅是为了用来构造线程对象,没有复用的需求。 因此,针对这种情况有了匿名类的出现,直接在使用的时候来实例化一个没有名字的类,减少了不少的工作量。

如果一个接口只有一个抽象方法,那么用匿名类的方式来实例化该接口,写起来还是会感觉有点多余,就比如上面的Runnable接口的实例化。于是lambda表达式就出现了。

lambda表达式的本质是一个接口的实例,而在表现形式上,lambda表达式一个没有方法签名,只有方法实现的匿名函数。为什么只有方法实现就能表示一个接口实例呢?因为既然这个lambda表达式出现在这里,其实现的接口和复写的方法就能确定了。比如上面的例子,() -> System.out.println(“hello world!”) 这个lambda表达式,显然代表一个Runnable接口实例,复写了run方法。

lambda表达式的规则

Lambda表达式也可称为闭包,允许把函数作为一个方法的参数(函数作为参数传递进方法中),使得代码变得更加简洁、紧凑。

从表现形式上看,Lambda表达式就是一个匿名函数,(a,b,…)表示参数列表,->表示连接符,{}内部是方法体。

Lambda表达式规则:

  • 如果形参列表为空,只需保留()即可;如果形参只有一个,()可以省略,只需要参数名称即可。
  • 如果方法体只有一句且无返回值,{}可以省略。
  • 形参列表的数据类型会自动推断。
  • Lambda表达式不会生成单独的内部类文件。
  • Lambda表达式若访问了局部变量,则局部变量必须是final的,若是没加final关键字,系统会自动添加,此后该局部变量不能修改,否则会报错。

方法引用

上文说了lambda表达式,下面看这样一个例子:

 public static void main(String[] args) {
    String[] strs = {"b","a","c"};
    Arrays.sort(strs, (s1, s2) -> s1.compareToIgnoreCase(s2));
}

显然,sort方法的第二个参数使用了lambda表达式,等价于:

public static void main(String[] args) {
    String[] strs = {"b","a","c"};
    Arrays.sort(strs, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    });
}

Lambda表达式允许将函数作为形参传递到方法中,但这里还有一种更简单的方式,即方法引用:

public static void main(String[] args) {
    String[] strs = {"b","a","c"};
    Arrays.sort(strs, String::compareToIgnoreCase);
}

方法引用是用来直接访问类或者实例的已经存在的方法,仅仅是引用而不执行。

当lambda表达式中只是执行一个方法调用时,将其写成方法引用的形式可读性更高。方法引用仍然是lambda表达式,方法引用的操作符是双冒号"::"。简单地说,就是当lambda表达式仅仅调用一个已存在的方法而不做其他任何事时,可以通过方法名称引用这个方法。方法引用是一个更加紧凑,易读的lambda表达式。

方法引用类型

方法引用有四种类型,分别是:

  • 引用静态方法。
  • 引用实例方法。
  • 引用某个类型的任意对象的实例方法。
  • 引用构造方法。

如果将方法引用展开为基本的Java写法,方法是通过类调用的就是静态方法引用,通过具体对象实例调用的就是实例引用,通过任意对象调用的就是任意实例引用,通过构造方法调用的就是构造方法引用。

如上面的方法引用String::compareToIgnoreCase,其展开后的调用是:s1.compareToIgnoreCase(s2)。这里s1不是某个具体的对象实例,而是String类型的任意对象,故属于任意实例引用(写法跟静态方法引用一样,但不是静态方法引用)。

构造方法引用的写法有点特殊,不是nameSpace::methodName的形式,而是namespace::new

Stream API

上文中介绍函数引用时举了一个栗子:

public static void main(String[] args) {
    String[] strs = {"b","a","c"};
    Arrays.sort(strs, String::compareToIgnoreCase);
}

遍历排序后的字符串数组,可以通过for循环的方式。而Java8中提供了一种新的方法:

Arrays.stream(strs).forEach(System.out::println);

Stream是Java函数式编程的重要API。Stream并不是某种数据结构,而是数据源的一种视图。这里的数据源可以是一个数组,Java容器或者I/O channel等。

常见的Stream接口继承关系图(引用自参考资料1):
这里写图片描述
IntStream,LongStream和DoubleStream对应三种基本(非包装类)类型的的视图,Stream对应所有剩余类型的视图。

stream特点

  1. 无存储。stream不是一种数据结构,它只是某种数据源的一个视图。
  2. 用于函数式编程。对stream的任何修改都不会修改实际的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是产生一个过滤后的stream视图。
  3. 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  4. 可消费性。Stream只能被消费一次,一旦遍历过就会失效,就像容器的迭代器一样,想要再次遍历必须重新生成。

详细介绍这里就不展开了,具体见参考资料1。

Date Time API

Java8中引入了新的时间日期类的API,可参考博主之前的文章-JAVA8日期API

参考资料

1.http://www.cnblogs.com/CarpenterLee/p/6545321.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值