1:抽象类的变化
前言:
接口里只能做方法定义不能有方法的实现,抽象类的方法不需要继承类必须去实现的一种方式。
定义一个抽象类TestAbstractclass 如下
package com.lm.jdk8.Abstractclass;/*** 抽象类*/
public abstract class Abstractclass {abstract double calculate(int a);// 求平方double sqrt(int a) {return Math.sqrt(a);}
}
使用的时候如下
package com.lm.jdk8.Abstractclass;import org.junit.jupiter.api.Test;public class TestAbstractclass {@Testpublic void test01() {Abstractclass abstractclass = new Abstractclass() {@Overridedouble calculate(int x) {return x * x;}};double calculate = abstractclass.calculate(10);System.out.println(calculate);double sqrt = abstractclass.sqrt(2);System.out.println(sqrt);}
}
运行如下:
2:接口中定义方法-jdk1.8之前不支持
在jdk1.8,不仅可以定义接口,还可以在接口中提供默认的实现。这一个小小的改变却让整个抽象设计都随着改变了!
package com.lm.jdk8.interfacePackage;/*** 接口里面定义的方法必须是 default关键字的*/
public interface interfaceExample {double calculate(int x);//平方default double sqrt(int x) {return Math.sqrt(x);}}
使用方式一
package com.lm.jdk8.interfacePackage;import org.junit.jupiter.api.Test;public class TestInterfaceExample {@Testpublic void test01() {InterfaceExample interfaceExample = new InterfaceExample() {@Overridepublic double calculate(int x) {return x + x;}};double calculate = interfaceExample.calculate(4);System.out.println(calculate);System.out.println(interfaceExample.sqrt(25));}
}
高级用法才能体现出他的骚气 如下
@Testpublic void test02() {InterfaceExample interfaceExample = x -> x * x;System.out.println(interfaceExample.calculate(4));}
3:Lambda 表达式
为什么会有Lambda 表达式的出现 如上的抽象类和接口给了Lambda 表达式的启发 所以你会从各个我们以前的List、Set等等所有接口中看到默认的方法实现。
List<String> list = Arrays.asList("A", "B", "C");@Testpublic void test01() {Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String a, String b) {return b.compareTo(a);}});System.out.println(list);}
Collections 工具类提供了静态方法 sort 方法,入参是一个 List 集合,和一个 Comparator 比较器,以便对给定的 List 集合进行排序。上面的示例代码创建了一个匿名内部类作为入参,这种类似的操作在我们日常的工作中随处可见
Java 8 中不再推荐这种写法,而是推荐使用 Lambda 表达: 如下
@Testpublic void Test02() {Collections.sort(list, (String a, String b) -> {return b.compareTo(a);});}
以上还可以更加简短的写法 如下
Collections.sort(names, (String a, String b) -> b.compareTo(a));@Testpublic void Test03() {Collections.sort(list, (String a, String b) -> b.compareTo(a));System.out.println(list);}
为了追求极致,我们还可以让它再短点
@Testpublic void Test04() {list.sort((x,y)->x.compareTo(y));System.out.println(list);}
其实底层使用了类型推断机制 这样,你连入参的类型都可以省略啦
4:函数式接口出现
可以看到通过Lambda可以开发出同样功能的逻辑但是代码却很简单,那么Jvm是如何进行类型推断,并且找到对应的方法呢?所谓函数式接口(Functional Interface)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。
总结:为了保证一个接口明确的被定义为一个函数式接口(Functional Interface),我们需要为该接口添加注解:
@FunctionalInterface。
@Testpublic void test01() {IConver<String, String> iConver = new IConver<String, String>() {@Overridepublic String convert(String param) {return param;}};System.out.println(iConver);}
4:内置的函数式接口
JDK 1.8 API 包含了很多内置的函数式接口。其中就包括我们在老版本中经常见到的 Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterface 注解,以用来支持 Lambda 表达式。
Predicate 断言
@Testpublic void test01() {Predicate<String> s=a->a.length()>0;System.out.println(s.test("o"));System.out.println(s.negate().test("o"));boolean foo0 = s.test("foo"); // trueboolean foo1 = s.negate().test("foo"); // negate否定相当于!truePredicate<Boolean> nonNull = Objects::nonNull;Predicate<Boolean> isNull = Objects::isNull;System.out.println(nonNull.test(null));Predicate<String> isEmpty = String::isEmpty;Predicate<String> isNotEmpty = isEmpty.negate();}
Functions 函数
Function 函数式接口的作用是,我们可以为其提供一个原料,他给生产一个最终的产品。通过它提供的默认方法,组合,链行处理(compose, andThen):
@Testpublic void test12() {Function<String, Integer> toInteger = Integer::valueOf; //转IntegerFunction<String, String> backToString = toInteger.andThen(String::valueOf); //转StringFunction<String, String> afterToStartsWith = backToString.andThen(new Something()::startsWith); //截取第一位String apply = afterToStartsWith.apply("123");// "123"System.out.println(apply);}
Suppliers
Supplier 与 Function 不同,它不接受入参,直接为我们生产一个指定的结果,有点像生产者模式:
@Testpublic void test13() {Supplier<Person> personSupplier0 = Person::new;personSupplier0.get();Supplier<String> personSupplier1 = Something::test01;personSupplier1.get();Supplier<String> personSupplier2 = new Something()::test02;System.out.println(personSupplier2);}
Consumers
Consumer,我们需要提供入参,用来被消费
@Testpublic void test14() {// 参照物,方便知道下面的Lamdba表达式写法Consumer<Person> greeter01 = new Consumer<Person>() {@Overridepublic void accept(Person p) {System.out.println("Hello, " + p.firstName);}};Consumer<Person> greeter02 = (p) -> System.out.println("Hello, " + p.firstName);greeter02.accept(new Person("Luke", "Skywalker")); //Hello, LukeConsumer<Person> greeter03 = new MyConsumer<Person>()::accept; // 也可以通过定义类和方法的方式去调用,这样才是实际开发的姿势greeter03.accept(new Person("Luke", "Skywalker")); //Hello, Luke}
5:Optionals
Optional 它不是一个函数式接口,设计它的目的是为了防止空指针异常(NullPointerException),要知道在 Java 编程中,空指针异常可是臭名昭著的。
@Testpublic void test1() {Optional<String> optional = Optional.of("bam");optional.isPresent(); // trueoptional.get(); // "bam"optional.orElse("回调"); // "bam"optional.ifPresent((s) -> System.out.println(s.charAt(0)));Optional<Person> optionalPerson = Optional.of(new Person());optionalPerson.ifPresent(s -> System.out.println(s.firstName));}
6:Stream 流
简单来说,我们可以使用 java.util.Stream 对一个包含一个或多个元素的集合做各种操作。这些操作可能是 中间操作 亦或是 终端操作。 终端操作会返回一个结果,而中间操作会返回一个 Stream 流。
初始化数据
List<String> list = new ArrayList<>();StreamTest() {list.add("ddd2");list.add("aaa2");list.add("bbb1");list.add("aaa1");list.add("bbb3");list.add("ccc");list.add("bbb2");list.add("ddd1");}
Filter 过滤
Filter 的入参是一个 Predicate, 上面已经说到,Predicate 是一个断言的中间操作,它能够帮我们筛选出我们需要的集合元素。
@Testpublic void test17() {list.stream().filter((s) -> s.startsWith("a")).forEach(System.out::println);System.out.println(list);}
Sorted 排序
@Test
public void test18() {stringCollection.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);
}
对象属性排序
tCjpoolSecurityReportList = tCjpoolSecurityReportList.stream().sorted(Comparator.comparing(TCjpoolSecurityReport::getPubdate).reversed()).collect(Collectors.toList());
过滤指定属性
public static String convertO32Market(String secuMarket) {if (StringUtils.isNotEmpty(secuMarket)) {List<O32SecuMarketEnum> filters = Arrays.stream(values()).filter((item) -> {return StringUtils.equals(secuMarket, item.getIssMktCode());}).collect(Collectors.toList());return CollectionUtils.isNotEmpty(filters) ? filters.get(0).getO32MktCode() : secuMarket;} else {return null;}}
7:Parallel-Streams 并行流
流可以是顺序的,也可以是并行的。顺序流上的操作在单个线程上执行,而并行流上的操作在多个线程上并发执行。
下面的示例演示了使用并行流来提高性能是多么的容易。亲测提升了1倍性能!
顺序流处理 比较慢
@Testpublic void test01() {int max = 1000000;List<String> values = new ArrayList<>(max);for (int i = 0; i < max; i++) {UUID uuid = UUID.randomUUID();values.add(uuid.toString());}// 纳秒long t0 = System.nanoTime();long count = values.stream().sorted().count();System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);System.out.println(String.format("顺序流排序耗时: %d ms", millis));}
并行流处理 嘎嘎快