java8常用特性总结

时间API java.time.*

原来的弊端:
  • 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
  • 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
  • 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
新版重要的2个API:
  • Local(本地)系列 − 简化了日期时间的处理,没有时区的问题。
  • Zoned(时区)系列 − 通过制定的时区处理日期时间。

local系列(LocalDateTime,LocalDate,LocalTime)

代码如下:

// 获取当前的日期时间
        LocalDateTime currentTime = LocalDateTime.now();
        System.out.println("当前时间: " + currentTime);
        //获取当前日期
		LocalDate date1 = currentTime.toLocalDate();
        System.out.println("date1: " + date1);
		//获取年月日
        Month month = currentTime.getMonth();
        int monthValue =currentTime.getMonthValue();
        int year = currentTime.getYear();
        int day = currentTime.getDayOfMonth();
        int seconds = currentTime.getSecond();
        System.out.println("年:"+year+" 英文月:" + month+" 数字月:"+month.getValue()+" 数字月:"+monthValue+" 日: " + day +" 秒: " + seconds);
		//设定日期,设定为2012-4-10 *这个不会修改原值
     	LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012).withMonth(3);
        System.out.println("date2: " + date2);
        // 2019-4-26
        LocalDate date3 = LocalDate.of(2019, 4, 26);
        LocalDate date4 = LocalDate.of(2019, Month.APRIL, 26);
        System.out.println("date3: " + date3);
        System.out.println("date4: " + date4);
        // 22 小时 15 分钟
        LocalTime time1 = LocalTime.of(22, 15);
        System.out.println("time1: " + time1);
        // 解析字符串
        LocalTime time2 = LocalTime.parse("20:15:30");
        System.out.println("time2: " + time2);
postgresql对应关系:

LocalDateTime -> timestamp

LocalDate -> date

LocalTime -> time

zoned系列(ZonedDateTime , ZoneId)

代码如下

// 获取当前时间日期
        ZonedDateTime zone = ZonedDateTime.parse("2019-04-28T10:15:30+05:30[Asia/Shanghai]");
        System.out.println("zone: " + zone);

        ZoneId id = ZoneId.of("Europe/Paris");
        System.out.println("ZoneId: " + id);

        ZoneId currentZone = ZoneId.systemDefault();
        System.out.println("当期时区: " + currentZone);

接口默认方法+静态方法

接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。

default +方法名+代码块 or static+方法名+代码块

代码如下

public interface Animal {
    default void print(){
        System.out.println("animal");
    }
    static void staticPrint(){
        System.out.println("staticPrint");
    }
}
public interface Dog {
    default void print(){
        System.out.println("dog");
    }
    static void staticPrint(){
        System.out.println("staticPrint");
    }
}
//如果实现多个接口,同一个方法的话,实现类必须复写
public class Bone implements Dog,Animal {

    @Override
    public void print() {
        Dog.super.print();
        Animal.super.print();
        System.out.println("bone");
    }
    private void test(){
        Dog.staticPrint();
    }
}
//单一的可以不用实现
public class Bone2 implements Dog {
}

lambda表达式

“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

写法:

// 1. 不需要参数,返回值为 5
// () -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
// x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
// (x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
// (int x, int y) -> x + y
// 只有表达式内部代码块只有一行时可以省略大括号和关键字
// (int x, int y) -> {return x+y;}
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
// (String s) -> System.out.print(s)

特点:

1.写法优化了。

2.编译不像匿名类会产生class文件。

3.对外部变量使用更灵活了。this关键字指向外部变量与匿名内部类有区别。

函数式接口

定义:有且仅有一个抽象方法(Object的public,default,static 除外)的接口。

写法:使用@FunctionalInterface注解

自定义的函数式接口:

@FunctionalInterface
public interface Eat {
    void eat();
    boolean equals(Object obj);
    default void print(){
        System.out.println("eat");
    }
    static void sayHello(){
        System.out.println("hello");
    }
}

常用的4中函数式接口:

1.Consumer

//1.抽象方法,传一个对象,执行,没有返回值。
void accept(T t);
//2.连续执行,即f(g(x))的意思,先执行本体,后执行传入的
 default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }

2.Supplier

T get();

不传入,反而得到一个对象。

3.Function<T, R>

//1.传入点东西,执行,得到点东西。 
R apply(T t);
//2.和andThen相反,先执行传入的那一个,后执行本体
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
//3.不解释
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
//4.f(x)=x 的既视感,也不太明白使用场景是啥
static <T> Function<T, T> identity() {
        return t -> t;
    }

4.Predicate

//1.传入一个对象,执行逻辑判断后得到一个布尔值, 
boolean test(T t);

//2.逻辑且的意思
default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
//3.逻辑非的意思
 default Predicate<T> negate() {
        return (t) -> !test(t);
    }
//4.逻辑或的意思
default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
//5.判断是不是null,不是null掉obejct的equals方法,目前还不知道使用场景
static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

img

方法引用

定义:方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。

当Lambda表达式中只是执行一个方法调用时,不用Lambda表达式,直接通过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式。

注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。

4种类型

类型 示例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 containingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new

ps:这里引用构造方法,返回的不是一个实例对象,是一个实例对象的工厂。

img

Demo:

public class Test {
   public static void main(String args[]){
      List names = new ArrayList();
      names.add("Google");
      names.add("Runoob");
      names.add("Taobao");
      names.add("Baidu");
      names.add("Sina");
        
      names.forEach(System.out::println);
   }
}

Optional

简单的说,optional就是一个对类的加工,一种包装器,主要用来解决NPE的问题。

简单用法:

Demo

public class Test {
   public static void main(String args[]){
   
      Test test = new Test();
      Integer value1 = null;
      Integer value2 = new Integer(10);
        
      // Optional.ofNullable - 允许传递为 null 参数
      Optional<Integer> a = Optional.ofNullable(value1);
       //请少用下面这个 
      // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
      Optional<Integer> b = Optional.of(value2);
      System.out.println(test.sum(a,b));
   }
    
   public Integer sum(Optional<Integer> a, Optional<Integer> b){
    
      //也请少用
      // Optional.isPresent - 判断值是否存在
      System.out.println("第一个参数值存在: " + a.isPresent());
      System.out.println("第二个参数值存在: " + b.isPresent());
        
      // Optional.orElse - 如果值存在,返回它,否则返回默认值
      Integer value1 = a.orElse(new Integer(0));
        
      //Optional.get - 获取值,值需要存在
      Integer value2 = b.get();
      return value1 + value2;
   }
}

高级用法

Demo

public class User {
    private long id;
    private String name;
    private int age;
    private Optional<Long> phone;
    private Optional<String> email;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 省略setter和getter
}
public class OptionalDemo {

    public static void main(String[] args) {
        User user = new User("jh",25);
        Optional<User> optUser= Optional.ofNullable(user);
        String name = optUser.map(User::getName).orElse("no name");
        //long phone = optUser.map(User::getPhone).orElse(-1L); 这个是错误的
        //map(User::getPhone) 返回的是 Optional<Optional<Long>>
        long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);

        long phone2 = optUser.flatMap(User::getPhone).orElse(-1L);
        optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));
    }

}

ps:

Optional是一个final类,未实现任何接口,所以如果我们定义的类有序列化的需求,那么因为Optional没有实现Serializable接口,这个时候执行序列化操作就会有问题:

public class User implements Serializable{
    private long id;
    private String name;
    private int age;
    private Optional<Long> phone;  // 不能序列化
    private Optional<String> email;  // 不能序列化
}

采用这种方式替换:

private long phone;

public Optional<Long> getPhone() {
    return Optional.ofNullable(this.phone);
}

Stream

流的概念定义:支持数据处理操作的源生成的元素序列。

Stream不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。

Stream操作:1.生成stream。2.中间操作(各种处理)。3.结束操作(形成最终结果)。

img

特点:

1 . 不是数据结构,不会保存数据。

  1. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
  2. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
  3. 生成的stream只使用一次,即在一个结束操作以后不可再次使用。

demo:

//常用的流的获取
        //Collection下的
        List<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream(); //获取一个顺序流
        Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
        //arrays下的
        Integer[] nums = new Integer[10];
        Stream<Integer> stream = Arrays.stream(nums);
        //静态方法 of,iterate,generate
        Stream<Integer> stream1 = Stream.of(1,2,3,4,5,6);
        Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
        stream2.forEach(System.out::println); // 0 2 4 6 8 10
        Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
        stream3.forEach(System.out::println);
		//从文件中读取
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        Stream<String> lineStream = reader.lines();
        

 		//常用的中间操作
        Stream<Integer> stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
        Stream<Integer> newStream = stream.filter(s -> s > 5) //过滤 6 6 7 9 8 10 12 14 14
                .distinct() //去重 6 7 9 8 10 12 14
                .skip(2) //跳过前n个 9 8 10 12 14
                .limit(2) //获取前n个 9 8
				.map(n->n+1) // 10 9 映射成一个新的流
				.sorted(); //排序,有重载方法可自定义排序规则
            //扁平化
		Stream<List<Integer>> inputStream = Stream.of(
                Arrays.asList(1),
                Arrays.asList(2, 3),
                Arrays.asList(4, 5, 6)
        );
        Stream<Integer> outputStream = inputStream.
                flatMap((childList) -> childList.stream());

		//常用的结束操作
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        boolean allMatch = list.stream().allMatch(e -> e > 10); //false
        boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
        boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true
        Integer findFirst = list.stream().findFirst().get(); //1
        Integer findAny = list.stream().findAny().get(); //1
        long count = list.stream().count(); //5
        Integer max = list.stream().max(Integer::compareTo).get(); //5
        Integer min = list.stream().min(Integer::compareTo).get();//1

		//常用结束操作
 		boolean allMatch = list.stream().allMatch(e -> e > 10); //false
        boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
        boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true
        
        Integer findFirst = list.stream().findFirst().get(); //1
        Integer findAny = list.stream().findAny().get(); //1
        
        long count = list.stream().count(); //5
        Integer max = list.stream().max(Integer::compareTo).get(); //5
        Integer min = list.stream().min(Integer::compareTo).get();//1
		list.stream().forEach(System.out::println);

 		 List<String> list2 = Arrays.asList("1","2","3","4");
        List<Integer> collect = 	list2.stream().map(Integer::parseInt).collect(Collectors.toList());
		//ps Collector 工具库:Collectors很强大,建议大家下来看看