Java 8由Oracle从2014年3月18日发布,此版本是自Java 5(发布于2004年)之后的一个重量级版本,也是java发展史上的一个里程碑式的版本。这个版本在JVM、编译器、库、Java语法特性等方面都做了很大改进,同时在语言的表达力、简洁性等个方面也有了很大的提高。目前几乎所有的JAVA框架也都已升级到支持JDK8,打开框架源码想了解其设计,假如不理解JDK8的这些特性看起来就会非常吃力。所以我们设计了这个专题,我们将在这个专题中讲解JDK8中的部分关键特性,并用实际案例讲解这些特性应用,希望同学们在JDK8技术的应用上有一个很好的提高。
Java 8这个版本提供了很多实用的新特性,针对接口推出了接口默认方法,接口静态方法以及函数式接口,同时为了简化代码编写,推出了lambda表达式,为了增强对数据的操作,还定义了Stream操作等。这个版本目前是市场上一个应用最广泛,也是最重要的一个版本。
JDK8中对接口规范进行了新的定义,允许在接口中定义默认方法(使用default关键字修饰),静态方法,同时还推出了函数式接口(使用@FunctionInterface注解描述)设计。
基于JDK8中接口新规范的定义,不仅可以扩展接口新功能(新的标准),还能保持接口向前兼容的特性。例如Java API中的集合操作规范。
JDK8中为了对接口业务进行扩展,但又不影响实现类,提供了默认方法。此类型的方法使用default关键字修饰,可以有方法体的实现。例如:
interface IA{
default void doMethod01() {
System.out.println("doMethod01");
}
default void doMethod02() {
System.out.println("doMethod02");
}
}
接口默认方法提高了编程的灵活度,一个类在实现接口时,接口中假如有默认方法,默认方法可以有选择性的对其进行重写,但不是必须要重写,例如:
class ClassA implements IA{
}
接口默认方法,解决了java8和以前接口版本特性的兼容性问题,对于我们以后的程序开发,可以在接口子类中直接使用接口默认方法,而并不再需要在各个子类中都去实现这些方法了。
案例展示(一):
package com.cy.java8.interf;
/**
* 抽象方法添加的时候,子类不重写的话会出错
* 默认方法的好处:扩展方法的时候,不会出现其他的错误,向前考虑
* @author lixin
*/
//接口
interface IA {
//默认方法
default void doMethod01() {
System.out.println("doMethod01()...");
}
default void doMethod02() {
System.out.println("doMethod02()...");
}
void doMethod03();//抽象方法
}
class ClassA implements IA{
//默认方法的重写
@Override
public void doMethod01() {
// TODO Auto-generated method stub
IA.super.doMethod01();//需要注意的地方
System.out.println("ClassA.doMethod01()...");
}
//普通方法的重写
@Override
public void doMethod03() {
System.out.println("doMethod03()...");
}
}
public class TestInterfaceDefaultMethod01 {
public static void main(String[] args) {
IA a1 = new ClassA();
a1.doMethod01();
a1.doMethod03();
}
}
案例展示(二):
package com.cy.java8.interf;
interface IC {
//抽象方法
default void doMethod01() {
};
default void doMethod02() {
};
}
@Deprecated //提示过时的类
class AbstractICAdapter implements IC {
//钩子方法,早期的写法
@Override
public void doMethod01() {
}
@Override
public void doMethod02() {
}
}
//class ClassC implements IC{}
class ClassC extends AbstractICAdapter{
@Override
public void doMethod01() {
// super.doMethod01();
System.out.println("doMethod01()...");
}
}
public class TestInterfaceDefaultMethod02 {
public static void main(String[] args) {
IC c1 = new ClassC();
c1.doMethod01();
}
}
Java8中的接口规范,不仅允许定义多个默认方法,也允许在接口中定义多个静态方法,这些静态方法类似于 class 中的静态方法,可以通过接口名进行直接调用。
interface IB{
static void doMethod() {
System.out.println("doMethod()");
}
}
接口中静态方法并不能在实现类中被覆写,实现类中可以声明相同的方法,但这两个方法之间除了名字相同,并没有 override 关系。
案例展示:
package com.cy.java8.interf;
//接口里面的静态方法
interface IStatic{
public static void doPrint(){
System.out.println("IStatic.doPrint()");
}
}
class ClassStatic implements IStatic{
public static void doPrint(){
System.out.println("ClassStatic.doPrint()");
}
}
public class TestInterfaceStaticMethod01 {
public static void main(String[] args) {
IStatic s1 = new ClassStatic();
// s1.doPrint();
IStatic.doPrint();//IStatic.doPrint()
ClassStatic s2 = new ClassStatic();
s2.doPrint();//ClassStatic.doPrint()
ClassStatic.doPrint();//正确的调用方式
}
}
Java8引入了一个是函数式接口(Functional Interfaces),此接口使用 @FunctionalInterface修饰,并且此接口内部只能包含一个抽象方法。
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2); // public abstract
}
说明:函数式接口推出的目的主要是为了配合后续Lambda表达式的应用。
案例展示:
package com.cy.java8.interf;
/**
* @FunctionalInterface 描述的接口为函数式接口,此接口内部只允许有一个抽象方法
* @author lixin
*
*/
@FunctionalInterface
interface Fun{
void doMethod01();
//函数式接口中只允许有一个方法是抽象的
// void doMethod02();
default void doMethod02() {
}
}
public class TestFunctionInterface01 {
public static void main(String[] args) {
new Fun() {
@Override
public void doMethod01() {
// TODO Auto-generated method stub
System.out.println("doMethod01()");
}
}.doMethod01();
}
}
在JDK中的java.util.function包中定义了大量函数式接口。常用的有如下几个:
(1)消费型接口(特点:方法有入参,没有返回值)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
(2)函数式接口(特点:方法有入参,有返回值)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
...
}
(3)判定式接口(特点:方法有入参,返回值为boolean)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
...
}
(4)供给式接口(特点:方法没有入参,但有返回值)
@FunctionalInterface
public interface Supplier<T> {
T get();
...
}
说明:这些接口可以配合Lambda使用以减少我们自己接口的定义。
案例展示:
package com.cy.java8.interf;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class TestFunctionInterface02 {
public static void main(String[] args) {
//1.消费型接口
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String t) {
// TODO Auto-generated method stub
System.out.println(t);
}
};
consumer.accept("hello world");
//2.函数式接口
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String t) {
// TODO Auto-generated method stub
return Integer.parseInt(t);
}
};
Integer result = function.apply("100");
System.out.println(result);
//3.判定型接口
Predicate<String> predicate = new Predicate<String>() {
List<String> list = Arrays.asList("A","B","C");
@Override
public boolean test(String t) {
// TODO Auto-generated method stub
return list.contains(t);
}
};
System.out.println(predicate.test("A"));
//4.供给型接口
Supplier<Object> supplier = new Supplier<Object>() {
@Override
public Object get() {
// TODO Auto-generated method stub
return new Object();
}
};
System.out.println(supplier.get());
//...
}
}
Java中的Lambda表达式是JDK8中的一种新特性,它允许我们将一段代码(这段代码可以理解为一个接口的实现)当成参数传递给某个方法,然后进行业务处理,这种方式更像是一种函数式编程风格,可以让代码结构更简洁,开发效率更高。
Java中的Lambda为JAVA编程注入了函数式编程思想,在迭代操作,映射操作,聚合操作等很多方面的实现上做出了很大努力。并从语法角度简化了程序员对特定代码的编写,通过底层的类型推断,方法引用等特性让代码表现的更加优雅。现在已成为我们编程过程中常用的一种编程方式。
最简单的Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成,例如:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
在上面这个代码中的参数e的类型是由编译器推理得出的,你也可以显式指定该参数的类型,例如:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
案例展示:
package com.cy.java8.lambda;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
public class TestLambda01 {
public static void main(String[] args) {
List<String> list = Arrays.asList("A","B","C");
for (String s : list) {
System.out.println(s);
}
System.out.println("=========");
list.forEach(new Consumer<String>() {
//匿名内部类
@Override
public void accept(String t) {
// TODO Auto-generated method stub
System.out.println(t);
}
});
System.out.println("====lambda表达式写法===");
list.forEach((s)->System.out.println(s));
}
}
如果Lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来,类似于Java中的函数体,例如:
Arrays.asList( "a", "b", "d" ).forEach( e -> {
System.out.print( e );
System.out.println( );
} );
lambda 表达式可以让代码编写更加简洁。我们先来思考下普通的函数或方法具备的几个元素:
在lambda 表达式应用过程中,你应该也注意到了,一般只有两个元素:
(parameter list) -> body
其中“->”
将参数列表与函数主体分离,旨在对给定参数进行处理。函数的主体可能是一条或多条语句。例如其常见结构如下:
() -> statement
arg -> statement
(arg1, arg2, ...) -> {
body-block }
(Type1 arg1, Type2 arg2, ...) -> {
method-body-block;return value; }
Lambda表达式有返回值,返回值的类型也由编译器推理得出。如果Lambda表达式中的语句块只有一行,则可以不用使用return语句,下列两个代码片段效果相同:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
案例展示:
package com.cy.java8.lambda;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class TestLambda02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> list = Arrays.asList("C","B","A");
//匿名内部类
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return o1.compareTo(o2);
}
});
System.out.println(list);
System.out.println("====lambda表达式====");
list.sort((o1,o2) -> o1.compareTo(o2));
System.out.println(list);
}
}
结合对象lambda表达式基本语法的认识,分析如下案例:
构建一个线程对象,执行Runnable类型的任务,传统方式的实现,其关键代码如下:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
}).start();
基于JDK8中的Lambda表达式实现方式,对传统方式线程对象的创建进行简化,其关键代码如下:
new Thread(()->{
System.out.println("hello");
}).start();
案例展示:
package com.cy.java8.lambda;
public class TestLambda03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Execute task");
}
}).start();
//=====lambda=====
new Thread(()->System.out.println("execute task")).start();
}
}
定义一字符串数组,然后对字符串数组中的内容,按字符串元素的长度对其进行排序。代码如下:
String[] strArray= {
"abcd","ab","abc"};
在JDK8之前传统的实现方案,基于Arrays类对数组中的元素进行排序操作,关键代码实现如下:
Arrays.sort(strArray,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
基于JDK8中的Lambda表达式,对排序如上排序方案的代码实现过程进行简化,关键代码如下:
Arrays.sort(strArray, (s1, s2) -> s1.length() - s2.length());
案例展示:
package com.cy.java8.lambda;
import java.util.Arrays;
import java.util.Comparator;
public class TestLambda04 {
public static void main(String[] args) {
String[] strArray = {
"b","DFEL","bcd"};
Arrays.sort(strArray,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
System.out.println("======Lambda表达式======");
Arrays.sort(strArray,(String o1,String o2)->{
return o1.length()-o2.length();
});
Arrays.sort(strArray,(o1,o2)->o1.length()-o2.length());
}
}
下篇预告:
- 方法
- Stream式操作应用
- 日期对象的应用