1. 新特性简介
新特性简介
-
速度更快
-
代码更少:新增了Lambda表达式
-
强大的Stream API
-
便于并行
-
最大化减少空指针异常Optional
2. Lambda表达式
介绍
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
基础语法
-
Java8引入了一个新的操作符:
->
,该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分为2部分 -
左侧:Lambda表达式的
参数列表
-
右侧:Lambda表达式中
所执行的功能,即Lambda体
-
语法格式1:无参数,无返回值:
() -> System.out.println( "hello world" );
public class test { public static void main(String[] args) { Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello World!"); } }; r.run(); System.out.println("--------------------------------------"); Runnable r1 = () -> { System.out.println("Hello Lambda"); }; r1.run(); } }
-
语法格式2:有一个参数,并且无返回值,小括号可省略不写,:
public class test { public static void main(String[] args) { Consumer<String> con = x -> { System.out.println(x); }; con.accept("QWERTY"); } }
-
语法格式3:有2个以上的参数,有返回值,并且Lambda体中有多条返回值
public class test { public static void main(String[] args) { Comparator<Integer> com = (x,y) -> { System.out.println("函数式接口"); return Integer.compare(x,y); }; } }
-
语法格式4:若Lambda体中只有一条语句,return和大括号都可省略不写
public class test { public static void main(String[] args) { Comparator<Integer> com = (x,y) -> Integer.compare(x,y); } }
-
语法格式5:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”
-
Lambda表达式需要
函数式接口
的支持!函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用
@FunctionalInterface
修饰
案例1:简单运算
-
函数式运算接口
@FunctionalInterface public interface MyFun { Integer getValue(Integer num); }
-
测试类
public class test { public static void main(String[] args) { Integer result = operation(10,x ->{ return x * x; }); System.out.println(result); } //operation方法,参数1为一Integer类型数据,参数2为一运算接口 public static Integer operation(Integer num, MyFun myFun){ return myFun.getValue(num); } }
案例2:定制排序比较两个Employee,先按年龄比,年龄相同再按姓名比,使用Lambda作为参数传递
-
Employee
@Data @AllArgsConstructor @NoArgsConstructor public class Employee { private String name; private int age; private Double salary; }
-
测试类
public class test { public static void main(String[] args) { List<Employee> list = Arrays.asList( new Employee("1号", 25, 5000.0), new Employee("2号", 35, 3000.0), new Employee("3号", 35, 2000.0), new Employee("4号", 35, 8000.0), new Employee("5号", 65, 1000.0) ); Collections.sort(list,(e1,e2)->{ if (e1.getAge() == e2.getAge()){ return e1.getName().compareTo(e2.getName()); }else { return -Integer.compare(e1.getAge(),e2.getAge()); } }); Iterator<Employee> iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
3. 函数式接口
四大核心函数式接口
-
Consumer<T>
:消费型接口void accept(T t);
-
Supplier<T>
:供给型接口T get();
-
Function(T,R)
:函数型接口R apply(T t);
-
Predicate<T>
:断言型接口boolean test(T t);
例子
-
Consumer<T>
:消费型接口@Test public void test(){ this.happy(1000,m -> { System.out.println("我消费了"+ m); }); } public void happy(double money, Consumer<Double> con){ con.accept(money); }
-
Supplier<T>
:供给型接口@Test public void test(){ List<Integer> numList = this.getNumList(5, () -> (int) (Math.random() * 100)); for (Integer integer : numList) { System.out.println(integer); } } //产生指定数量的整数 public List<Integer> getNumList(int num, Supplier<Integer> sup){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { list.add(sup.get()); } return list; }
-
Function(T,R)
:函数型接口@Test public void test(){ System.out.println(this.handler("str", str -> (str + "123"))); } //处理字符串 public String handler(String str, Function<String,String> fun){ return fun.apply(str); }
-
Predicate<T>
:断言型接口@Test public void test(){ List<String> stringList = Arrays.asList("qwe", "123", "hello", "ranhaifeng", "asdasdsaewqewqe"); List<String> list = filterStr(stringList, s -> (s.length() > 3)); for (String s : list) { System.out.println(s); } } //将满足条件的字符串放入集合 public List<String> filterStr(List<String> list, Predicate<String> pre){ List<String> stringList = new ArrayList<>(); for (String s : list) { if (pre.test(s)){ stringList.add(s); } } return stringList; }
其他接口
4. 方法引用与构造器引用
方法引用
-
方法引用:若 Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”,可以理解为方法引用是Lambda表达式的另外一种表现形式。
-
主要有三种语法格式:
对象::实例方法名
类::静态方法名
类::实例方法名
-
例子
//对象::实例方法 @Test public void test(){ Consumer<String> con = x -> { System.out.println(x); }; PrintStream out = System.out; Consumer<String> con1 = out::println; Consumer<String> con2 = System.out::println; con2.accept("qwe"); } //类::静态方法名 @Test public void test(){ int x;int y; Comparator<Integer> com = (x,y)-> Integer.compare(x,y); Comparator<Integer> com1 = Integer::compare; } // 类::实例方法名 @Test public void test(){ BiPredicate<String,String> bp = (x,y) -> x.equals(y); BiPredicate<String,String> bp2 = String::equals; }
-
注意:
- Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
- 若Lambda参数列表中的第一参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用
ClassName::method
构造器引用
-
例子
@Test public void test(){ Supplier<Object> sup = () -> new Object(); Supplier<Object> sup2 = Object::new; //如果有多个构造器,如何判断是调用的实体类的哪个构造器呢?看下面的注意,即Function(T,R)内部的函数 R apply(T t) 是1个参数,那么就会调用是1个参数的构造器。 Function<Integer,Employee> fun2 =Employee::new; }
-
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
数组引用
@Test
public void test(){
Function<Integer,String[]> fun = x -> new String[x];
Function<Integer,String[]> fun2 = String[]::new;
}
5. Stream API
介绍
- Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。Stream是 Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL执行的数据库查询。也可以使用Stream API来并行执行操作简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。
- 流(Stream)到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是致据遏进的是让算*”
注意: - 注意:
- Stream 自己不会存储元素。
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
5.1 创建
Stream的创建
@Test
public void test(){
//1.可以通过Collection系列集合提供的stream()或parallelstream()
List<String> list = Arrays.asList("1", "2", "3");
Stream<String> stream = list.stream();
//2.通过Arrays中的静态方法stream()获取数组流
int [] arr = new int[10];
IntStream stream1 = Arrays.stream(arr);
//3.通过Stream类中的静态方法of()
Stream<String> stream2 = Stream.of("1", "2", "3");
//4.创建无限流
//迭代
Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2);
stream3.limit(10).forEach(System.out::println);
//生成
Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
}
5.2 筛选与切片
Stream的筛选与切片
-
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
-
filter:接收Lambda,从流中排除某些元素
public class test { @Test public void test(){ List<Person> people = Arrays.asList( new Person("张三", 18, 9999.99), new Person("李四", 58, 5555.55), new Person("王五", 26, 3333.33), new Person("赵六", 36, 6666.66), new Person("田七", 12, 8888.88) ); //中间操作 Stream<Person> stream = people.stream().filter(person -> { System.out.println("中间操作"); return person.getAge() > 5; }); //终止操作 stream.forEach(System.out::println); } }
-
limit:截断流,使其元素不超过给定数量
//中间操作 people.stream() .filter(person -> person.getScore() > 5000) .limit(1) .forEach(System.out::println);
-
skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补
//中间操作 people.stream() .skip(2) .forEach(System.out::println);
-
distinct:筛选,通过流所生成元素的hashcode()和equals()去除重复元素,要想实现成功,必须实体类实现重写这两个方法!
//中间操作 people.stream() .distinct() .forEach(System.out::println);
5.3 映射
-
map:接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
@Test public void test() { List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee", "fff"); list.stream().map(str -> str.toUpperCase(Locale.ROOT)).forEach(System.out::println); }
-
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public class test { @Test public void test() { List<String> list = Arrays.asList("aaa", "bbb"); list.stream().flatMap(test::filterCharacter).forEach(System.out::println); } public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (char c : str.toCharArray()) { list.add(c); } return list.stream(); } }
5.4 排序
public class test {
@Test
public void test(){
//自然排序
List<String> list = Arrays.asList("aaa", "eee", "ddd", "bbb");
list.stream().sorted().forEach(System.out::println);
//定制排序
List<Person> list1 = Arrays.asList(
new Person("张三", 18, 2000.0),
new Person("李四", 18, 5000.0),
new Person("王五", 45, 8700.0),
new Person("赵六", 42, 4200.0),
new Person("陈七", 56, 13100.0)
);
list1.stream().sorted((p1,p2) -> {
if (p1.getAge().equals(p2.getAge())){
return p1.getSale().compareTo(p2.getSale());
}else {
return p1.getAge().compareTo(p2.getAge());
}
}).forEach(System.out::println);
}
}
5.5 查找与匹配
public class test {
@Test
public void test(){
List<Person> list = Arrays.asList(
new Person("张三", 18, 2000.0, Person.Status.BUSY),
new Person("李四", 18, 5000.0,Person.Status.FREE),
new Person("王五", 45, 8700.0,Person.Status.VOCATION),
new Person("赵六", 42, 4200.0,Person.Status.BUSY),
new Person("陈七", 56, 13100.0,Person.Status.BUSY)
);
//allMatch 检查是否匹配所有元素,返回值为Boolean类型
boolean b = list.stream().allMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b); // false
//anyMatch 检查是否匹配至少一个元素,返回值为Boolean类型
boolean b1 = list.stream().anyMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b1); // true
//noneMatch 检查是否没有匹配所有元素,返回值为Boolean类型
boolean b2 = list.stream().noneMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b2); // false
//findFirst 返回第一个元素
//Optional 防止空指针异常的类型,如果first为null,可以使用.orelse()方法指定一个不为空的对象
Optional<Person> op1 = list.stream()
.sorted((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())).findFirst();
System.out.println(op1.get()); // Person(name=张三, age=18, sale=2000.0, status=BUSY)
//findAny 返回当前流中的任意元素
//parallelStream 并行流,多个进程同时去进行filter、findAny,谁先找到算谁的
Optional<Person> op2 = list.parallelStream().filter(e -> e.getStatus().equals(Person.Status.FREE)).findAny();
System.out.println(op2.get()); // Person(name=李四, age=18, sale=5000.0, status=FREE)
//count 返回流中元素的总个数
long count = list.stream().count();
System.out.println(count); // 5
//max 返回流中的最大值
Optional<Person> max = list.stream().max((e1, e2) -> Double.compare(e1.getSale(), e2.getSale()));
System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY)
//min 返回流中的最小值
//返回list中的最小工资数
System.out.println(list.stream().map(Person::getSale).min(Double::compare).get()); // 2000.0
}
}
5.6 归约与收集
归约
-
归约:redece(T identity,BinaryOperator) / reduce(BinarOperator):可以将流中元素反复结合起来,得到一个值
public class test { @Test public void test(){ List<Person> list = Arrays.asList( new Person("张三", 18, 2000.0, Person.Status.BUSY), new Person("李四", 18, 5000.0,Person.Status.FREE), new Person("王五", 45, 8700.0,Person.Status.VOCATION), new Person("赵六", 42, 4200.0,Person.Status.BUSY), new Person("陈七", 56, 13100.0,Person.Status.BUSY) ); List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); //从0开始,0作为x,先取y=1,执行x+y=1,再将x作为1,y取流中下一个元素2,一直执行下去 Integer sum = list1.stream().reduce(0, (x, y) -> x + y); System.out.println(sum); //上面那个返回值是Integer,是因为有起始值,不可能为空,而这条语句没有起始值,有可能为空! Optional<Double> reduce = list.stream().map(Person::getSale).reduce(Double::sum); System.out.println(reduce.get()); } }
-
map和reduce的连接通常被成为map-reduce模式,因Google用它来进行网络搜索而出名
收集
-
collect:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
public class test { @Test public void test(){ List<Person> list = Arrays.asList( new Person("张三", 18, 2000.0, Person.Status.BUSY), new Person("张三", 18, 5000.0,Person.Status.FREE), new Person("王五", 45, 8700.0,Person.Status.VOCATION), new Person("赵六", 42, 4200.0,Person.Status.BUSY), new Person("陈七", 56, 13100.0,Person.Status.BUSY) ); List<String> nameList = list.stream().map(Person::getName).collect(Collectors.toList()); //放入set可以进行去重 Set<String> collect = list.stream().map(Person::getName).collect(Collectors.toSet()); //要想放入我们指定的收集器中,可以采取以下方式 // Collectors.toCollection( xxx ::new) xxx为我们想要放入的集合类型 // 例如:想将list中的人员name收集放入一个LinkedList中 LinkedList<String> collect1 = list.stream().map(Person::getName).collect(Collectors.toCollection(LinkedList::new)); //总数 Long count = list.stream().collect(Collectors.counting()); System.out.println(count); // 5 //平均值 Double avg = list.stream().collect(Collectors.averagingDouble(Person::getSale)); System.out.println(avg); // 6600.0 //总和 Double sum = list.stream().collect(Collectors.summingDouble(Person::getSale)); System.out.println(sum); // 33000.0 //最大值:最大salary的员工信息 Optional<Person> max = list.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSale(), e2.getSale()))); System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY) //最小值:最小的salary Optional<Double> min = list.stream().map(Person::getSale).collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); // 2000.0 } }
-
分组
public class test { @Test public void test(){ List<Person> list = Arrays.asList( new Person("张三", 18, 2000.0, Person.Status.BUSY), new Person("张三", 18, 5000.0,Person.Status.FREE), new Person("王五", 45, 8700.0,Person.Status.VOCATION), new Person("赵六", 42, 4200.0,Person.Status.BUSY), new Person("陈七", 56, 13100.0,Person.Status.BUSY) ); //简单分组 Map<Person.Status, List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getStatus)); System.out.println(map); // {FREE=[Person(name=张三, age=18, sale=5000.0, status=FREE)], // BUSY=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=赵六, age=42, sale=4200.0, status=BUSY), Person(name=陈七, age=56, sale=13100.0, status=BUSY)], // VOCATION=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]} //多级分组 Map<Person.Status, Map<String, List<Person>>> map2 = list.stream().collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy(e -> { if (e.getAge() <= 35) { return "青年"; } else if (e.getAge() <= 50) { return "中年"; } else { return "老年"; } }))); //{FREE={青年=[Person(name=张三, age=18, sale=5000.0, status=FREE)]}, // BUSY={青年=[Person(name=张三, age=18, sale=2000.0, status=BUSY)], 老年=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)], 中年=[Person(name=赵六, age=42, sale=4200.0, status=BUSY)]}, // VOCATION={中年=[Person(name=王五, age=45, sale=8700.0, status=VOCATION)]}} System.out.println(map2); } }
-
其他api
public class test { @Test public void test(){ List<Person> list = Arrays.asList( new Person("张三", 18, 2000.0, Person.Status.BUSY), new Person("张三", 18, 5000.0,Person.Status.FREE), new Person("王五", 45, 8700.0,Person.Status.VOCATION), new Person("赵六", 42, 4200.0,Person.Status.BUSY), new Person("陈七", 56, 13100.0,Person.Status.BUSY) ); //分区 Map<Boolean, List<Person>> map = list.stream().collect(Collectors.partitioningBy(e -> e.getSale() > 9000.0)); System.out.println(map); //{false=[Person(name=张三, age=18, sale=2000.0, status=BUSY), Person(name=张三, age=18, sale=5000.0, status=FREE), Person(name=王五, age=45, sale=8700.0, status=VOCATION), Person(name=赵六, age=42, sale=4200.0, status=BUSY)], // true=[Person(name=陈七, age=56, sale=13100.0, status=BUSY)]} //对数据进行统计 DoubleSummaryStatistics dss = list.stream().collect(Collectors.summarizingDouble(Person::getSale)); System.out.println(dss.getAverage()); // 6600.0 System.out.println(dss.getCount()); // 5 System.out.println(dss.getMax()); // 13100.0 //对数据进行连接 String str = list.stream().map(Person::getName).collect(Collectors.joining(",", "**", "==")); System.out.println(str); // **张三,张三,王五,赵六,陈七== } }
5.7 并行流与串行流
- 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过parallel() 与sequential()在并行流与顺序流之间进行切换。
- fork/join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
- Fork/Join框架与传统线程池的区别:采用工作窃取模式(work-stealing)
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
相对于一般的线程池实现, fork/join框架的优势体现仕对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能。
public class ForkJoinCalculate extends RecursiveTask<Long> {
private static final long serialVersionUID = 12313435L;
private long start;
private long end;
private static final long THRESHOLD = 10000;
public ForkJoinCalculate(long start,long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD){
long sum = 0;
for (long i = start; i <= end ; i++) {
sum+= i;
}
return sum;
}else {
long middle = (start + end )/2;
ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
left.fork(); //拆分子任务,同时压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle + 1,end);
right.fork();
return left.join() + right.join();
}
}
}
测试:
public class test {
@Test
public void qwe(){
Instant start = Instant.now();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinCalculate forkJoinCalculate = new ForkJoinCalculate(0, 10000000000L);
Long sum = forkJoinPool.invoke(forkJoinCalculate);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("消耗时间:" + Duration.between(start,end).toMillis()); //5701
}
//普通for循环计算
@Test
public void test2(){
Instant start = Instant.now();
long sum = 0L;
for (int i = 0; i < 10000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("消耗时间:" + Duration.between(start,end).toMillis()); //没跑出来
}
//java8并行流
@Test
public void test3(){
Instant start = Instant.now();
LongStream.rangeClosed(0,10000000000L)
.parallel()
.reduce(0,Long::sum);
Instant end = Instant.now();
System.out.println("消耗时间:" + Duration.between(start,end).toMillis()); //2145
}
}
还是java8并行流
快!
5.8 Optional容器
这里只介绍基本的API,应用举例不展开,有兴趣的请查阅其它资料
public class test {
@Test
public void test(){
// Option.of(T t):创建一个Optional实例
Optional<Person> op = Optional.of(new Person());
Person person = op.get();
System.out.println(person); // Person(name=null, age=null, sale=null, status=null)
}
@Test
public void test2(){
// Option.empty():创建一个Optional实例
Optional<Object> op = Optional.empty();
System.out.println(op.get()); // java.util.NoSuchElementException: No value present
}
@Test
public void test3(){
// Option.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
Optional<Object> op = Optional.ofNullable(new Person());
System.out.println(op.get()); // Person(name=null, age=null, sale=null, status=null)
}
@Test
public void test4(){
Optional<Person> op = Optional.ofNullable(new Person());
// Option.isPresent():判断是否包含值
if (op.isPresent()){
System.out.println(op.get()); // Person(name=null, age=null, sale=null, status=null)
}
}
@Test
public void test5(){
Optional<Person> op = Optional.ofNullable(null);
// Option.orElse(T t):如果调用对象包含值,返回该值,否则返回t
Person emp = op.orElse(new Person("qwe", 18, 2000.2, Person.Status.BUSY));
System.out.println(emp); // Person(name=qwe, age=18, sale=2000.2, status=BUSY)
}
@Test
public void test6(){
Optional<Person> op = Optional.ofNullable(null);
//如果调用对象包含值,返回该值,否则返回s获取的值
Person person = op.orElseGet(() -> new Person());
System.out.println(person); // Person(name=null, age=null, sale=null, status=null)
}
@Test
public void test7(){
Optional<Person> op = Optional.ofNullable(new Person("qwe", 18, 200.0, Person.Status.BUSY));
// map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
Optional<String> str = op.map(e -> e.getName());
System.out.println(str.get()); // qwe
// faltMap(Function mapper):map类似,要求返回值必须是Optional,方法中必须用Optional包装
Optional<String> str2 = op.flatMap(e -> Optional.of(e.getName()));
System.out.println(str2.get());
}
}
6. 接口中的默认方法与静态方法
接口中的默认方法
接口默认方法的”类优先
”原则:若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
- 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
import java.util.stream.Collectors;
public interface MyFun {
default String getName(){
return "哈哈哈";
}
}
public class MyClass {
public String getName(){
return "嘿嘿嘿";
}
}
public class SubClass extends MyClass implements MyFun{
}
import io.swagger.annotations.Example;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.LongStream;
public class test {
@Test
public void test(){
SubClass sc = new SubClass();
System.out.println(sc.getName());
}
}
接口中的静态方法
public interface MyFun {
public static void show(){
System.out.println("接口中的静态方法");
}
}
7. 新时间日期API
LocalDate、LocalTime、LocalDateTime
-
LocalDateTime
public class test { @Test public void test() { /* * LocalDateTime * */ LocalDateTime now = LocalDateTime.now(); System.out.println(now); // 2021-12-24T14:51:37.161 //LocalDataTime实例创建 LocalDateTime ldt = LocalDateTime.of(2021, 12, 23, 13, 23, 32); System.out.println(ldt); // 2021-12-23T13:23:32 //年份加 LocalDateTime ldt2 = ldt.plusYears(2); System.out.println(ldt2); // 2023-12-23T13:23:32 //月份减 LocalDateTime ldt3 = ldt.minusMonths(2); System.out.println(ldt3); // 2021-10-23T13:23:32 //获取时间的各个值 System.out.println(ldt.getYear()); // 2021 System.out.println(ldt.getMonthValue()); // 12 System.out.println(ldt.getDayOfMonth()); // 23 System.out.println(ldt.getHour()); // 13 System.out.println(ldt.getMinute()); // 23 System.out.println(ldt.getSecond()); // 32 } }
-
Instant:时间戳(以Unix元年:1970年1月1日 00:00:00 到某个时间之间的毫秒值)
public class test { @Test public void test() { Instant ins = Instant.now(); // 默认获取UTC时区 System.out.println(ins); OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8)); // 带偏移量,这里表示东八区 System.out.println(odt); System.out.println(ins.toEpochMilli()); // 1640329758791 Instant instant = Instant.ofEpochSecond(60); // 1970-01-01T00:01:00Z System.out.println(instant); } }
-
计算时间间隔
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
public class test { @Test public void test() { Instant ins1 = Instant.now(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Instant ins2 = Instant.now(); Duration duration = Duration.between(ins1, ins2); System.out.println(duration.toMillis()); // 1009 LocalDate ld1 = LocalDate.of(2021,12,12); LocalDate ld2 = LocalDate.now(); Period period = Period.between(ld1, ld2); System.out.println(period.getYears()); // 0 System.out.println(period.getMonths()); // 0 System.out.println(period.getDays()); // 12 System.out.println(); } }
日期的操纵
-
TemporalAdiuster :时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
public class test { @Test public void test() { LocalDateTime ldt = LocalDateTime.now(); LocalDateTime ldt2 = ldt.withDayOfMonth(10); // 将日期设置为10 System.out.println(ldt); // 2021-12-24T16:23:16.451 System.out.println(ldt2); // 2021-12-10T16:22:40.417 LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); // 下一个周日 System.out.println(ldt3); // 2021-12-26T16:22:40.417 //自定义:下一个工作日志 LocalDateTime ldt5 = ldt.with( l -> { LocalDateTime ldt4 = (LocalDateTime) l; DayOfWeek dow = ldt4.getDayOfWeek(); if (dow.equals(DayOfWeek.FRIDAY)) { return ldt4.plusDays(3); } else if (dow.equals(DayOfWeek.SATURDAY)) { return ldt4.plusDays(2); } else{ return ldt4.plusDays(1); } }); System.out.println(ldt5); // 2021-12-27T16:30:55.074 } }
-
DateTimeFormatter:格式化时间/日期
public class test { @Test public void test() { DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE; LocalDateTime ldt = LocalDateTime.now(); String strDate = ldt.format(dtf); System.out.println(strDate); // 2021-12-24 DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String strDate2 = dtf2.format(ldt); System.out.println(strDate2); // 2021年12月24日 16:35:55 LocalDateTime newDate = LocalDateTime.parse(strDate2, dtf2); System.out.println(newDate); // 2021-12-24T16:38:58 } }
-
时区的处理
public class test { @Test public void test() { LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Tallinn")); System.out.println(ldt); // 2021-12-24T10:43:37.135 LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn")); ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Europe/Tallinn")); System.out.println(zdt); // 2021-12-24T10:45:08.507+02:00[Europe/Tallinn] } }
8. 其他新特性
重复注解
Java 8对注解处理提供了两点改进:可重复的汪解及可用于类型的注解。
-
自定义注解,需要加
@Repeatable
注解实现可重复,括号里是注解容器@Repeatable(MyAannotations.class) @Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "atguigu"; }
-
自定义注解容器
@Target({ TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAannotations { MyAnnotation[] value(); }
-
测试
public class test { @Test public void test() throws NoSuchMethodException { Class<test> clazz = test.class; Method m1 = clazz.getMethod("show"); MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class); for (MyAnnotation myAnnotation : mas) { System.out.println(myAnnotation.value()); // hello 、 world } } @MyAnnotation("hello") @MyAnnotation("world") public void show(@MyAnnotation("str") String str) { } }