一、Java8的新特性
1.Java8的概述
- Java8是Java语言的一个重要版本,该版本与2014年3月发布,是自Java5以来最具革命性的版本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
2.函数式接口
- 函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等。
- Java8提供@Functionallnterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。
- Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
接口名称 | 方法声明 | 功能介绍 |
---|---|---|
Consumer | void accept(T t) | 根据指定的参数执行操作 |
Suppelier | T get() | 得到一个返回值 |
Function<T,R> | R apply(T t) | 根据指定的参数执行操作并返回 |
Predicate | boolean test(T t) | 判断指定的参数是否满足条件 |
3.Lambda表达式
- Lambda表达式是实例化函数式接口的重要方式,使用Lambda表达式可以使代码变得更加简洁紧凑。
- Lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以使表达式,也可以是语句块。
- 语法格式:(参数列表)->{方法体} -其中()、参数类型、{}以及return关键字可以省略。
4.方法引用
- 方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
- 方法引用使用一对冒号::将类或对象与方法名进行连接,通常使用如下:
* 对象的非静态方法引用 ObjectName::MethodName
* 类的静态方法引用 ClassName::StaticMethodName
* 类的非静态方法应用 ClassName::MethodName
* 构造器的引用 ClassName::new
* 数组的引用 TypeName[]::new - 方法引用实在特定场景下Lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。
函数式接口、Lambda表达式、方法引用代码示例:
Person类:
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
void show(){
System.out.println("没事出来秀一下哦");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//排序规则
@Override
public int compareTo(Person o){
return getName().compareTo(o.getName());
}
}
MethodReferenceTest类:
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceTest {
public static void main(String[] args) {
//1.使用匿名内部类的方式通过函数式接口Runnable中的方法实现对Person类中show方法的调用
Person person = new Person("zhangfei", 30);
new Runnable(){
@Override
public void run() {
person.show();
}
}.run();//没事出来秀一下哦
System.out.println("---------------------------------------------");
//2.使用lambda表达式的方式实现Person类中show方法的调用
Runnable runnable1 = () -> person.show();
runnable1.run();
System.out.println("---------------------------------------------");
//3.使用方法引用的方式实现Person类中show方法的调用
Runnable runnable2 = person::show;
runnable2.run();
System.out.println("---------------------------------------------");
//4.使用匿名内部类的方式通过函数式接口Consumer中的方法来实现Person类中setName方法的调用
new Consumer<String>() {
@Override
public void accept(String s) {
person.setName(s);
}
}.accept("guanyu");
System.out.println("person = " + person);//person = Person{name='guanyu', age=30}
System.out.println("---------------------------------------------");
//5.使用lambda表达式的方式实现Person类中setName方法的调用
Consumer<String> consumer1 = s -> person.setName(s);
consumer1.accept("liubei");
System.out.println("person = " + person);//person = Person{name='liubei', age=30}
System.out.println("---------------------------------------------");
//6.使用方法引用的方式实现Person类中setName方法的调用
Consumer<String> consumer2 = person::setName;
consumer2.accept("zhangfei");
System.out.println("person = " + person);//person = Person{name='zhangfei', age=30}
System.out.println("---------------------------------------------");
//7.使用匿名内部类的方式通过函数式接口Supplier中的方法来实现Person类中getName方法的调用
Supplier<String> supplier = new Supplier<>() {
@Override
public String get() {
return person.getName();
}
};
System.out.println(supplier.get());//zhangfei
//lambda表达式
Supplier<String> supplier1 = () -> person.getName();
System.out.println(supplier1.get());//zhangfei
//方法引用
Supplier<String> supplier2 = person::getName;
System.out.println(supplier2.get());//zhangfei
System.out.println("---------------------------------------------");
//8.使用匿名内部类的方式通过函数式接口Function中的方法实现Integer类中parseInt方法的调用
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
};
System.out.println(function.apply("12345"));//12345
//lambda接口
Function<String, Integer> function1 = s -> Integer.parseInt(s);
System.out.println(function1.apply("12345"));//12345
//方法引用
Function<String, Integer> function2 = Integer::parseInt;
System.out.println(function2.apply("12345"));//12345
System.out.println("---------------------------------------------");
//9.使用匿名内部类的方式通过函数式接口Comparator中的方法实现Integer类中compare方法的调用
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(comparator.compare(10, 20));//-1
//lambda
Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comparator1.compare(10,20));//-1
//方法引用
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(10, 20));//-1
System.out.println("---------------------------------------------");
//10.使用匿名内部类的方式通过类名来调用非静态方法
//其中一个参数对象作为调用对象来调用方法时,可以使用上述方式 更抽象
Comparator<Integer> comparator3 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator3.compare(10, 20));//-1
//lambda
Comparator<Integer> comparator4 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator4.compare(10, 20));//-1
//方法引用
Comparator<Integer> comparator5 = Integer::compareTo;
System.out.println(comparator5.compare(10, 20));//-1
System.out.println("---------------------------------------------");
//11.使用匿名内部类的方式通过Supplier函数式接口创建Person类型的对象并返回
Supplier<Person> supplier3 = new Supplier<Person>(){
@Override
public Person get() {
return new Person();
}
};
System.out.println(supplier3.get());//Person{name='null', age=0}
//lambda
Supplier<Person> supplier4 = () -> new Person();
System.out.println(supplier4.get());//Person{name='null', age=0}
//方法引用
Supplier<Person> supplier5 = Person::new;
System.out.println(supplier5.get());//Person{name='null', age=0}
System.out.println("---------------------------------------------");
//12.使用匿名内部类的方式通过BiFunction函数式接口采用有参方式创建Person类型的对象并返回
BiFunction<String, Integer, Person> biFunction = new BiFunction<String, Integer, Person>() {
@Override
public Person apply(String s, Integer integer) {
return new Person(s, integer);
}
};
System.out.println(biFunction.apply("zhangfei",30));
//lambda
BiFunction<String, Integer, Person> biFunction1 = (s,integer) ->new Person(s, integer);
System.out.println(biFunction1.apply("zhangfei", 30));
//方法引用
BiFunction<String, Integer, Person> biFunction2 = Person::new;
System.out.println(biFunction2.apply("zhangfei", 30));
System.out.println("---------------------------------------------");
//15.使用匿名内部类的方式通过Function函数式接口创建指定数量的Person类型的对象数组并返回
Function<Integer, Person[]> function3 = new Function<Integer, Person[]>() {
@Override
public Person[] apply(Integer integer) {
return new Person[integer];
}
};
Person[] pArr = function3.apply(3);
System.out.println(Arrays.toString(pArr));//[null, null, null]
//lambda
Function<Integer, Person[]> function4 = integer -> new Person[integer];
System.out.println(Arrays.toString(function4.apply(4)));//[null, null, null, null]
//方法引用
Function<Integer, Person[]> function5 = Person[]::new;
System.out.println(Arrays.toString(function5.apply(5)));//[null, null, null, null, null]
}
}
5.Stream接口
- 案例题目:
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打
印出来
ListPersonTest方法:
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ListPersonTest{
public static void main(String[] args){
//1.准备一个List集合并放入Person类型的对象后打印
List<Person> list = new LinkedList<Person>();
list.add(new Person("zhangfei",30));
list.add(new Person("xiaoqiao",17));
list.add(new Person("zhouyu",20));
list.add(new Person("zhangfei", 30));
list.add(new Person("guanyu", 35));
list.add(new Person("liubei", 40));
for(Person tp : list){
System.out.println(tp);
}
System.out.println("--------------------------------------");
//2.将List集合中所有的成年人过滤出来并放入另一个集合中打印
List<Person> list1 = new Linkedlist<Person>();
for(Person tp : list){
if(tp.getAge() >= 18){
list1.add(tp);
}
}
for(Person tp : list1){
System.out.println(tp);
}
System.out.println("--------------------------------------");
//3.使用stream接口实现上述功能
list.stream().filter(new Predicate<Person>(){
@Override
public boolean test(Person person){
return person.getAge() >= 18;
}
}).forEach(new Consumer<Person>(){
@Override
public void accept(Person person){
System.out.println(person);
}
});
System.out.println("--------------------------------------");
//4.使用lambda表达式对上述代码进行优化
list.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);
System.out.println("--------------------------------------");
//5.对集合中元素通过流跳过2个元素后再取3个元素后打印
list.stream().skip(2).limit(3).forEach(System.out::println);
System.out.println("--------------------------------------");
//6.实现集合中所有元素中的年龄获取出来并打印
list.stream().map(new Function<Person, Integer>(){
@Override
public Integer apply(Person person){
return person.getAge();
}
}).forEach(System.out::println);
System.out.println("--------------------------------------");
//7.实现集合中所有元素的自然排序并打印
list.stream().sorted.forEach(System.out.pringln);//需要在Person类中增加自然排序规则
//8.判断集合中是否没有元素的年龄是大于45岁的
boolean b1 = list.stream().noneMatch(new Predicate<Person>(){
@Override
public boolean test(Person person){
return person.getAge() > 45;
}
});
System.out.println("b1 = " + b1);
b1 = list1.stream().noneMatch(person -> person.getAge() > 45);
System.out.println("b1 = " + b1);
System.out.println("--------------------------------------");
//9.按照指定的比较器规则获取集合中所有元素的最大值
Optional<Person> max = list.stream().max(new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2){
return o1.getAge() - o2.getAge()
}
});
System.out.println("按照年龄排序后的最大值是:" + max);
max = list.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println("按照年龄排序后的最大值是:" + max);
System.out.println("--------------------------------------");
//10.实现将集合中所有元素映射出来并进行累加后打印
Optional<Integer> reduce = list.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>(){
@Override
public Integer apply(Integer integer, Integer integer2){
return integer + integer2;
}
});
System.out.println("最终所有年龄的累加和是:" + reduce);//172
reduce = list.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println("最终所有年龄的累加和是:" + reduce);//172
System.out.println("--------------------------------------");
//11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
list.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
}
}
(1)基本概念
- java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选
等操作。 - Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两
种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
(2)使用步骤
- 创建Stream,通过一个数据源来获取一个流。
- 转换Stream,每次转换返回一个新的Stream对象。
- 对Stream进行聚合操作并产生结果
(3)创建方式
- 方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
- 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
- 方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
- 方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T>
s)
(4)中间操作
- 筛选与切片的常用方法如下:
方法声明 | 功能介绍 | 详情 |
---|---|---|
Stream filter(Predicate<? super T> predicate) | 返回一个包含匹配元素的流 | 筛选过滤。会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。 |
Stream distinct() | 返回不包含重复元素的流 | 去重。它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流 |
Stream limit(long maxSize) | 返回不超过给定元素数量的流 | 截断。它会返回一个不超过给定长度的流,如果流是有序的,则最多返回前n个元素 |
Stream skip(long n) | 返回丢弃前n个元素后的流 | 跳过。返回一个扔掉了前n个元素的流 |
- 映射常用方法如下:
方法声明 | 功能介绍 |
---|---|
Stream map(Function<? super T,? extends R> mapper) | 返回每个处理过元素组成的流 |
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) | 返回每个被替换过元素组成的流,并将所有流合成一个流 |
- 排序的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Stream sorted() | 返回经过自然排序后元素组成的流 |
Stream sorted(Comparator<? super T> comparator) | 返回经过比较器排序后元素组成的流 |
(5)终止操作
- 匹配与查找的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Optional findFirst() | 返回该流的第一个元素 |
boolean allMatch(Predicate<? super T> predicate) | 返回所有元素是否匹配 |
boolean noneMatch(Predicate<? super T> predicate) | 返回没有元素是否匹配 |
Optional max(Comparator<? super T> comparator) | 根据比较器返回最大元素 |
Optional min(Comparator<? super T> comparator) | 根据比较器返回最小元素 |
long count() | 返回元素的个数 |
void forEach(Consumer<? super T> action) | 对流中每个元素执行操作 |
- 规约的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Optional reduce(BinaryOperator accumulator) | 返回结合后的元素值 |
List<Integer> numList = Arrays.asList(1,2,3,4,5);
int result = numList.stream().reduce((a,b) -> a + b ).get();
System.out.println(result);
- 上述代码实现了对numList中的元素累加。lambada表达式的a参数是表达式的执行结果的缓存,也就是表达式这一次的执行结果会被作为下一次执行的参数,而第二个参数b则是依次为stream中每个元素。如果表达式是第一次被执行,a则是stream中的第一个元素。
收集的常用方法如下:
方法声明 | 功能介绍 |
---|---|
<R,A> R collect(Collector<? super T,A,R> collector) | 使用收集器对元素进行处理 |
6.Optional类
- 案例题目
判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。
import java.util.Optional;
import java.util.function.Function;
public class OptionalTest {
public static void main(String[] args) {
// String str1 = "hello";
String str1 = null;
if (null != str1){
System.out.println("字符串的长度是: " + str1.length());//5 空指针异常
} else {
System.out.println("字符串为空,因此长度为0!");
}
System.out.println("------------------------------------------------");
//Java8中使用Optional类实现空值的处理
//1.将数据str1装到Optional对象代表的容器中
Optional<String> optional = Optional.ofNullable(str1);
//2.建立映射关系 使用字符串的长度与字符串建立映射关系
/*Optional<Integer> integer = optional.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});*/
// Optional<Integer> integer = optional.map(s -> s.length());
Optional<Integer> integer = optional.map(String::length);
//3.若字符串为空则打印0,否则打印字符串的数值
System.out.println("integer = " + integer);//Optional.empty
System.out.println(integer.orElse(0));//0
}
}
(1)基本概念
- java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在
或不存在。 - 该类的引入很好的解决空指针异常,不用显式进行空值检测。
(2)常用的方法
方法声明 | 功能介绍 |
---|---|
static Optional ofNullable(T value) | 根据参数指定数值来得到Optional类型的象 |
Optional map(Function<? super T,? extends U>mapper) | 根据参数指定规则的结果来得到Optional类型的对象 |
T orElse(T other) | 若该值存在就返回,否则返回other的数值。 |
二、Java9的新特性
1.Java9的概述
- Java9发布于2017年9月发布,带来了很多新特性,其中最主要的变化是模块化系统。
- 模块就是代码和数据的封装体,模块的代码被组织成多个包,每个包中包含Java类和接口,模块的
数据则包括资源文件和其他静态信息
2. 模块化的使用
(1)语法格式
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,具体如下:
module 模块名称 {
}
(2)模块化的优势
- 减少内存的开销。
- 可简化各种类库和大型应用的 开发和维护。
- 安全性,可维护性,提高性能。
操作示例:
新建module,在其中新建Person类和 module-info.java
Person:
package com.lagou;
public class Person {
}
module-info.java:
module java9 {
//实现将com.lagou这个包暴露出去
exports com.lagou;
}
新建另一个module,在其中新建PersonTest类和module
PersonTest:
import com.lagou.Person;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
}
}
module-info.java:
module java92 {
//需要java9-01中的模块信息
requires java9;
}
3.钻石操作符的使用升级
- 在Java9中允许在匿名内部类的使用中使用钻石操作符
import java.util.Comparator;
public class DiamondTest {
public static void main(String[] args) {
//实现匿名内部类和钻石操作符的搭配使用
// Comparator<Integer> comparator = new Comparator<Integer>() {
Comparator<Integer> comparator = new Comparator<>() {//钻石操作符可以省略后面的泛型
@Override
public int compare(Integer o1, Integer o2) {
return 0;
}
};
}
}
4.集合工厂方法
(1)基本概念
- Java9的List、Set和Map集合中增加了静态工厂方法of实现不可变实例的创建。
- 不可变体现在无法添加、修改和删除它们的元素。
- 不允许添加null元素对象。
(2)实际意义
- 保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率。
- 被不可信的类库使用时会很安全。
- 如果一个对象不需要支持修改操作,将会节省空间和时间的开销。
- 可以当作一个常量来对待,并且这个对象在以后也不会被改变
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionTest {
public static void main(String[] args) {
//1.创建List类型的不可变实例
List<Integer> list = List.of(1, 2, 3, 4, 5);
//list.add(6);//编译ok,运行发生UnsupportedOperationException不支持此操作的异常
System.out.println(list);
Set<Integer> set = Set.of(6, 7, 8);
//set.add(null);//编译ok,运行发生UnsupportedOperationException不支持此操作的异常
Map<Integer, String> map = Map.of(1, "one", 2, "two");
map.put(3, "three");//编译ok,运行发生UnsupportedOperationException不支持此操作的异常
}
}
5.InputStream的增强
- InputStream类中提供了transferTo方法实现将数据直接传输到OutputStream中。
import java.io.*;
public class InputStreamTest {
public static void main(String[] args) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream("d:/a.txt");
outputStream = new FileOutputStream("d:/b.txt");
inputStream.transferTo(outputStream);//实现数据的复制,底层是read和write方法的调用
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
三、Java10的新特性
1.Java10的概述
- Java10于2018年3月发布,改进的关键点包括一个本地类型推断、一个垃圾回收的增强。
- Java10计划只是一个短期版本,因此公开更新将在六个月内结束,9月份发布的Java11将是Java的
长期支持(LTS)版本,LTS版本的发布每三年发布一次
2.局部变量类型推断
(1)基本概念
- Java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索
引,以及传统for循环的本地变量。 - 它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其
他类型的变量声明。
(2)实际意义
- 标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不
会受到影响,但var不能作为类或则接口的名字。 - 避免了信息冗余。
- 对齐了变量名。
- 更容易阅读。
四、Java11的新特性
1.Java11的概述
Java11于2018年9月正式发布,这是 Java 大版本周期变化 后的第一个长期支持版本,非常值得关
注。
2.简化的编译运行操作
- 在Java11中可以使用java命令一次性进行编译和运行操作。
- 执行源文件中的第一个类必须包含主方法。
- 不可以使用其它源文件中自定义的类。
3.String类新增方法
方法声明 | 功能介绍 |
---|---|
boolean isBlank() | 判断字符串是否为空或只包含空白代码点 |
Optional map(Function<? super T,? extends U>mapper) | 根据参数指定规则的结果来得到Optional类型的对象 |
T orElse(T other) | 若该值存在就返回,否则返回other的数值。 |
五、总结
1.新特性
函数式接口、lambda表达式、方法引用、Stream流、Optional类、模块化系统、钻石操作符的增强、集合的工厂方法
Inputstream类的增强、局部变量的类型推断、简化的编译运行、String类新增的方法等