JDK1.8新特性简介
-
速度更快 - 优化底层源码,比如HashMap、ConcurrentHashMap
-
代码更少 - 添加新的语法Lambda表达式
-
强大的Stream API
-
便于并行
-
最大化减少空指针异常 - Optional
Lambda表达式
简介
Lambda是一个匿名函数(方法), 允许把函数作为一个方法的参数 。利用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
一般都是优化匿名内部类
基础语法
无参数、无返回值的抽象方法
public class Test1 { @Test public void test01() { // I1 i1 = new I1() { // @Override // public void method() { // System.out.println("传统使用匿名内部类的方式"); // } // }; // i1.method(); I1 i1 = ()-> System.out.println("采用Lambda表达式的方式"); i1.method(); } } interface I1{ public void method();//无参数、无返回值的抽象方法 }
一个参数、无返回值的抽象方法
public class Test1 { @Test public void test01() { I1 i1 = (x)-> System.out.println("采用Lambda表达式的方式 " + x); i1.method(1000); } } interface I1{ public void method(int num1);//一个参数、无返回值的抽象方法 }
多个参数、无返回值的抽象方法
public class Test1 { @Test public void test01() { I1 i1 = (x,y,z)-> System.out.println("采用Lambda表达式的方式 " + x + y + z); i1.method(1000,2000,3000); } } interface I1{ //多个参数、无返回值的抽象方法 public void method(int num1,int num2,int num3); }
多个参数、有返回值的抽象方法
public class Test1 { @Test public void test01() { I1 i1 = (x,y,z)-> x+y+z; int result = i1.method(1000,2000,3000); System.out.println(result); } } interface I1{ //多个参数、有返回值的抽象方法 public int method(int num1,int num2,int num3); }
注意点
-
重写方法的形参只有一个时,可以不加小括号
-
Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量
-
Lambda表达式中访问外层的局部变量,外层的局部变量自动变成隐式常量,默认添加final
-
重写方法的形参同时加类型或同时不加类型
public class Test1 { @Test public void test01() { int x; int num = 10; I1 i1 = x -> System.out.println(x + (num++)); i1.method(1000); I2 i2 = (int x,int y) -> { int result = x+y; return result; }; int result = i2.method(10, 20); System.out.println(result); } } interface I1{ public void method(int num1); } interface I2{ public int method(int num1,int num2); }
函数式接口
简介
函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。配合Lambda表达式一起使用
四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer<T> 消费型接口 | T | void | void accept(T t); |
Supplier<T> 供给型接口 | void | T | T get(); |
Function<T, R> 函数型接口 | T | R | R apply(T t); |
Predicate<T> 断言型接口 | T | boolean | booelan test(T t); |
BiConsumer<T, U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t,U u); |
BiFunction<T, U, R> | T,U | R | 对类型为T,U参数应用操作,并返回R类型的结果。包含方法为R apply(T t,U u); |
UnaryOperator<T> extends Function<T, T> | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator<T> extends BiFunction<T,T,T> | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2); |
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> | T | int long double | 分别计算int、long、double值的函数 |
IntFunction<R> LongFunction<R> DoubleFunction<R> | int long double | R | 参数为int、long、double类型的函数 |
应用场景:当项目中需要一个接口,并且该接口中只有一个抽象方法,就没必要去创建新的接口,直接选择Java提供的使用合适的函数式接口即可
方法、构造方法和数组引用
方法、构造方法和数组引用就是Lambda的另一种表现形式
方法引用
若Lamdba表达式中的内容由方法已经实现了,可以使用方法引用这个技能
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面
对象::实例方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test1 { @Test public void test01() { // I1 i1 = (x)->System.out.println(x); // i1.method("abcd"); //println里的参数列表和返回值类型必须和method方法一致才行 PrintStream ps = System.out; I1 i1 = ps::println;//对象::实例方法 i1.method("abcd"); } } interface I1{ public void method(String str); }
类名::静态方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test1 { @Test public void test01() { // Comparator<Integer> com = (x,y)-> Integer.compare(x, y); // int compare = com.compare(10, 20); // System.out.println(compare); //类名::静态方法 Comparator<Integer> com = Integer::compare; int compare = com.compare(10, 20); System.out.println(compare); } }
类名::实例方法
Lambda表达式参数列表中第一个参数必须是实例方法的调用者
Lambda表达式参数列表中第二个参数必须是实例方法的参数
public class Test1 { @Test public void test01() { // I1<String> i1 = (x,y) -> x.equals(y); // boolean method = i1.method("abc", "abc"); // System.out.println(method); //类名::实例方法 //注意:Lambda表达式参数列表中第一个参数是equals方法的调用者, // Lambda表达式参数列表中第二个参数是equals方法的参数 I1<String> i1 = String::equals; boolean method = i1.method("abc", "abc"); System.out.println(method); } } interface I1<T>{ public boolean method(T t1,T t2); }
构造方法引用
类名::new
需要调用的构造方法的参数列表必须和函数式接口中抽象方法的参数列表一致
public class Test1 { @Test public void test01() { //需求:创建学生对象 I1<Student> i1 = Student::new; // System.out.println(i1.method()); // System.out.println(i1.method("桥本有菜",24)); System.out.println(i1.method("桥本有菜",24,8888,Course.JAVA)); } } interface I1<T>{ // public T method(); public T method(String name,int age,double salary,Course course); } enum Course{//课程枚举 JAVA,HTML,PYTHON; } class Student{//学生类 private String name; private int age; private double salary; private Course course; ... }
数组引用
语法格式:type[]::new
public class Test1 { @Test public void test01() { //创建数组 I1<String[]> i1 = String[]::new; System.out.println(Arrays.toString(i1.method(10))); } } interface I1<T>{ public T method(int capacity); }
Stream
简介
Stream(流)是数据渠道,用于操作数据源(集合、数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式
注意:
Stream不会存储数据
Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
执行步骤
-
创建Stream:通过数据源(集合、数组等)获取一个Stream
-
中间操作:中间操作链,对源数据的数据进行处理
-
终止操作:执行中间操作,并产生结果
创建Stream
public class Test1 { @Test public void test01() { //方式一:通过Collection接口提供的stream()-串行流或parallelStream()-并行流 获取流对象 List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); //方式二:通过Arrays的静态方法stream()获取流对象 String[] strs = new String[10]; Stream<String> stream2 = Arrays.stream(strs); //方式三:通过Stream的静态方法of()获取流对象 Stream<String> stream3 = Stream.of("aaa","bbb","ccc"); //方式四:创建无限流 //iterate()迭代 Stream<Integer> stream4 = Stream.iterate(1, (x)->x+=100); stream4.limit(3).forEach(System.out::println); //方式五:创建无限流 Stream<Double> stream5 = Stream.generate(()->Math.random()); stream5.limit(3).forEach(System.out::println); } }
注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载
中间操作 - 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 从流中排除元素 |
limit(long maxSize) | 设置限制数据条数 |
skip(long n) | 跳过元素,返回跳过n个元素的流,若流中不满足n个元素则返回空流。与limit()互补 |
distinct() | 筛选,流通过元素对象的hashCode()和equals()方法去除重复元素 |
如果没有终止操作,中间操作就不会被调用,终止操作时一次性全部处理,这种称为惰性求值/延迟加载
中间操作 - 映射
方法 | 描述 |
---|---|
map(Function<?, ? > mapper) | 将流中所有元素映射成一个新的元素或者提取信息 |
flatMap(Function<?, ? extends Stream<? >> mapper) | 将流中的流整合(整合成平面/平铺流) |
中间操作 - 排序
方法 | 解释 |
---|---|
sorted() | 使用元素原有排序规则 - Comparable<T> |
sorted(Comparator<? super T> comparator) | 使用自定义排序规则 - Comparator<T> |
终止操作 - 匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate<? super T> predicate) | 检查是否匹配所有元素 |
anyMatch(Predicate<? super T> predicate) | 检查是否匹配至少一个元素 |
noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回任意一个元素(但效果不好) |
count() | 返回流中元素的个数 |
max(Comparator<? super T> comparator) | 返回流中最大值 |
min(Comparator<? super T> comparator) | 返回流中最小值 |
终止操作 - 归约
归约:将流中的元素反复结合起来,得到一个值
map+reduce的连接通常称为map_reduce模式,因Google用它进行网络搜索而出名
方法 | 描述 |
---|---|
reduce( T identity , BinaryOperator<T> accumulator) | 参数:(初始值,结合逻辑) |
reduce(BinaryOperator<T> accumulator) | 参数:(结合逻辑) |
终止操作 - 收集
收集:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
方法 | 描述 |
---|---|
collect(Collector<? super T, A, R> collector) | 把元素放入Collector集合中 |
并行流与串行流
并行流就是把一个内容拆分成多个数据块,并用不同的线程分别处理每个数据块的流。Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过 parallel() - 并行流 与sequential()-顺序流 之间进行切换。
注意
默认为顺序流/串行流
并行流一般在大数据搜索里使用到
JDK1.8之前也有并行流,叫做Fork/Join并行计算框架
public class Test1 { @Test public void test01() { //需求:使用并行流计算1-10000000L之和 OptionalLong reduce = LongStream. range(0, 10000001L).//生成1-10000000的数流 parallel(). //转换为并行流 reduce(Long::sum); //求和 System.out.println(reduce); } }
Optional
Optional<T>类(java. util. Optional)是一个容器类,代表一个存在或不存在的值,原来用null表示一个值不
存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常
此类的设计就是更好的避免空指针异常
方法 | 描述 |
---|---|
Optional.of(T t) | 创建一个Optional实例 |
Optional.empty() | 创建一 个空的 Optional实例 |
Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例 |
get() | 获取Optional实例里的对象 |
isPresent() | 判断是否包含值 |
orElse(T t) | 如果调用对象包含值, 返回该值,否则返回t |
orElseGet(Supplier s) | 如果调用对象包含值,返回该值,否则返回s获取的值 |
map(Function f) | 如果有值对其处理,并返回处理后的Optional,否则返回optional. empty() |
flatMap(Function mapper) | 与map 类似,要求返回值必须是Optional |
public class Test1 { @Test public void test01() { //创建一个Optional实例,把对象封装到Optional容器里 // Optional<Student> op = Optional.of( // new Student("aaa", 26, 6666, Course.HTML)); //创建一个空的Optional容器 // Optional<Student> op = Optional.empty(); //创建一个Optional实例,若对象为null->创建空实例,若对象为不为null->创建Optional实例 Optional<Student> op = Optional.ofNullable( new Student("bbb", 26, 7777, Course.PYTHON)); //判断容器里是否包含对象 if(op.isPresent()){//包含 System.out.println("获取容器里的对象:" + op.get().getName()); }else{//不包含 System.out.println("容器里的对象为null"); } //如果容器里有对象就返回,否则就返回新对象 Student stu = op.orElse(new Student("ccc", 26, 8888, Course.JAVA)); System.out.println(stu.getName()); //不同情况下可以返回不同对象,orElseGet()比orElse()可变性更强 boolean bool = true; stu = op.orElseGet(()->{ if(bool){ return new Student("吴彦祖", 26, 8888, Course.JAVA); }else{ return new Student("麻生希", 26, 8888, Course.JAVA); } }); //获取原容器中的某个值并返回新容器中 //map(Function<? super T, ? extends U> mapper) Optional<String> map = op.map(Student::getName); System.out.println(map.get()); //与map 类似,要求返回值必须是Optional //flatMap(Function<? super T, Optional<U>> mapper) Optional<String> flatMap = op.flatMap((e)->Optional.of(e.getName())); System.out.println(flatMap.get()); } } enum Course{//课程枚举 JAVA,HTML,PYTHON; } class Student implements Comparable<Student>{//学生类 private String name; private int age; private double salary; private Course course; ... }
接口的默认方法与静态方法
从JDK1.8开始,接口中可以有默认方法,既default修饰的方法,此方法可以让接口的实现类所调用,而接
口中的静态方法直接用接口名调用即可
public class Test1 { @Test public void test01() { MyClass myClass = new MyClass(); myClass.defaultMethod(); I1.staticMethod(); } } interface I1{ default void defaultMethod(){ System.out.println("接口中的默认方法"); } public static void staticMethod(){ System.out.println("接口中的静态方法"); } } class MyClass implements I1{}
接口默认方法的”类优先”原则:
如果一个接口中定义了一个默认方法,而接口实现类的父类定义了一个同名的方法时,选择父类中的方法
接口冲突:如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
public class Test1 { @Test public void test01() { MyClass myClass = new MyClass(); MyClass.method(); } } interface I1{ default void method(){ System.out.println("I1接口中的默认方法"); } } class Father { public void method(){ System.out.println("Father类中的默认方法"); } } class MyClass extends Father implements I1 {}
public class Test1 { @Test public void test01() { MyClass myClass = new MyClass(); myClass.method(); } } interface I1{ default void method(){ System.out.println("I1接口中的默认方法"); } } interface I2{ default void method(){ System.out.println("I2接口中的默认方法"); } } class MyClass implements I1,I2 { @Override public void method() { //I1.super.method(); I2.super.method(); } }
日期组件
JDK1.8提供的新日期类都是不可变的,既不管怎么样的改变,都会产生一个新的实例,他们都是线程安全的
日期组件遵循与IOS-8601世界时间标准
组件简介
包路径 | 类名 | 描述 |
---|---|---|
java.time | 针对日期和时间操作的包 | |
LocalDate | 用于表示日期的类 | |
LocalTime | 用于表示时间的类 | |
LocalDateTime | 用于表示日期时间的类 | |
Instant | 时间戳类(1970.1.1 0:0:0 到线程的毫秒数) | |
Period | 两个日期间隔类 | |
Duration | 两个时间间隔类 | |
java.time.chrono | 针对日期时间特殊格式操作的包 | |
JapaneseChronology | 日本帝国历法系统类 | |
ThaiBuddhistChronology | 泰国佛教日历系统类 | |
java.time.format | 针对时间日期时间格式化操作的包 | |
DateTimeFormatter | 格式化日期时间类 | |
java.time.temporal | 针对时间矫正操作的包 | |
java.time.zone | 针对时区操作的包 |
重复注解及类型注解
jdk1.8开始可以重复注解
ps:一个类可有多个同样的注解