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 {
@Test
public void test01() {
Abstractclass abstractclass = new Abstractclass() {
@Override
double 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 {
@Test
public void test01() {
InterfaceExample interfaceExample = new InterfaceExample() {
@Override
public double calculate(int x) {
return x + x;
}
};
double calculate = interfaceExample.calculate(4);
System.out.println(calculate);
System.out.println(interfaceExample.sqrt(25));
}
}
高级用法才能体现出他的骚气 如下
@Test
public 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");
@Test
public void test01() {
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
System.out.println(list);
}
Collections 工具类提供了静态方法 sort 方法,入参是一个 List 集合,和一个 Comparator 比较器,以便对给定的 List 集合进行排序。上面的示例代码创建了一个匿名内部类作为入参,这种类似的操作在我们日常的工作中随处可见
Java 8 中不再推荐这种写法,而是推荐使用 Lambda 表达: 如下
@Test
public void Test02() {
Collections.sort(list, (String a, String b) -> {
return b.compareTo(a);
});
}
以上还可以更加简短的写法 如下
Collections.sort(names, (String a, String b) -> b.compareTo(a));
@Test
public void Test03() {
Collections.sort(list, (String a, String b) -> b.compareTo(a));
System.out.println(list);
}
为了追求极致,我们还可以让它再短点
@Test
public void Test04() {
list.sort((x,y)->x.compareTo(y));
System.out.println(list);
}
其实底层使用了类型推断机制 这样,你连入参的类型都可以省略啦
4:函数式接口出现
可以看到通过Lambda可以开发出同样功能的逻辑但是代码却很简单,那么Jvm是如何进行类型推断,并且找到对应的方法呢?所谓函数式接口(Functional Interface)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。
总结:为了保证一个接口明确的被定义为一个函数式接口(Functional Interface),我们需要为该接口添加注解:
@FunctionalInterface。
@Test
public void test01() {
IConver<String, String> iConver = new IConver<String, String>() {
@Override
public String convert(String param) {
return param;
}
};
System.out.println(iConver);
}
4:内置的函数式接口
JDK 1.8 API 包含了很多内置的函数式接口。其中就包括我们在老版本中经常见到的 Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterface 注解,以用来支持 Lambda 表达式。
Predicate 断言
@Test
public 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"); // true
boolean foo1 = s.negate().test("foo"); // negate否定相当于!true
Predicate<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):
@Test
public void test12() {
Function<String, Integer> toInteger = Integer::valueOf; //转Integer
Function<String, String> backToString = toInteger.andThen(String::valueOf); //转String
Function<String, String> afterToStartsWith = backToString.andThen(new Something()::startsWith); //截取第一位
String apply = afterToStartsWith.apply("123");// "123"
System.out.println(apply);
}
Suppliers
Supplier 与 Function 不同,它不接受入参,直接为我们生产一个指定的结果,有点像生产者模式:
@Test
public 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,我们需要提供入参,用来被消费
@Test
public void test14() {
// 参照物,方便知道下面的Lamdba表达式写法
Consumer<Person> greeter01 = new Consumer<Person>() {
@Override
public 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, Luke
Consumer<Person> greeter03 = new MyConsumer<Person>()::accept; // 也可以通过定义类和方法的方式去调用,这样才是实际开发的姿势
greeter03.accept(new Person("Luke", "Skywalker")); //Hello, Luke
}
5:Optionals
Optional 它不是一个函数式接口,设计它的目的是为了防止空指针异常(NullPointerException),要知道在 Java 编程中,空指针异常可是臭名昭著的。
@Test
public void test1() {
Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.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 是一个断言的中间操作,它能够帮我们筛选出我们需要的集合元素。
@Test
public 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倍性能!
顺序流处理 比较慢
@Test
public 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));
}
并行流处理 嘎嘎快