总结下常见的1.8新特性
JDK1.8最大的改变就是:1.Lambda表达式,2.Stream API
1.接口的扩展方法
jdk1.8之前,接口中只允许有抽象方法,但在1.8之后,接口中允许有多个非抽象的方法,但是必须使用default进行修饰,叫做扩展方法。
其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,可以不重写实现类接口的default方法,直接调用default修饰的方法
函数式接口中还可以定义多个static方法
public interface A {
void test1();
public default void test2(){
System.out.println("新特性");
}
public static void test3(){
System.out.print("static")
}
}
public class B implements A{
@Override
public void test1() {
}
public static void main(String[] args) {
A a = new B();
a.test2();
A.test3();
}
}
2.时间日期的更新
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多
LocalDate/LocalTime/LocalDateTime(类似于String类,不可变性,可替换Calendar类)
TemporalAdjusters
//这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
java.time.Instant (可替换Date类)
DateTimeFormatter(替代SimpleDateFormat类)
//现在1.8引入了DateTimeFormatter类,在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,
比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(dtf.format(ldt));
3.在HashMap中1.8添加新特性,底层实现为(数组+链表+红黑二叉树)
4.Lambda表达式(不是所有的接口都可以通过这种方法来调用,只有函数式接口才行)
Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。
“函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!”
Runnable runnable = () -> System.out.println("我爱中国!");
runnable.run();
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("我爱中国!");
}
};
r.run();
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare = comparator.compare(12, 56);
System.out.println(compare);
Comparator<Integer> c = (o1,o2) -> Integer.compare(o1,o2);
int compare1 = c.compare(12,56);
System.out.println(compare1);
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("中国");
Consumer<String> consumer1 = s -> {
System.out.println(s);
};
consumer1.accept("中国");
函数式接口(lambda表达式,方法引用,构造器引用,数组引用都是函数式接口的实例)
“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。
Java内置的核心函数式接口
消费型接口:Consumer<T> 参数T 返回值void 方法:void accept(T t)
供给型接口:Supplier<T> 参数无 返回值T 方法:T get()
函数型接口:Function<T,R> 参数T 返回值R 方法:R apply(T t)
断定型接口:Predicate<T> 参数T 返回值Boolean 方法 Boolean test(T t)
//示例:
@Test
public void test1(){
//原始的方法调用
happyTime(200, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("花销"+aDouble+"元");
}
});
//lambda表达式调用
happyTime(300,money -> System.out.println("花销"+money+"元"));
}
public void happyTime(double money, Consumer<Double> consumer){
consumer.accept(money);
}
除了Java提供的,我们也可以自定义函数式接口
@FunctionalInterface
public interface MyLambda {
public void test1(String y);
//这里如果继续加一个抽象方法便会报错
//public void test1();
//default方法可以任意定义
default String test2(){
return "123";
}
default String test3(){
return "456";
}
//static方法也可以定义
static void test4(){
System.out.println("789");
}
}
//调用
MyLambda myLambda = y -> {
System.out.println("Hello!");
}
5.方法引用
使用情景:当要传递给lambda体的操作,已经有实现的方法,就可以使用方法引用
方法引用:本质上就是lambda表达式,而lambda表达式作为函数式接口的实例,所以,方法引用也是函数式接口的实例
使用格式:类/(对象) :: 方法名
具体使用情形:
1.对象::非静态方法
2.类::静态方法
3.类::非静态方法
方法引用使用的要求:函数式接口中的抽象方法的参数,返回值要和方法引用方法的参数,返回值相同(对于情形1,2而言)
//对象::非静态方法
//void accept(T t)
//void println(T t)
@Test
public void test2(){
Consumer<String> consumer1 = str -> System.out.println(str);
consumer1.accept("天津");
PrintStream printStream = System.out;
Consumer<String> consumer2 = printStream :: println;
consumer2.accept("北京");
}
//类::非静态方法
@Test
public void test3() {
Comparator<String> comparator = String::compareTo;
System.out.println(comparator.compare("abc","efj"));
}
6.构造器引用
构造方法引用使用的要求:函数式接口的抽象方法的形参,和构造器的形参列表保持一致。抽象方法的返回值即为构造器的所属类的类型
@Test
public void test4(){
Supplier supplier1 = () -> new Object();
System.out.println(supplier1.get());
Supplier supplier2 = Object::new;
System.out.println(supplier2.get());
}
7.数组引用
数组引用写法和构造器引用一样
@Test
public void test5(){
Function<Integer,String[]> function1 = length -> new String[length];
String[] strings = function1.apply(5);
System.out.println(Arrays.toString(strings));
Function<Integer,String[]> function2 = String[] :: new;
String[] strs = function2.apply(4);
System.out.println(Arrays.toString(strs));
}
8.Stream API(Stream(流)用于操作集合数组等)
把真正的函数式编程引入到Java,Stream API 可以极大的提高生产力
1.Stream自己不会存储数据
2.Stream不会改变数据源,相反,会返回一个持有结果的新的Stream
3.操作是延迟执行的,意味着需要结果才执行
Stream的执行流程
1.Stream的实例化
2.Stream的中间操作
3.Stream的停止操作
一旦执行停止操作,就会执行中间操作链(操作数据),产生结果之后,不会再被使用
//Stream的实例化的四种方式
@Test
public void test1() {
//通过集合的方式创建Stream对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
//返回顺序流
Stream<String> stream1 = list.stream();
//返回并行流
Stream<String> stream2 = list.parallelStream();
}
@Test
public void test2() {
//通过数组的方式创建Stream对象
Integer[] integers = new Integer[]{1, 2, 3, 4, 5, 6};
//返回一个顺序流
Stream<Integer> integerStream = Arrays.stream(integers);
}
@Test
public void test3() {
//通过Stream的of()方法
//返回一个顺序流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
@Test
public void test4(){
//返回无限流
Stream.iterate(1,t -> t * 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
}
//Stream的中间操作
@Test
public void test1(){
//1.筛选&&切片
//通过集合的方式创建Stream对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("");
list.add("cccc");
list.add("ddd");
list.add("d");
list.add("eeeeeeee");
/*//返回顺序流
Stream<String> stream1 = list.stream();
//过滤流中的元素(集合元素不为空数据),并输出
stream1.filter(lists -> !lists.isEmpty()).forEach(System.out::println);
//返回顺序流
Stream<String> stream2 = list.stream();
//截断流中部分元素
//stream2.limit(2).forEach(s -> System.out.println(s));
stream2.limit(2).forEach(System.out::println);
//跳过指定个数的流中的元素,从其后操作
list.stream().skip(4).forEach(System.out::println);
//筛选去除重复的,通过流中元素所在类的hashCode方法,和equals方法
list.stream().distinct().forEach(System.out::println);
//2.映射
list.stream().map(String::toUpperCase).forEach(System.out::println);
list.stream().map(s -> s.toUpperCase()).forEach(s -> System.out.println(s));
Stream<Integer> stream = list.stream().map(String::length);
stream.forEach(System.out::println);*/
//3.对流中元素排序
Stream<Integer> stream = Stream.of(1,5,4,6,9,8,3);
stream.sorted().forEach(System.out::println);
List<Student> listStudent = new ArrayList<>();
listStudent.add(new Student(1,"张三"));
listStudent.add(new Student(3,"王五"));
listStudent.add(new Student(4,"赵六"));
listStudent.add(new Student(2,"李四"));
listStudent.add(new Student(2,"七七"));
listStudent.stream().sorted((o1,o2) -> {
if(o1 instanceof Student && o2 instanceof Student){
Student student1 = (Student) o1;
Student student2 = (Student) o2;
if(o1.getId() != o2.getId()){
return o1.getId().compareTo(o2.getId());
}else {
return o1.getName().compareTo(o2.getName());
}
}else {
throw new RuntimeException("参数有误");
}
}).forEach(System.out::println);
}
//Stream的停止操作
@Test
public void test1() {
//1.匹配与查找
List<Student> listStudent = new ArrayList<>();
listStudent.add(new Student(1, "张三"));
listStudent.add(new Student(3, "王五"));
listStudent.add(new Student(4, "赵六"));
listStudent.add(new Student(2, "李四"));
listStudent.add(new Student(2, "七七"));
//存在一个流中元素不符合条件,则返回false
boolean b1 = listStudent.stream().allMatch(student -> student.getId() > 3);
System.out.println(b1);
//存在一个流中元素符合条件,就返回true
boolean b2 = listStudent.stream().anyMatch(student -> student.getName() != null);
System.out.println(b2);
//查找流中元素是否存在名字中不含有“五”
boolean b3 = listStudent.stream().noneMatch(student -> student.getName().contains("三"));
System.out.println(b3);
//返回流中元素的第一个
Optional<Student> optional1 = listStudent.stream().findFirst();
System.out.println(optional1);
//返回流中元素的任意一个
Optional<Student> optional2 = listStudent.parallelStream().findAny();
System.out.println(optional2);
//返回流中元素的总个数
int c = (int) listStudent.stream().filter(student -> student.getId() > 3).count();
System.out.println(c);
//返回最大的流中的元素
Stream<Integer> integerStream = listStudent.stream().map(student -> student.getId());
Optional<Integer> optionalInteger = integerStream.max(Integer::compareTo);
System.out.println(optionalInteger);
//返回流中指定属性所在元素的最小元素
Optional<Student> studentOptional = listStudent.stream().min((o1, o2) -> Integer.compare(o1.getId(), o2.getId()));
System.out.println(studentOptional);
//这是集合的forEach
listStudent.forEach(System.out::println);
//2.归约
//计算流中的元素总和
List<Integer> list = Arrays.asList(1, 2, 5, 4, 6, 3, 9, 7, 8);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
Stream<Integer> iStream1 = listStudent.stream().map(student -> student.getId());
Optional<Integer> iOptional = iStream1.reduce(Integer::sum);
System.out.println(iOptional);
//3.收集
List<Student> list1 = listStudent.stream().filter(student -> student.getId() > 2).collect(Collectors.toList());
list1.forEach(System.out::println);
Set<Student> set = listStudent.stream().filter(student -> student.getId() > 2).collect(Collectors.toSet());
set.forEach(System.out::println);
}
9.Optional类
为了解决Java中的空指针问题而生
@Test
public void test1() {
Optional optional = Optional.empty();
if (!optional.isPresent()) {
System.out.println("空空!!!");
}
//get()通常与of(T t)方法一起使用(非空)
Integer integer = 1;
//integer = null;
Optional<Integer> integerOptional = Optional.of(integer);
System.out.println(integerOptional.get());
}
@Test
public void test2() {
//通常ofNullable(T t)和orElse(T t)方法一起使用(可以为空)
String str = "里斯";
str = null;
Optional<String> str1 = Optional.ofNullable(str);
String s = str1.orElse("哈哈");
System.out.println(s);
}