文章目录
新特性
Java8的新特性
Java8的概述
- java8是java语言的一个重要版本,该版本于2014年3月发布,是自java5以来最具革命性的版本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性
函数式接口
-
函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等
-
Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错
-
Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
接口名称 方法声明 功能介绍 Consumer<T> void accept(T t) 根据指定的参数执行操作 Supplier<T> T get() 得到一个返回值 Function<T,R> R apply(T t) 根据指定的参数执行操作并返回 Predicate<T> boolean test(T t) 判断指定的参数是否满足条件
Lambda表达式
- Lambda表达式是实例化函数式接口的重要方式,使用Lambda表达式可以使代码变得更加简洁紧凑
- Lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块
- 语法格式:(参数列表) -> {方法体;} - 其中()、参数类型、{}以及return关键字可以省略
方法引用
-
方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行
-
方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
- 对象的非静态方法引用 ObjectName(引用名) :: MethodName
- 类的静态方法引用 ClassName(类名) :: StaticMethodName
- 类的非静态方法引用 ClassName :: MethodName
使用前提:方法中的一个参数对象作为调用对象来调用方法时才可使用这种方式 - 构造器的引用 ClassName :: new
- 若是无参使用Supplier函数式接口的supply()方法调用无参构造方法
- 若是有参使用可以获得对应参数的函数式接口内部调用有参构造方法即可
- 数组的引用 TypeName[] :: new
-
方法引用是在特定场景下Lambda表达式的一种简化表示,可以进一步简化代码的编写,使代码更加紧凑简洁,从而减少冗余代码
-
注:方法引用一般要求调用函数与被调函数之间的结构差不多(参数个数、返回值差不多)
-
使用代码举例:
package com.lagou.task22; import java.util.Arrays; import java.util.Comparator; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; /** * 使用方法引用实现Lambda表达式 */ public class MethodReferenceTest { public static void main(String[] args) { //1、使用匿名内部类创建对象 Person person = new Person("张飞", 30); Consumer<String> consumer1 = new Consumer<String>() { @Override public void accept(String s) { person.setName(s); //被调用函数与调用函数accept结构相同:都是有一个参数无返回值 } }; consumer1.accept("关羽"); System.out.println("person = " + person); System.out.println("--------------------------------------"); //2、使用Lambda表达式 Consumer<String> consumer2 = s -> person.setName(s); consumer2.accept("刘备"); System.out.println("person = " + person); System.out.println("--------------------------------------"); //3、由于调用函数与被调用函数结构差不多,可以使用方法引用的方式 Consumer<String> consumer3 = person::setName; //使用方法引用的方式创建匿名内部类 consumer3.accept("张飞"); System.out.println("person = " + person); System.out.println("-----------------------------------"); //4、被调用方式是静态方法时使用方法引用的方式创建匿名内部类 //使用匿名内部类的方式通过函数式接口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 Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2); //使用Lambda表达式 System.out.println(comparator1.compare(10, 20)); //-1 Comparator<Integer> comparator2 = Integer::compare; //使用方法引用的方式 System.out.println(comparator2.compare(10, 20)); //-1 //5、使用匿名内部类的方式通过类名来调用非静态方法 //其中一个参数对象作为调用对象来调用方法时,可以使用上述方式 System.out.println("----------------------------------------"); Comparator<Integer> comparator3 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); //o1参数对象作为调用对象调用compareTo()方法 } }; System.out.println(comparator3.compare(10, 20)); //-1 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("-----------------------------------"); //6、使用匿名内部类的方式通过Supplier函数式接口创建Person类型的对象并返回 BiFunction<String, Integer, Person> biFunction = new BiFunction<String, Integer, Person>() { //使用匿名内部类 @Override public Person apply(String s, Integer age) { return new Person(s, age); } }; System.out.println(biFunction.apply("张飞", 25)); BiFunction<String, Integer, Person> biFunction1 = (s, age) -> new Person(s, age); //使用Lambda表达式 System.out.println(biFunction1.apply("关羽", 26)); BiFunction<String, Integer, Person> biFunction2 = Person::new; //使用方法引用 System.out.println(biFunction2.apply("刘备", 27)); System.out.println("-------------------------------------"); //7、使用匿名内部类的方式通过Function函数式接口创建指定数量的Person类型的对象数组并返回 Function<Integer, Person[]> function = new Function<Integer, Person[]>() { //使用匿名内部类方式 @Override public Person[] apply(Integer integer) { return new Person[integer]; } }; Person[] pArr = function.apply(3); System.out.println(Arrays.toString(pArr)); Function<Integer, Person[]> function1 = (integer) -> new Person[integer]; //使用Lambda表达式 Person[] pArr1 = function1.apply(4); System.out.println(Arrays.toString(pArr1)); Function<Integer, Person[]> function2 = Person[] :: new; //使用方法引用 System.out.println(Arrays.toString(function2.apply(5))); } }
Stream接口
- 案例题目
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打印出来
基本概念
- java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作
- Stream接口借助于Lambda表达式极大地提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器地优势
使用步骤
- 创建Stream,通过一个数据源来获取一个流
- 转换Stream,每次转换返回一个新的Stream对象(比如使用filter()函数过滤后转换为一个新Stream)
- 对Stream进行聚合操作并产生结果
创建方式
- 方式一:通过调用集合的默认方法来获取流,如:default Stream<E> stream()
- 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
- 方式三:通过Stream接口的静态方法来获取流,如:static <T> Stream<T> of(T… values)
- 方式四:通过Stream接口的静态方法来获取流,static <T> Stream<T> generates(Supplier<? extends T> s)
中间操作
-
筛选与切片的常用方法如下
方法声明 功能介绍 Stream filter(Predicate<? super T> predicate) 返回一个包含匹配元素(即有筛选条件)的流 Stream distinct() 返回不包含重复元素的流 Stream limit(long maxSize) 返回不超过给定元素数量的流 Stream skip(long 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) 返回经过比较器排序后元素组成的流
终止操作
-
匹配与查找的常用方法如下:
方法声明 功能介绍 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) 返回结合后的元素值(其实就将
所有元素结合起来进行统一处理的过程) -
收集的常用方法如下:
方法声明 功能介绍 <R,A> R collect(Collector<? super T,A,R> collector) 使用收集器对元素进行处理
代码实现举例
package com.lagou.task22;
import java.util.ArrayList;
import java.util.Comparator;
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;
import java.util.stream.Stream;
public class ListPersonTest {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
persons.add(new Person("刘备", 30));
persons.add(new Person("关羽", 28));
persons.add(new Person("张飞", 25));
persons.add(new Person("小乔", 15));
persons.add(new Person("曹操", 32));
persons.add(new Person("周瑜", 18));
//使用stream接口实现将persons集合中不满18岁的人拷贝到一个新的集合中
//1、创建stream
Stream stream = persons.stream();
//2、转换stream
//3、对stream进行聚合操作
stream.filter(new Predicate<Person>() { //转换stream,使用过滤器过滤满足要求的人员
@Override
public boolean test(Person person) {
return person.getAge() >= 18;
}
}).forEach(new Consumer<Person>() { //对stream进行聚合操作,之后遍历所有stream流中元素
@Override
public void accept(Person person) {
System.out.println(person);
}
});
System.out.println("-------------------------------------");
//使用Lambda表达式对上述代码进行优化
// persons.stream().filter(person -> person.getAge() >= 18).forEach(person -> System.out.println(person));
//使用方法引用对上述代码进行优化
persons.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);
//4、实现对集合中元素通过流跳过2个元素之后再取3个元素后打印
System.out.println("--------------------------------------");
persons.stream().skip(2).limit(3).forEach(System.out::println);
//5、实现将集合中所有元素中的年龄获取出来并打印
System.out.println("--------------------------------------");
persons.stream().map(new Function<Person, Integer>() { //使用匿名内部类的方式
@Override
public Integer apply(Person person) {
return person.getAge(); //获取流(集中)中所有元素的年龄
}
}).forEach(System.out::println);
//使用Lambda表达式
persons.stream().map(person -> person.getAge()).forEach(System.out::println);
//使用方法引用:apply()函数中的参数作为调用对象调用方法-->可以用类名调用非静态方法
persons.stream().map(Person::getAge).forEach(System.out::println);
//6、实现集合中所有元素的自然排序并打印
System.out.println("----------------------------------");
persons.stream().sorted().forEach(System.out::println); //需要在Person中实现Comparator接口否则会出现异常ClassCastException
//7、判断集合中是否没有元素的年龄>45岁
System.out.println("------------------------------------");
boolean isAge = persons.stream().noneMatch(new Predicate<Person>() { //使用匿名内部类
@Override
public boolean test(Person person) {
return person.getAge() > 45;
}
});
isAge = persons.stream().noneMatch(person -> person.getAge() > 45); //使用Lambda表达式实现
System.out.println("是否没有元素年龄>45岁:" + isAge);
//8、按照指定的比较器规则获取集合中所有元素中的最大值(年龄最大值)
Optional<Person> max = persons.stream().max(new Comparator<Person>() { //使用匿名内部类实现
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
max = persons.stream().max((o1, o2) -> o1.getAge() - o2.getAge()); //实现Lambda表达式实现
System.out.println("年龄中的最大值是:" + max);
//9、实现将集合中所有元素的年龄映射出来并进行累加后打印
System.out.println("-------------------------------------");
Optional<Integer> reduce = persons.stream().map(person -> person.getAge()).reduce(new BinaryOperator<Integer>() { //使用匿名内部类
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
reduce = persons.stream().map(Person::getAge).reduce((integer, integer2) -> integer + integer2); //使用Lambda表达式
reduce = persons.stream().map(Person::getAge).reduce(Integer::sum); //使用方法引用
System.out.println("所有元素的年龄累加和是:" + reduce);
//10、实现将集合中所有元素的姓名映射出来并手机到集合中打印
System.out.println("--------------------------------------");
persons.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
}
}
Optional类
- 案例题目:判断字符串是否为空,若不为空则打印字符串的长度,否则打印0
基本概念
- java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在或不存在
- 该类的引入很好地解决空指针异常,不用显式进行空值检测
常用方法
方法声明 | 功能介绍 |
---|---|
static Optional ofNullable(T value) | 根据参数指定数值来得到Optional类型的对象 |
Optional map(Function<? super T,? extends U> mapper) | 根据参数指定规则的结果来得到Optional类型的对象 |
T orElse(T other) | 若该值存在就返回,否则返回other的数值 |
代码实现举例
package com.lagou.task22;
import java.util.Optional;
import java.util.function.Function;
public class OptionalTest {
public static void main(String[] args) {
//使用if判断处理空指针异常问题
String str = null;
if (null != str) {
System.out.println("str = " + str);
} else {
System.out.println("字符串为空,因此长度为0!");
}
System.out.println("-----------------------------------------");
//Java8中使用Optional类实现空值的处理
//1、将数据str装到Optional对象代表的容器中
Optional<String> optional = Optional.ofNullable(str);
//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()); //使用Lambda表达式*/
Optional<Integer> integer = optional.map(String::length); //使用方法引用
//3、若字符串为空则打印0,否则打印字符串的数值
System.out.println("integer = " + integer);
System.out.println(integer.orElse(0));
}
}
Java9的新特性
Java9的概述
- java9发布于2017年9月,带来了很多新特性,其中最主要的变化是模块化系统
- 模块就是代码和数据的封装体,模块的代码被组织成多个包,每个包中包含Java类和接口,模块的数据则包括资源文件和其他静态信息
模块化的使用
语法格式
-
在module-info.java文件中,我们可以用新的关键词module来声明一个模块,具体如下:
module 模块名称 { exports 包名; //暴露包 requires 模块名; //接收模块 }
模块化的优势
- 减少内存的开销
- 可简化各种类库和大型应用的开发和维护
- 安全性、可维护性、提高性能
代码实现举例
创建一个模块名为java9_01里面放com.lagou.Person类,创建一个模块名为java9_02里面放PersonTest类。若要实现在模块java9_02中的PersonTest类中使用模块java9_01中的com.lagou.Person类,则需要先将模块java9_01中的包名暴露出来(在module-info.java中exports)给模块java9_02中使用(在module-info.java中requires)后才能import包
java9_01中的module-info.java文件:
module java9 {
//实现将com.lagou这个包暴露出去
exports com.lagou;
}
java9_02中的module-info.java文件:
module java92 {
//接收java9_01中的模块信息
requires java9;
}
钻石操作符的使用升级
- 在Java9中允许在匿名内部类的使用中使用钻石操作符。所谓钻石操作符就是泛型符号<>,泛型new对象后面可以不用写,这就是钻石操作符的使用升级。如List<Integer> list = new ArrayList<>();
集合工厂方法
基本概念
- java9的List、Set和Map集合中增加了静态工厂方法of()实现不可变实例的创建
- 不可变体现在无法添加、修改和删除它们的元素
- 不允许添加null元素对象
实际意义
- 保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率
- 被不可信的类库使用时会很安全
- 如果一个对象不需要支持修改操作,将会节省空间和时间的开销
- 可以当作一个常量来对待,并且这个对象在以后也不会被改变
InpustStream的增强
-
InputStream类中提供了tansforTo()方法将数据直接传输到OutputStream中。其底层本质还是输入流与输出流之间的复制
-
实现代码举例如下
import java.io.*; public class IOCopy { public static void main(String[] args) { OutputStream os = null; InputStream is = null; try { is = new FileInputStream("E:/FilesForTest/LaGou_Java/b.txt"); //read出来数据 os = new FileOutputStream("E:/FilesForTest/LaGou_Java/a.txt"); //write进去数据 is.transferTo(os); //实现数据的复制,底层是read和write方法的调用 } catch (IOException e) { e.printStackTrace(); } finally { if (null != os) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != is) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
Java10新特性
Java10的概述
- java10于2018年3月发布,改进的关键点包括一个本地类型推断、一个垃圾回收的增强
- Java10计划只是一个短期版本,因此公开更新将在六个月内结束,9月发布的Java11讲是Java的长期支持(LTS)版本,LTS版本的发布每三年发布一次
局部变量类型推断var
基本概念
- java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量
- 它不能使用于方法形式参数、构造函数形式参数、方法返回类型、字段、catch形式参数或任何其他类型的变量声明
实际意义
- 标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不会受到影响,但var不能作为类或者接口的名字
- 避免了信息冗余
- 对齐了变量名
- 更容易阅读
使用举例
//int i = 10;
var i = 10; //自动推断i是int类型
var list0 = new ArrayList<>(); //泛型中没有指定类型,自动推断为Object类型(没有钻石特性)
var list = new ArrayList<Integer>(); //自动推断list为Integer类型
list.add(10);
Java11新特性
Java11的概述
- java11于2018年9月正式发布,这是java大版本周期变化后的第一个长期支持版本,非常值得关注
简化的编译运行操作
- 在java11中可以使用java命令一次性进行编译和运行操作
- 执行源文件中的第一个类必须包含主方法
- 不可以使用其它源文件中自定义的类
String类新增方法
方法声明 | 功能介绍 |
---|---|
boolean isBlank() | 判断字符串是否为空或只包含空白代码点 |
Optional map(Function<? super T,? extends U> mapper) | 根据参数指定规则的结果来得到Optional类 型的对象 |
T orElse(T other) | 若该值存在就返回,否则返回other的数 值 |
任务总结
1、新特性
函数式接口、Lambda表达式、方法引用、Stream流、Optional类、模块化系统、钻石操作符的增强、集合的工厂方法、InputStream类的增强、局部变量的类型推断、简化的编译运行、String类新增的方法等