前言

Open jdk 和 Oracle jdk
open jdk :
Sun 2006年开源项目 是java se 平台版的开源和免费实现
Oracle jdk:
基于Open jdk 原码的商业版本

JDK8 新特性

1、Lambda 表达式

Lambda是一个匿名函数,可以理解为一段可以传递的代码。

优点:简化匿名内部类的使用,语法更加简单。

Lambda的语法非常简洁,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:

  1. 方法的参数或局部变量类型必须为接口才能使用Lambda
  2. 接口中有且仅有一个抽象方法

1.1.标准格式

(参数类型 参数名称) -> { 
	代码体;
 }

格式说明:

(参数类型 参数名称):参数列表
{代码体;}:方法体
-> :箭头,分隔参数列表和方法体

Lambda与方法的对比

匿名内部类
 public void run() { 
 	System.out.println("aa"); 
 }
 
 Lambda
() -> System.out.println("bb!")
/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/21 16:59
 */
public class Demo01Lambdaintro {
    public static void main(String[] args) {
        //很冗余匿名的方法
        //lambda表达式 体现的是函数式编程思想 ,只需要将要执行的代码放到函数中(函数就是类中的方法)
        //Lambda 就是一个匿名函数,只需要吧代码放到Lambda表达式中

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("原始的线程1");
            }
        }).start();


        // Lambda表达式
        new Thread(() -> {
            System.out.println("sdsdsd");
        }).start();
        //Lambda 表达式的缓存 : 可以简化匿名内部类 ,语法更简单

    }
}

1.2、Lambda和匿名内部类对比

  1. 所需的类型不一样
    匿名内部类,需要的类型可以是类,抽象类,接口
    Lambda表达式,需要的类型必须是接口
  2. 抽象方法的数量不一样
    匿名内部类所需的接口中抽象方法的数量随意
    Lambda表达式所需的接口只能有一个抽象方法
  3. 实现原理不同
    匿名内部类是在编译后会形成class
    Lambda表达式是在程序运行的时候动态生成class

1.3、方法引用

了解Lambda的冗余场景
掌握方法引用的格式
了解常见的方法引用方式

使用Lambda表达式求一个数组的和

public class Demo01MethodRefInfo {

    public static void getMax(int[] arr){
        int sum = 0;
        for (int j : arr) {
            sum += j;
        }
        System.out.println(sum);
    }

    public static void main(String[] args) {
        /*printMax((int[] arr)->{
            getMax(arr);
        });*/
        //如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引
        //用”过去就好了:
        printMax(Demo01MethodRefInfo::getMax);
        //请注意其中的双冒号::写法,这被称为“方法引用”,是一种新的语法。
    }
    public static void printMax(Consumer<int[]> consumer){
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        consumer.accept(arr);
    }
}

符号表示 : ::
符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用
应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。

1.3.1、常见的方法引用方式
  • instanceName::methodName 对象::方法名
  • ClassName::staticMethodName 类名::静态方法
  • ClassName::methodName 类名::普通方法
  • ClassName::new 类名::new 调用的构造器
  • TypeName[]::new String[]::new 调用数组的构造器(和上面有个一样其实)
1.3.2、对象名::引用成员方法

最常见的一种用法,与上例相同。
如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法,

    @Test
    public void test01(){
        Date date = new Date();
        Supplier<Long> supp = () -> {
            return date.getTime();
        };
        System.out.println(supp.get());

        //方法引用的注意事项
        //1. 被引用的方法,参数要和接口中抽象方法的参数一样
       // 2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值
        Supplier<Long> getTime = date::getTime;
        System.out.println(getTime.get());
    }
1.3.3、类名::引用静态方法

由于在java.lang.System类中已经存在了静态方法方法时,可以使用方法引用

    // 类名::静态方法
    @Test
    public void test02(){
        java.util.function.Supplier<Long> supplier = () -> {
            return System.currentTimeMillis();
        };
        System.out.println(supplier.get());
        
        java.util.function.Supplier<Long> currentTimeMillis = System::currentTimeMillis;
        System.out.println( currentTimeMillis.get());
        
    }
1.3.4、类名::引用实例方法

Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,
实际上是拿第一个参数作为方法的调用者。

1.3.5、类名::new引用构造器
1.3.6、数组::new 引用数组构造器
小结

方法引用是对Lambda表达式符合特定情况下的一种缩写,它使得我们的Lambda表达式更加的精简,也可以理解为 Lambda表达式的缩写形式
, 不过要注意的是方法引用只能"引用"已经存在的方法!

2、JDK 8接口新增的两个方法

jdk8以前的接口

interface 接口名 { 
	静态常量; 
	抽象方法; 
}

jdk8以后

interface interfaceName{
	静态常量;
	抽象方法;
	默认方法;
	静态方法;
}

为什么要进行这个修改

就是不利于接口的扩展
白话就是,如果使用接口在以前就必须重写抽象方法,但有时候根本就不需要,

jdk8在Map中新增了forEach方法

这个forEach方法是 默认方法,如果是抽象方法,那么所有实现类都必须实现这个方法,这样是非常浪费的,而默认方法就解决了这个功能

接口中的默认方法实现类可以不重写,可以直接用,或者根据需要重写,方便扩展,开闭原则


 
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

2.1、接口的默认方法

interface interfaceName{
	修饰符 default 返回值类型 方法名(){
	
		代码块;
		
	}
}

使用

  1. 实现类直接调用接口默认方法
  2. 实现类重写接口默认方法

2.2、接口的静态方法

为了方便接口扩展 ,注意静态方法不能被重写

interface interfaceName {
	修饰符 static 返回值类型 方法名(){
	
		代码;
		
	}
}

直接使用接口名调用即可:接口名.静态方法名();

2.3、接口默认方法和静态方法的区别

  1. 默认方法通过实例调用,静态方法通过接口名调用。
  2. 默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法。
  3. 静态方法不能被继承,实现类不能重写接口静态方法,只能使用接口名调用。

如果这个方法需要被实现类继承或重写,使用默认方法,如果接口中的方法不需要被继承就使用静态方

3、内置函数式接口

3.1、为什么要有内置函数

因为Lambda表达式的前提需要函数式接口,而且不需要去关心接口和抽象方法的名字,所以为了Lambda的使用,jdk提供了很多常用的函数式接口。

package org.example.demo03functionalinterface;

import java.lang.reflect.Array;
import java.security.Key;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author zhangyifan
 * @version 8.0
 * @description: 函数式接口
 * @date 2022/3/31 22:45
 */
public class Demo01UserFunctionInterface {
    public static void main(String[] args) {
        //调用函数式接口中的方法
        method(arr -> {
            int sum = 0;
            for (int n : arr){
                sum +=n;
            }
            return sum;
        });
        methodArrayList((ArrayList<Integer> arrayList ) -> {
           //吧集合转数组
           Object[] objects1 =  arrayList.toArray();
           Integer[] objects= Arrays.copyOfRange(objects1,0,objects1.length,Integer[].class);
            /*
             //排序1
             for (int i=0;i< objects.length;i++){
                for (int j=0;j< objects.length;j++){
                    if ( objects[i] > objects[j] ) {
                       // 赋值
                        Integer interger=objects[i];
                       objects[i]=objects[j];
                       objects[j]=interger;
                    }
                }
            }*/
            //排序2
            Arrays.sort(objects);
             ArrayList<Integer> list=new ArrayList<>(Arrays.asList(objects));
            return list ;

        });
    }

    //使用自定义的函数式接口作为方法参数
    public static void method(Operator operator){
        int[] arr = {1,2,3,4};
        int sum = operator.getSum(arr);
        System.out.println("sum="+sum);
    }
    //特别垃圾的排序
    public static void methodArrayList( Operation operation){
        ArrayList arrayList = new ArrayList<Integer>();
        arrayList.add(4);arrayList.add(5);arrayList.add(6);arrayList.add(1);arrayList.add(10);
        ArrayList<Integer> sort = operation.getSort(arrayList);
         sort.forEach(System.out::println);
    }
}

@FunctionalInterface
interface Operator{
    public abstract int getSum(int[] arr);
}

interface Operation{
    public abstract ArrayList<Integer> getSort(ArrayList<Integer> arrayList);
}
3.1.1、Supplier接口
package java.util.function;

 
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类
型的对象数据。

供给型接口,通过接口的get方法得到一个值,没有参数有返回值的接口。

实例

  • 使用Lambda表达式返回数组元素最大值
  • 使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用
    java.lang.Integer 类
package org.example.demo03functionalinterface;

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * @author zhangyifan
 * @version 8.0
 * @description: Supplier函数式接口 供应商
 * @date 2022/4/1 12:32
 */
public class Demo02Supplier {
    public static void main(String[] args) {
        printMax(()->{
            int [] arr= {12,13,14,15,11};
            //先排序,最后就是最大的
            Arrays.sort(arr);//排序方法
            return arr[arr.length - 1];// 最后就是最大的
        });
    }

    public static void printMax(Supplier<Integer> supplier){
        int max = supplier.get();
        System.out.println("max="+ max);
    }
}
3.1.2、Consumer接口

java.util.function.Consumer
接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定。

package java.util.function;

import java.util.Objects;

 
@FunctionalInterface
public interface Consumer<T> {

    
    void accept(T t);

 
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出
NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

实例
使用Lambda表达式将一个字符串转成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:


import java.util.Locale;
import java.util.function.Consumer;

/**
 * @author zhangyifan
 * @version 8.0
 * @description: 消费者
 * @date 2022/4/1 13:33
 */
public class Demo03Comsumer {
    public static void main(String[] args) {
		//只要一个值的时候不用小括号标数据类型
        test(s -> {
            System.out.println(s.toLowerCase(Locale.ROOT));
        });
    }
    public static void test (Consumer<String> consumer){
        consumer.accept("HelloWorld");
    }
}

默认方法:andThen
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合
的情况:

package org.example.demo03functionalinterface;

import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author zhangyifan
 * @version 8.0
 * @description:运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。
 * 当然,通过链式写法可以实现更多步骤的组合。
 * @date 2022/4/1 14:14
 */
public class Demo03AndThen {
    public static void main(String[] args) {
        //Lambd表达式
        test((s) -> {
            System.out.println(s.toLowerCase());//大写
        },s -> {
            System.out.println(s.toUpperCase());//小写
        });
        //Lambda表达式简写 如果只有一个方法 大括号就可以取消 
        test(s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toLowerCase()));
    }
    public static void test (Consumer<String> c1, Consumer<String > c2){
         String str = "HelloWord";
//         c1.accept(str);
//         c2.accept(str);
//        c1.andThen(c2).accept(str);
        c2.andThen(c1).accept(str);
    }
}

3.1.3、Function接口
java.util.function.Function<T,R>

接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

package java.util.function;

import java.util.Objects;

 
@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
 
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

   
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

例如:将String类型转换为Integer类型。

    public static void main(String[] args) {
        test(Integer::parseInt);
    }
    public static void test(Function<String,Integer> function){
        Integer in = function.apply("10");
        System.out.println("in: " + (in +5));
    }

例子: 使用Lambda表达式将字符串转成数字

    public static void main(String[] args) {
        String a = "AAAA";
        Consumer<String> consumer = v -> {
            System.out.println(   v.toLowerCase(Locale.ROOT));
        };
        consumer.accept(a);

        test(v -> {
            System.out.println(v+1);
        });
    }
    public static void test (Consumer<Integer> consumer){
        System.out.println("sssssssssss");
        consumer.accept(1);
    }

默认方法:andThen
将字符转Integer 在乘5

  public static void main(String[] args) {
        getName(Integer::parseInt, (Integer i)-> i* 5)
      ;

    }
    public static void getName (Function<String,Integer> f1,Function<Integer,Integer> f2){
        Integer apply = f1.andThen(f2).apply("6");
        System.out.print("apply"+apply );
    }

请注意,Function的前置条件泛型和后置条件泛型可以相同。

3.1.4、Predicate接口

Predicate接口用于做判断,返回boolean类型的值

package java.util.function;

import java.util.Objects;

@FunctionalInterface
 public interface Predicate<T> { 
     public abstract boolean test(T t); 
} 

例子 使用Lambda判断一个人名如果超过3个字就认为是很长的名字

public class Demo05Predicate {
    public static void main(String[] args) {
        test(s -> s.length() > 3, "古力娜扎");
    }

    public static void test(Predicate<String> predicate, String name) {
        boolean test = predicate.test(name);
        System.out.println("名字答于3不" + test);
    }

}

默认方法: and

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个现“并且”的效果时,可以使用default方法and

默认方法 negate (取反)

默认方法:or (或)

 public class Demo05PredicateAndOrNegate {
    // 使用Lambda表达式判断一个字符串中即包含W,也包含H

    // 使用Lambda表达式判断一个字符串中包含W或者包含H

    // 使用Lambda表达式判断一个字符串中即不包含W
    public static void main(String[] args) {
      /*  test((String s)->{
            return s.contains("W");
        },(String s)->{
            return s.contains("H");
        });*/
        test(s -> s.contains("H"), s -> s.contains("W"));
    }

    public static void test(Predicate<String> predicate, Predicate<String> predicate1) {
        String result = "elloWorld";
        // 使用Lambda表达式判断一个字符串中即包含W,也包含H
   /*     boolean out = predicate.test(result);
        boolean test = predicate.test(result);
        if (out && test){
            System.out.println("即包含W,也包含H");
        }*/
     /*   boolean test = predicate.and(predicate1).test(result);
        if (test){
            System.out.println("即包含W,也包含H");
        }*/

        //使用Lambda表达式判断一个字符串中包含W或者包含H
        // 与and的“与”类似,默认方法or实现逻辑关系中的“或
     /*   boolean test = predicate.or(predicate1).test(result);
        if (test){
            System.out.println("包含W或者包含H");
        }*/

        //“与”、“或”已经了解了,剩下的“非”(取反)也会简单。 negate

        boolean test = predicate.negate().test(result);
        if (test) {
            System.out.println("不包含H");
        }
    }
}

4、Stream流

parallelStream线程问题

5、Optional类

6、新日期类和时间api

6.1 新时间介绍

API

  • LocalDate :表示日期,包含年月日,格式为 2019-10-16
  • LocalTime :表示时间,包含时分秒,格式为 16:38:54.158549300
  • LocalDateTime :表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
  • DateTimeFormatter :日期时间格式化类。
  • Instant:时间戳,表示一个特定的时间瞬间。
  • Duration:用于计算2个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZonedDateTime :包含时区的时间

“Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366
天。此外Java 8还提供了4套其他历法,分别是:”

  • ThaiBuddhistDate :泰国佛教历
  • MinGuo Date :中华民国历
  • JapaneseDate :日本
  • HiRahDate :伊斯兰

6.2 LocalDate

    @Test
    public void test01() {
        //指定日期
        LocalDate localDate = LocalDate.of(2019, 10, 16);
        //2019-10-16
        System.out.println(localDate);


        //当前日期
        LocalDate localDate1 = LocalDate.now();
        //2024-05-25
        System.out.println(localDate1);

        //获取时间信息
        //2024
        System.out.println(localDate1.getYear());
        //5
        System.out.println(localDate1.getMonthValue());
        //25
        System.out.println(localDate1.getDayOfMonth());
        //SATURDAY
        System.out.println(localDate1.getDayOfWeek());
        //146
        System.out.println(localDate1.getDayOfYear());
        //检查年份是否为闰年。
        //true
        System.out.println(localDate1.isLeapYear());

    }

接口返回格式化

@JsonFormat(pattern = "yyyy-MM")
private LocalDate time;

接口传递格式化 yyyy-MM
前台传递月份,自动补齐

    @JsonDeserialize(using = LocalDateDeserializer.class)
    private LocalDate  TimeBegin;

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    public LocalDateDeserializer() {
        this(null);
    }

    public LocalDateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public LocalDate deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
        String dateString = jsonparser.getText();
        // 假设每月的第一天
        if (dateString.isEmpty()) {
              return null;
        }
        dateString += "-01";
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
        return LocalDate.parse(dateString, formatter);
    }
}

6.3 LocalTime

    @Test
    public void test02() {
        LocalTime localTime = LocalTime.of(10, 16, 9);
        System.out.println(localTime);
        // 得到指定的时间
        LocalTime time = LocalTime.of(12,15, 28, 129_900_000);
        System.out.println("time = " + time);

        LocalTime localTime1 = LocalTime.now();
        System.out.println(localTime1);
        System.out.println(localTime1.getHour());
        System.out.println(localTime1.getMinute());
        System.out.println(localTime1.getSecond());
        System.out.println(localTime1.getNano());
        System.out.println(localTime1.plusHours(2));
        System.out.println(localTime1.minusHours(2));
        System.out.println(localTime1.plusMinutes(2));
        System.out.println(localTime1.minusMinutes(2));
        System.out.println(localTime1.plusSeconds(2));
        System.out.println(localTime1.minusSeconds(2));
        System.out.println(localTime1.plusNanos(2));
        System.out.println(localTime1.minusNanos(2));
    }

6.4 LocalDateTime

//LocalDateTime :获取日期时间的信息。LocalDateTime格式为yyyy-mm-ddTHH:mm:ss.n

   @Test
    public void test03() {
        LocalDateTime localDateTime = LocalDateTime.of(2019, 10, 16, 10, 16, 9);
        System.out.println(localDateTime);//2019-10-16T10:16:09

        LocalDateTime localDateTime1 = LocalDateTime.now();
        System.out.println(localDateTime1);//2024-05-13T17:08:27.455

        System.out.println(localDateTime1.getYear());
        System.out.println(localDateTime1.getMonthValue());
        System.out.println(localDateTime1.getDayOfMonth());
        System.out.println(localDateTime1.getDayOfWeek());
        System.out.println(localDateTime1.getDayOfYear());
        //......
    }

修改日期

  @Test//修改日期
    public void test04() {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        LocalDateTime localDateTime = now.withYear(2042);
        System.out.println("修改年份"+localDateTime);//修改年份2042-05-13T17:13:45.887
        localDateTime = now.withMonth(12);
        System.out.println("修改月份"+localDateTime);//修改月份2024-12-13T17:13:45.887



        //再当前对象的基础上加上或减去指定的时间
        LocalDateTime localDateTime3 = now.plusDays(5);
        System.out.println("5天后: " + localDateTime3);
        System.out.println("now == localDateTime: " + (now == localDateTime3));
        System.out.println("10年后: " + now.plusYears(10));
        System.out.println("20月后: " + now.plusMonths(20));
        System.out.println("20年前: " + now.minusYears(20));
        System.out.println("5月前: " + now.minusMonths(5));
        System.out.println("100天前: " + now.minusDays(100));
    }

7、重复注解与类型注解

参考文章

哔哩哔哩:黑马视频

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐