前言
由于博主在铜三铁四时期面试了一家**公司,然后对方面试官噼里啪啦的从数据结构到算法,从java源码到架构设计,足足面了一个小时。这里不得不恭维一下,该司面试官还是属于追求技术,有一定实力的程序猿。嘿嘿,但是到了博主反问业务的时候,他们说就是做一个数据对接,规则清洗,然后数据可视化的平台。这不是典型的面试造航母,工作拧螺丝。%*&%¥#!…(妈卖批)
好吧,不吐槽了,别人问的问题我确实不太会,现在来简单了解一下Java8的一些比较好用的特性吧,开干!!!!
主要知识点
由于Java8新特性比较多,我们只讲解一些比较实用的特性,其他的希望各位自己慢慢研究吧 ( * _ * )
- default关键字
- lamda表达式和函数式编程
- stream流
- 新的日期API
- 重复注解
- optional的使用
default关键字
在Java8之前,interface里面只允许有接口的定义,不允许进行接口的实现。但是自从default关键字的出现,interface里面也可以定义接口实现了,这样一来对于接口扩展带来了便利。然后接口实现类的实现方法里面都可以调用接口里面定义的default方法。代码如下:
接口定义:
public interface DefaultInteface {
void complete();
default Integer add(){
return new Random().nextInt();
}
}
接口实现类:
public class DefaultTest implements DefaultInteface{
@Override
public void complete() {
this.add();
}
public void test(){
add();
}
}
lamda表达式和函数式接口
lamda表达式的出现简化了代码量,使代码更简洁,lamda表达式在集合遍历、线程定义、比较器里面用的比较多,具体代码如下:
//线程定义
public void test(){
Runnable runnable = ()-> System.out.println("显示定义线程");
new Thread(runnable).start();;
}
//集合遍历
public void sort(){
List<String> list = new ArrayList<>();
list.forEach(s -> System.out.println(s));
}
//比较器
public void add(){
Comparator<Integer> comparable = (x, y)->y.compareTo(y);
comparable.compare(1,2);
}
Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,因此它包含有一个有输入而无输出(无返回值)的accept接口方法;
除accept方法,它还包含有andThen这个方法
JDK源码定义如下:
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
andThen这个方法是作用是:指定在调用当前Consumer后是否还要调用其它的Consumer;
public static void main(String[] args) {
//定义第一个Consumer
Consumer<Integer> consumer1 = (param) -> {System.out.println(param);};
//定义第二个Consumer
Consumer<Integer> consumer2 = (param) -> {System.out.println(param * param);};
//consumer1可以连续的调用自己
//consumer1.andThen(consumer1).andThen(consumer1).accept(3);
//打印出 3 3 3
//consumer1可以调用自己后调用consumer2
consumer1.andThen(consumer1).andThen(consumer2).accept(3);
//打印出3 3 9
}
stream流
stream流是一种相对于迭代器代码更简洁,效率更高的工具。它可以对一个集合进行遍历、排序、分组、聚合等操作。下面我们将演示使用迭代器和stream之间的性能差异,以100000个数据的List集合为例:
public class StreamClass {
public static void main(String[] args) {
List<String> nameList = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
nameList.add("");
}
long start = System.currentTimeMillis();
for (String s : nameList) {
System.out.print(s);
}
long end = System.currentTimeMillis();
System.out.println();
System.out.println("迭代器时间:" + (end - start));
start = System.currentTimeMillis();
nameList.stream().forEach(name ->
System.out.print(""));
end = System.currentTimeMillis();
System.out.println();
System.out.println("stream流时间:" + (end - start));
}
}
我们可以看到在10w级,100w级数据,stream流的性能远远强于迭代器,
10w级数据,迭代器10ms,stream流5ms
100w级数据,迭代器62ms,stream流42ms
新的日期API
Java8之前我们用的日期时间api是Date,DateTime,Calendar,SimpleDateFormat这些。Java8的时候提供了LocalDate,LocalTime,LocalDateTime,Duration,Period这些API。其中:
- LocalDate获取日期
- LocalTime获取时间
- LocalDateTime同时获取日期和时间
- Duration计算两个时间之间的差值
- Period计算两个日期之间的差值
代码示例如下:
public static void main(String[] args) {
//获取当前日期
System.out.println(LocalDate.now());
//获取当前时间
System.out.println(LocalTime.now());
//获取当前日期时间
System.out.println(LocalDateTime.now());
//计算两个时间的时间差
LocalTime localTime = LocalTime.of(1,2);
LocalTime localTime2 = LocalTime.of(5,2);
System.out.println(Duration.between(localTime,localTime2));
//计算两个日期差
LocalDate localDate = LocalDate.of(2022,1,1);
LocalDate localDate2 = LocalDate.of(2022,5,12);
System.out.println(Period.between(localDate,localDate2));
}
注解
Java8新增类型注解和重复注解。
- 重复注解:可以在一个类上或方法上重复多次使用的注解
自定义可重复注解:使用@Repeatable元注解,参数为可重复注解的容器
@Repeatable(MyAnnotations.class)
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "java8";
}
注解容器定义,注意:可重复注解的容器的Target和Retention必须要比可重复注解的范围大
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
使用:
public class TestAnnotation {
@Test
public void test() throws NoSuchMethodException, SecurityException {
Class<TestAnnotation> clazz = TestAnnotation.class;
Method method = clazz.getMethod("show");
MyAnnotation[] annotationsByType = method.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation myAnnotation : annotationsByType) {
System.out.println(myAnnotation.value());
}
}
@MyAnnotation("hello")
@MyAnnotation("world")
public void show() {
}
}
- 类型注解:在入参的地方可以使用自定义注解,只需要将自定义注解的目标类型标注为ElementType.PARAMETER
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "java8";
}
public void show(@MyAnnotation("str") String str) {
System.out.println(str);
}
optional的使用
optional的方式是java提供的对于类之间调用链提供的辅助类,使用optional可以很好避免一些NullPointerException异常的出现,使用方式如下:
public class Person {
private Man man;
public Man getman(){
return man;
}
}
public class Man {
private Son son;
public Son getSon(){
return son;
}
}
public class Son {
private int age;
public int getAge(){
return age;
}
}
//正常获取年龄
Person person = new Person();
System.out.println(person.getman().getSon().getAge());
//optional方式获取年龄
Person person1 = new Person();
System.out.println(Optional.ofNullable(person)
.map(Person::getman)
.map(Man::getSon)
.map(Son::getAge)
.orElse(null));