java8发布已经有几年了,如今java11已经出来了,在这里谈java8有点过时,不过java8确实是一个改变比较大的版本,在这里简单分析一下。
JDK8新增内容大致分为如下内容:
1:接口的static,default方法
2:函数式接口
3:labmda语法
4:方法引用
5:重新定义了时间类
6:stream操作数据集合类
7:其他内容
1:接口里的static,default方法
我们都知道,在1.8之前的jdk版本中,接口里面只有有抽象方法。但是现在我说接口里面有具体的实现方法,你会不会觉得我说的有问题?
但是,请看JDK1.8中如下代码:
java.util.list 接口的方法:
default void replaceAll(UnaryOperator operator) {
Objects.requireNonNull(operator);
final ListIterator li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
java.util.Map 接口的方法:
public static , V> Comparator> comparingByKey() {
return (Comparator> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
有人看到这里会说:”我X,JDK1.8是不是有bug,接口里面有实现方法,这不是颠覆我的三观吗?”
是的,JDK1.8中,确实在接口中加入了static和default的实现方法。
1:static方法属于接口,不能被子类重写。
2:default可以被子类重写。
抛出问题:
1:JDK1.8是一个新增内容比较多的版本,如果在接口里面新增抽象方法,那么他们的所有子类都需要重写该方法,那么多子类需要去实现,这个工作量是不是很大?
2:即使接口加了抽象方法,但是有些子类不想重写接口方法,有些子类需要重写接口方法,这可怎么办才好?
解决方案
好吧,既然这样的话
重写定义接口规范:
1:接口里面加入static实现方法,那么子类都不需要重写了。
2:加入default实现方法(这里相当于钩子方法,钩子方法在前文的模板方法里有说明),需要的重写的子类去重写接口就好。
是不是非常省事呢?这样static方法和default方法就诞生了。。。
2:函数式接口
接口大家都知道,但是什么是函数式接口呢?请看下面例子:
/**
* 只有一个抽象方法的接口:函数是接口
*/
interface FunctionInterface{
public void abstractMethod();
}
上面代码很明确:只有一个抽象方法的接口是函数式接口。
这属于jdk8的新特性,大家有没有回忆道我们之前用过的接口,如:
java.lang.Runnable:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
java.util.concurrent.Callable:
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
这两个我们经常用的线程类接口,都有且仅有一个抽象方法,所以他们都为函数式接口。
注意:
JDK1.8中新加了如下接口的注解,用来标注此接口为函数式接口(也可以不用):
@FunctionalInterface
3:lambda语法
1.8之前创建线程代码:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("异步线程");
}
}).start();
一共6行代码才能进行创建。
1.8创建线程代码
new Thread(() -> {System.out.println("异步线程");}).start();
一行代码就能进行线程的创建,这里就用到了lambda语法。是不是很方便。
lambda语法:
(参数体) -> {方法体}
注意
1:只有函数式接口才能用lambda
2:lambda语法只做抽象方法的申明(相当于子类去实现重写接口),并没有真正去执行
我们这里有个几个例子加深说明:
a:无参数无返回值的函数式接口的lambda写法
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod();
}
/**
* 测试
*/
public static void main(String []args){
/*
第一种写法
*/
FunctionInterface functionInterface = () -> {System.out.println("测试");};
functionInterface.abstractMethod();
/*
第二种写法
*/
FunctionInterface functionInterface1 = () -> System.out.println("测试");
functionInterface1.abstractMethod();
}
这里lambda语法中()并没有写参数,和abstractMethod的参数对应,{}里面没有返回和抽象方法的返回对应。
b:有一个参数有返回值的lambda语法:
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod(int a);
}
/**
* 测试
*/
public static void main(String []args){
/*
第一种写法
*/
FunctionInterface functionInterface = (a) -> {System.out.println("测试");};
functionInterface.abstractMethod(1);
/*
第二种写法
*/
FunctionInterface functionInterface2 = (a) -> System.out.println("测试");
functionInterface2.abstractMethod(1);
/*
第三种写法
*/
FunctionInterface functionInterface1 = a -> System.out.println("测试");
functionInterface1.abstractMethod(1);
}
c:有一个参数有返回值的lambda的语法:
@FunctionalInterface
interface FunctionInterface{
public int abstractMethod(int a);
}
/**
* 测试
*/
public static void main(String []args){
/*
第一种写法
*/
FunctionInterface functionInterface = (a) -> {return 1;};
functionInterface.abstractMethod(1);
/*
第二种写法
*/
FunctionInterface functionInterface2 = (a) -> 1;
functionInterface2.abstractMethod(1);
/*
第三种写法
*/
FunctionInterface functionInterface1 = a ->{return 1;};
functionInterface1.abstractMethod(1);
}
d:多个参数有返回值的lambda的语法:
@FunctionalInterface
interface FunctionInterface{
public int abstractMethod(int a,int b);
}
/**
* 测试
*/
public static void main(String []args){
/*
第一种写法
*/
FunctionInterface functionInterface = (a,b) -> {return 1;};
functionInterface.abstractMethod(1,2);
/*
第二种写法
*/
FunctionInterface functionInterface2 = (a,b) -> 1;
functionInterface2.abstractMethod(1,2);
}
可以看出来只要有一个参数,()是可以省略的,如果有返回值{}是可以省略的,这个看个人喜好吧。
4:方法引用
方法引用,只是一种lambda书写的技巧,可以让我们的lambda语法更加的简洁。
需要遵循以下规则就可以使用方法引用:
1:方法引用只能用在lambda的方法体{}中
2:方法体只有一个方法才可以使用方法引用
3:使用 :: 双冒号连接符
方法引用分为:
1:静态方法引用
2:对象方法引用
3:实例方法引用
4:构造方法引用
a:静态方法引用:
static void methodReference(){
System.out.println("测试");
}
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod();
}
public static void main(String []args){
FunctionInterface functionInterface = Demo6::methodReference;
functionInterface.abstractMethod();
//相当于下面的lambda语法
FunctionInterface functionInterface1 = () -> { Demo6.methodReference(); };
functionInterface1.abstractMethod();
}
注意:方法引用中,接口方法和参数要和被使用的静态方法的参数对应。,不然不能使用方法引用
比如:
public class Demo6 {
static void methodReference(int a,int b){
System.out.println("测试");
}
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod(int a,int b);
}
public static void main(String []args){
FunctionInterface functionInterface = Demo6::methodReference;
//相当于lambda语法: FunctionInterface functionInterface = (a,b) -> { Demo6.methodReference(a,b); };
functionInterface.abstractMethod(1,2);
}
}
静态类的方法参数和函数式接口的方法参数一一对应
b:对象方法引用
public class Demo6 {
void methodReference(){
System.out.println("测试");
}
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod();
}
public static void main(String []args){
FunctionInterface functionInterface = new Demo6()::methodReference;
//相当于lambda语法: FunctionInterface functionInterface = () - > { new Demo6().methodReference(); };
functionInterface.abstractMethod();
}
}
想要对象去引用,肯定需要先new出对象,注意点同上。
c:实例方法引用
public class Demo6 {
void methodReference(int a){
System.out.println("测试");
}
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod(Demo6 demo6,int a);
}
public static void main(String []args){
FunctionInterface functionInterface = Demo6::methodReference;
//相当于lambda语法: FunctionInterface functionInterface = () - > { new Demo6().methodReference(); };
Demo6 demo6 = new Demo6();
functionInterface.abstractMethod(demo6,1);
}
}
注意:
1:实例方法引用,函数式接口的抽象方法的第一个参数,必须是需要引用的对象
(public void abstractMethod(Demo6 demo6,int a); 中的 Demo6 demo6)
2:其他参数注意点和静态方法引用一样,都要一一对应。
d:构造方法引用
public class Demo6 {
public Demo6() {
}
@FunctionalInterface
interface FunctionInterface{
public void abstractMethod();
}
public static void main(String []args){
FunctionInterface functionInterface = Demo6::new;
//相当于lambda语法: FunctionInterface functionInterface = () - > { new Demo6(); };
functionInterface.abstractMethod();
}
}
用new关键字
注意点同静态方法引用,参数需要一一对应
上面的四种方法引用,其实我们完全也可以用lambda语法实现,但是使用了方法引用是不是更加方便呢?这个需要多多练习。
5:重新定义了时间类
Java 8引入了新的Date-Time API(JSR 310)来改进时间、日期的处理。时间和日期的管理一直是最令Java开发者痛苦的问题。java.util.Date和后来的java.util.Calendar一直没有解决这个问题(甚至令开发者更加迷茫)。
因为上面这些原因,诞生了第三方库Joda-Time,可以替代Java的时间管理API。Java 8中新的时间和日期管理API深受Joda-Time影响,并吸收了很多Joda-Time的精华。新的java.time包包含了所有关于日期、时间、时区、Instant(跟日期类似但是精确到纳秒)、duration(持续时间)和时钟操作的类。新设计的API认真考虑了这些类的不变性(从java.util.Calendar吸取的教训),如果某个实例需要修改,则返回一个新的对象。
新增内容如下,你们可以自行翻阅查询api,这里就不介绍其用法了。
java.time.Duration
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.ZonedDateTime
java.time.Clock
6:stream操作数据集合类
Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找、遍历、过滤以及常见计算等。
比如我们先要取出list对象里面的各个字符,并且去掉重复项:
使用stream(注意: filter,map,flatMap里面的参数都为函数式接口,所以我们直接使用lambda语法):
public static void main(String []args){
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add(null);
list.add("!!!");
String result = list.stream()
.filter(Objects::nonNull)
.map((a) -> a.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.joining(",","【","】"));
System.out.println(result);
}
结果:
【h,e,l,o,w,r,d,!】
Steam操作数据肯定是有一种状态,比如操作中(过滤,排序等)或者操作结束(返回结果,遍历等等)
stream状态分为三种:Intermediate,Terminal,Short-circuiting,具体为:
Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
stream操作比较多,篇幅有点长,小编会在后期进行具体操作演示。
7:其他内容
1:Optional类,空指针在代码中非常头疼,用optional非常有好的解决了这个空指针判断的问题。
2:Nashorn JavaScript引擎,我们可以再JVM中编写和运行JS应用。
3:对于Base64的引入,不需要我们再使用第三方库。
4:其他。