JDK8新特性

    技术2024-10-20  26

    1、Lambda表达式

    Lambda表达式:特殊的匿名内部类,语法更加简洁。Lambda表达式允许把函数作为一个抽象方法的参数(函数作为方法参数传递),将代码块像数据一样传递。

    Lambda表达式的操作符: 格式:(箭头操作符)->,将箭头分为两个部分

    左侧:(参数1,参数2…)表达式列表;右侧: { }内部是方法体;

    注意事项: 1)形参列表的数据类型会自动的推断; 2)如果形参列表为空,只需保留(); 3)如果形参只有一个,,可以省略()不写,只需要参数的名称即可; 4)如果执行语句只有一句,且无返回值,{ }可以省略不写,若有返回值,则想省去{},则同时省略return,且执行语句也保证只有一句; 5)Lambda表达式不会生成一个单独的内部类文件;

    /** * Lambda表达式的使用 */ public class TestLambda { public static void main(String[] args) { System.out.println("-----lambda表达式-----"); // Runnable接口的匿名内部类方式 Runnable runnable=new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"子线程"); } }; new Thread(runnable).start(); //Lambda表达式优化Runnable接口匿名内部类 new Thread(()-> System.out.println("子线程")).start(); //Comparator比较器的匿名内部类 Comparator<String> comparator=new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.length()-o2.length(); } }; //使用Lambda表达式优化简写Comparator的匿名内部类 Comparator<String> comparator1=(o1, o2) -> o1.length()-o2.length(); } }

    2、函数式接口

    如果一个接口只有一个抽象方法,则该接口为函数式接口,只有函数式接口中才可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法。

    @Functionallnterface注解:作用为检测接口符合函数式接口。

    /** * 函数式接口:有且只能有一个抽象方法 */ @FunctionalInterface interface USB{ public abstract void run(); } public class TestFunctionInterface { public static void main(String[] args) { System.out.println("-------匿名内部类--------"); USB usb=new USB() { @Override public void run() { System.out.println("匿名内部类方式......"); } }; usb.run(); System.out.println("----Lambda表达式----"); USB usb1=()-> System.out.println("Lambda表达式....."); usb1.run(); System.out.println(usb1); } }

    常见的函数式接口: 举例:

    /** * 常见函数式接口的使用: */ public class Demo { public static void main(String[] args) { System.out.println("1.消费型接口:"); System.out.println("--------匿名内部类的方式-----"); Consumer<Integer> consumer=new Consumer<Integer>() { @Override public void accept(Integer integer) { for (int i = 90; i <100 ; i++) { System.out.println("消费了:"+i); } } }; consumer.accept(90); //Lambda方式 System.out.println("-----Lambda方式-----"); Consumer<Integer> consumer1=t-> System.out.println("消费了:"+t); consumer1.accept(67); System.out.println("2.供给型接口"); System.out.println("--------匿名内部类的方式-----"); Supplier<Integer> supplier1=new Supplier<Integer>() { @Override public Integer get() { Random random=new Random(); int i = random.nextInt(10); System.out.println("随机数:"+i); return i; } }; supplier1.get(); System.out.println("-----Lambda方式-----"); Supplier<Integer> supplier2=()-> new Random().nextInt(10); Integer integer = supplier2.get(); System.out.println("随机数:"+integer); //函数式接口Function String s1 = handleString(s -> s.toUpperCase(), "hello"); System.out.println(s1); } public static String handleString(Function<String,String> fuuction, String str){ return fuuction.apply(str); } }

    3、方法引用

    方法引用是Lambda表达式的一种简写。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用引用方法。常见形式: 对象::实例属性 类::静态对象 类::实例方法 类::new

    第一种: 对象::实例属性

    //首先来看下第一种 类名::静态方法名 为了演示我们自定义了一个Student类 public class Student { private String name; private int score; public Student(){ } public Student(String name,int score){ this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public static int compareStudentByScore(Student student1,Student student2){ return student1.getScore() - student2.getScore(); } }

    Student类有两个属性name和score并提供了初始化name和score的构造方法,并且在最下方提供了两个静态方法分别按score和name进行比较先后顺序。 接下来的需求是,按着分数由小到大排列并输出,在使用方法引用前,我们先使用lambda表达式的方式进行处理:

    Student student1 = new Student("zhangsan",60); Student student2 = new Student("lisi",70); Student student3 = new Student("wangwu",80); Student student4 = new Student("zhaoliu",90); List<Student> students = Arrays.asList(student1,student2,student3,student4); students.sort((o1, o2) -> o1.getScore() - o2.getScore()); students.forEach(student -> System.out.println(student.getScore()));

    sort方法接收一个Comparator函数式接口,接口中唯一的抽象方法compare接收两个参数返回一个int类型值,下方是Comparator接口定义

    @FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); }

    我们再看下Student类中定义的compareStudentByScore静态方法

    public static int compareStudentByScore(Student student1,Student student2){ return student1.getScore() - student2.getScore(); }

    同样是接收两个参数返回一个int类型值,而且是对Student对象的分数进行比较,所以我们这里就可以 使用类名::静态方法名 方法引用替换lambda表达式

    students.sort(Student::compareStudentByScore); students.forEach(student -> System.out.println(student.getScore()));

    第二种 对象::实例方法名

    我们再自定义一个用于比较Student元素的类

    public class StudentComparator { public int compareStudentByScore(Student student1,Student student2){ return student2.getScore() - student1.getScore(); } }

    StudentComparator中定义了一个非静态的,实例方法compareStudentByScore,同样该方法的定义满足Comparator接口的compare方法定义,所以这里可以直接使用 对象::实例方法名 的方式使用方法引用来替换lambda表达式

    StudentComparator studentComparator = new StudentComparator(); students.sort(studentComparator::compareStudentByScore); students.forEach(student -> System.out.println(student.getScore()));

    第三种 类名::实例方法名 这种方法引用的方式较之前两种稍微有一些不好理解,因为无论是通过类名调用静态方法还是通过对象调用实例方法这都是符合Java的语法,使用起来也比较清晰明了。那我们带着这个疑问来了解一下这个比较特殊的方法引用。 现在再看一下Student类中静态方法的定义

    public static int compareStudentByScore(Student student1,Student student2){ return student1.getScore() - student2.getScore(); } 虽然这个方法在语法上没有任何问题,可以作为一个工具正常使用,但是有没有觉得其在设计上是不合适的或者是错误的。这样的方法定义放在任何一个类中都可以正常使用,而不只是从属于Student这个类,那如果要定义一个只能从属于Student类的比较方法下面这个实例方法更合适一些

    public int compareByScore(Student student){ return this.getScore() - student.getScore(); } 接收一个Student对象和当前调用该方法的Student对象的分数进行比较即可。现在我们就可以使用 类名::实例方法名 这种方式的方法引用替换lambda表达式了

    students.sort(Student::compareByScore); students.forEach(student -> System.out.println(student.getScore())); 这里非常奇怪,sort方法接收的lambda表达式不应该是两个参数么,为什么这个实例方法只有一个参数也满足了lambda表达式的定义(想想这个方法是谁来调用的)。这就是 类名::实例方法名 这种方法引用的特殊之处:当使用 类名::实例方法名 方法引用时,一定是lambda表达式所接收的第一个参数来调用实例方法,如果lambda表达式接收多个参数,其余的参数作为方法的参数传递进去。 结合本例来看,最初的lambda表达式是这样的

    students.sort((o1, o2) -> o1.getScore() - o2.getScore()); 那使用 类名::实例方法名 方法引用时,一定是o1来调用了compareByScore实例方法,并将o2作为参数传递进来进行比较。是不是就符合了compareByScore的方法定义。

    第四种 类名::new 也称构造方法引用,和前两种类似只要符合lambda表达式的定义即可,回想下Supplier函数式接口的get方法,不接收参数有返回值,正好符合无参构造方法的定义

    @FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); } Supplier<Student> supplier = Student::new;

    上面就是使用了Student类构造方法引用创建了supplier实例,以后通过supplier.get()就可以获取一个Student类型的对象,前提是Student类中存在无参构造方法。

    4、Stream流

    什么是Stream?

    流(Stream)中保存对集合或数组数据的操作。和集合类似 ,但集合中保存的是数据。

    Stream的特点: 1.Stream自己不会存储元素; 2.Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream. 3.操作是延迟执行的.

    Stream的创建:

    通过Collection对象的stream()或者parallelStream()方法

    通过Arrays类的stream()方法

    通过stream接口的of()、iterate()、generate()方法

    常用流的使用:

    public class TestStream { public static void main(String[] args) { System.out.println("----创建Stream----"); List<Person> list=new ArrayList<>(); list.add(new Person("利拉德", 30)); list.add(new Person("詹姆斯", 35)); list.add(new Person("Cj麦克勒姆", 28)); list.add(new Person("欧文", 29)); list.add(new Person("詹姆斯", 35)); //list.stream().forEach(System.out::println); //单线程 list.parallelStream().forEach(System.out::println); //多线程 System.out.println("Arrays方式创建Stream:"); String[] as={"渣男","渣女"}; Arrays.stream(as).forEach(System.out::println); System.out.println("Stream接口的of方式创建Stream:"); Stream.of(320,"迪奥999").forEach(System.out::println); System.out.println("Stream接口的iterate迭代流方式创建Stream:"); Stream.iterate(3,x->x+2).limit(5).forEach(System.out::println); System.out.println("Stream接口产生Stream对象"); Stream.generate(()->new Random().nextInt(5)).limit(3).forEach(System.out::println); System.out.println("IntStream接口of产生对象"); IntStream.of(3,6,8).forEach(System.out::println); System.out.println("IntStream接口range"); IntStream.range(3, 8).forEach(System.out::println); System.out.println("IntStream接口rangeClose"); IntStream.rangeClosed(3, 8).forEach(System.out::println); System.out.println("--中间过程--"); System.out.println("filter:"); //过滤 list.stream().filter(person->person.getAge()>30).forEach(System.out::println); System.out.println("limit:");//限制 list.stream().limit(3).forEach(System.out::println); System.out.println("skip:");//跳过 list.stream().skip(2).forEach(System.out::println); System.out.println("distinct:");//去重,需重写hashCode和equals list.stream().distinct().forEach(System.out::println); System.out.println("sorted:");//排序 list.stream().sorted((o1,o2)->o1.getAge()-o2.getAge()).forEach(System.out::println); System.out.println("map:");//映射 list.stream().map(person->person.getName()).forEach(System.out::println); System.out.println("parallel:");//多线程 list.stream().parallel().forEach(System.out::println); System.out.println("reduce:");//规约,汇总 Optional<String> reduce = list.stream().map(person -> person.getName()).reduce((o1, o2) -> o1 + o2); System.out.print(reduce.get()); System.out.println(" "); System.out.println("collect:收集"); List<String> list2 = list.stream().map(p>p.getName()).collect(Collectors.toList()); System.out.print(list2); } }

    5.新时间API

    以往时间API存在问题:线程安全、设计混乱;本地化日期时间API:LacalDate、LacalTime、LacalDateTime时间戳:Instant时区:ZoneIdDate、Instant、LocalDateTime的转换DateTimeFormatter:格式化

    代码验证:

    /** * 线程安全问题: * 使用多线程测试时间安全 * @author */ public class SafeDate { public static void main(String[] args) throws ExecutionException, InterruptedException { //旧时间API //SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd"); //新时间API DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy:MM:dd"); ExecutorService pool = Executors.newFixedThreadPool(10); Callable<LocalDate> callable=new Callable<LocalDate>() { @Override public LocalDate call() throws Exception { // synchronized (sdf) { // return sdf.parse("2020:07:04"); // } return LocalDate.parse("2020:07:04",dtf); } }; List<Future<LocalDate>> list=new ArrayList<>(); for (int i = 0; i <10 ; i++) { Future<LocalDate> future= pool.submit(callable); list.add(future); } for (Future<LocalDate> dateFuture: list) { System.out.println(dateFuture.get()); } pool.shutdown(); } }

    Date、Instant、LocalDateTime的转换

    /** * LacalDateTime的使用 */ public class NewDate01 { public static void main(String[] args) { //1.创建本地时间 LocalDateTime localDateTime=LocalDateTime.now(); System.out.println(localDateTime); System.out.println(localDateTime.getYear()); System.out.println(localDateTime.getMonth()); System.out.println(localDateTime.getDayOfMonth()); //时间戳Instant Instant instant=Instant.now(); System.out.println(instant.toString()); //ZoneId时区 Set<String> zoneids= ZoneId.getAvailableZoneIds(); for (String s:zoneids) { System.out.println(s); } System.out.println(ZoneId.systemDefault().toString()); //Date-->Instant-->LocalDateTime Date date=new Date(); Instant instant1 = date.toInstant(); System.out.println(instant1); LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()); System.out.println(localDateTime1); //LocalDateTime-->Instant-->Date Instant instant2=localDateTime1.atZone(ZoneId.systemDefault()).toInstant(); System.out.println(instant2); Date form=Date.from(instant2); System.out.println(form); } }

    DateTimeFormatter:格式化:

    /** * DataTimeFormatter的使用: */ public class TestDataTimeFarmatter { public static void main(String[] args) { //1.创建DateTimeFormatter对象 DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"); //2.获取当前时间 LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime); //3.把字符串转换成解析为时间 LocalDateTime parse = LocalDateTime.parse("2020:07:05 23:44:55",dft); System.out.println(parse); } }
    Processed: 0.009, SQL: 11