Java 基础-Java8

简介: Java 基础-Java8

Java 8介绍


Java 8的主要新语法特性如下:


  1. Lambda表达式


Lambda表达式使Java程序员能够编写更加简洁、易读和易维护的代码。它是一种匿名函数,可以将其作为参数传递给其他方法或函数。


  1. 方法引用


方法引用是指通过名称来引用现有的方法,从而让代码变得更简洁、易读和易于维护。Java 8中提供了四种不同的方法引用方式:静态方法引用、实例方法引用、构造器引用和超类方法引用。


  1. 接口默认方法


Java 8允许在接口中定义具体实现的默认方法,这样实现类就可以继承该方法的实现,避免了因为新增方法而导致向所有实现类迁移代码的麻烦。


  1. 函数式接口


函数式接口仅包含一个抽象方法的接口,它是Java 8中Lambda表达式的基础。Java 8中提供了许多常用的函数式接口,例如:Predicate、Function、Consumer等。


  1. Stream API


Stream API是Java 8中一个强大的工具,它允许程序员轻松处理集合数据。使用流API可以实现筛选、排序、映射、归约等操作,让代码更加简洁、易读和易于维护。


  1. 时间API


Java 8中引入了新的时间API,它提供了一种更好的方式来处理日期和时间。这个API包含多个类,可以用来处理日历、时间、时区和持续时间等问题。


  1. Optional 类型


Optional类型是一个容器对象,可以包含null或非null值。它为程序员提供一种优雅的方式来处理null检查,从而避免NullPointerException异常。


这些新语法都带来了很大的变化,使得Java编程更加简洁、高效、可读性强和易于维护。


Lambda 表达式


Lambda是一个 匿名函数,我们可以把Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。


基础语法


Java8引入了一个新的操作符: ->,该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分为2部分:


  1. 左侧:Lambda表达式的 参数列表


  1. 右侧:Lambda表达式中 所执行的功能,即Lambda体


语法格式1:无参数,无返回值


public class test {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        r.run();
        System.out.println("--------------------------------------");
        Runnable r1 = () -> System.out.println("Hello Lambda");
        r1.run();
    }
}


语法格式2:有一个参数,并且无返回值,小括号可省略不写,:


public class test {
    public static void main(String[] args) {
        Consumer<String> con = x -> System.out.println(x);
        con.accept("SNOW");
    }
}


语法格式3:有2个以上的参数,有返回值,并且Lambda体中有多条语句 ,有返回值


public class test {
    public static void main(String[] args) {
        Comparator<Integer> com = (x,y) -> {
            System.out.println("函数式接口");
            return Integer.compare(x,y);
        };
    }
}


语法格式4:若Lambda体中只有一条语句,return和大括号都可省略不写


public class test {
    public static void main(String[] args) {
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
    }
}


语法格式5:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”


Lambda表达式需要 函数式接口 的支持!(函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用 @FunctionalInterface 修饰。)


案例


@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private String name;
    private int age;
    private Double salary;
}


public class test {
    public static void main(String[] args) {
        List<Employee> list = Arrays.asList(
                new Employee("1号", 25, 5000.0),
                new Employee("2号", 35, 3000.0),
                new Employee("3号", 35, 2000.0),
                new Employee("4号", 35, 8000.0),
                new Employee("5号", 65, 1000.0)
        );
        Collections.sort(list,(e1,e2)->{
            if (e1.getAge() == e2.getAge()){
                return e1.getName().compareTo(e2.getName());
            }else {
                // 倒序
                return -Integer.compare(e1.getAge(),e2.getAge());
            }
        });
        Iterator<Employee> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}


内置的四大核心函数式接口


  1. Consumer:消费型接口 void accept(T t);


  1. Supplier:供给型接口 T get();


  1. Function(T,R):函数型接口 R apply(T t);


  1. Predicate:断言型接口 boolean test(T t);


案例


  1. Consumer:消费型接口


@Test
public void test(){
    this.happy(1000,m -> {
        System.out.println("我消费了"+ m);
    });
}
public void happy(double money, Consumer<Double> con){
    con.accept(money);
}


  1. Supplier:供给型接口


@Test
public void test(){
    List<Integer> numList = this.getNumList(5, () -> (int) (Math.random() * 100));
    for (Integer integer : numList) {
        System.out.println(integer);
    }
}
//产生指定数量的整数
public List<Integer> getNumList(int num, Supplier<Integer> sup){
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < num; i++) {
        list.add(sup.get());
    }
    return list;
}


  1. Function(T,R):函数型接口


@Test
public void test(){
    System.out.println(this.handler("str", str -> (str + "123")));
}
//处理字符串
public String handler(String str, Function<String,String> fun){
    return fun.apply(str);
}


  1. Predicate:断言型接口


@Test
public void test(){
    List<String> stringList = Arrays.asList("qwe", "123", "hello", "ranhaifeng", "asdasdsaewqewqe");
    List<String> list = filterStr(stringList, s -> (s.length() > 3));
    for (String s : list) {
        System.out.println(s);
    }
}
//将满足条件的字符串放入集合
public List<String> filterStr(List<String> list, Predicate<String> pre){
    List<String> stringList = new ArrayList<>();
    for (String s : list) {
        if (pre.test(s)){
            stringList.add(s);
        }
    }
    return stringList;
}


方法引用与构造器引用


方法引用


方法引用:若 Lambda 体中的内容有方法已经实现了,我们可以使用“方法引用”,可以理解为方法引用是Lambda表达式的另外一种表现形式。


主要有三种语法格式:


  • 对象::实例方法名


  • 类::静态方法名


  • 类::实例方法名


//对象::实例方法
@Test
public void test(){
    Consumer<String> con = x -> {
        System.out.println(x);
    }; 
    PrintStream out = System.out;
    Consumer<String> con1 = out::println;
    Consumer<String> con2 = System.out::println;
    con2.accept("qwe");
} 
//类::静态方法名
@Test
public void test(){
    int x;int y;
    Comparator<Integer> com = (x,y)-> Integer.compare(x,y);
    Comparator<Integer> com1 = Integer::compare;
}
// 类::实例方法名
@Test
public void test(){
    BiPredicate<String,String> bp = (x,y) -> x.equals(y);
    BiPredicate<String,String> bp2 = String::equals;
}


注意:


  • Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!

  • 若Lambda参数列表中的第一参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method


构造器引用


@Test
public void test(){
    Supplier<Object> sup = () -> new Object();
    Supplier<Object> sup2 = Object::new;
    //如果有多个构造器,如何判断是调用的实体类的哪个构造器呢?看下面的注意,即Function(T,R)内部的函数 R apply(T t) 是1个参数,那么就会调用是1个参数的构造器。
    Function<Integer,Employee> fun2 =Employee::new;
}


注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!


并行流与串行流


  1. 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过parallel() 与sequential()在并行流与顺序流之间进行切换。


  1. fork/join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。


  1. Fork/Join框架与传统线程池的区别:采用工作窃取模式(work-stealing)


当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。


相对于一般的线程池实现, fork/join框架的优势体现仕对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能。


public class ForkJoinCalculate extends RecursiveTask<Long> {
    private static final long serialVersionUID = 12313435L;
    private long start;
    private long end;
    private static final long THRESHOLD = 10000;
    public ForkJoinCalculate(long start,long end){
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD){
            long sum = 0;
            for (long i = start; i <= end ; i++) {
                sum+= i;
            }
            return sum;
        }else {
            long middle = (start + end )/2;
            ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
            left.fork(); //拆分子任务,同时压入线程队列
            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1,end);
            right.fork();
            return left.join() + right.join();
        }
    }
}


@Test
public void qwe(){
    Instant start = Instant.now();
    ForkJoinPool forkJoinPool = new ForkJoinPool();
    ForkJoinCalculate forkJoinCalculate = new ForkJoinCalculate(0, 10000000000L);
    Long sum = forkJoinPool.invoke(forkJoinCalculate);
    System.out.println(sum);
    Instant end = Instant.now();
    System.out.println("消耗时间:" + Duration.between(start,end).toMillis());   //5701
}
//普通for循环计算
@Test
public void test2(){
    Instant start = Instant.now();
    long sum = 0L;
    for (int i = 0; i < 10000000000L; i++) {
        sum += i;
    }
    System.out.println(sum);
    Instant end = Instant.now();
    System.out.println("消耗时间:" + Duration.between(start,end).toMillis());   //没跑出来
}
//java8并行流
@Test
public void test3(){
    Instant start = Instant.now();
    LongStream.rangeClosed(0,10000000000L)
            .parallel()
            .reduce(0,Long::sum);
    Instant end = Instant.now();
    System.out.println("消耗时间:" + Duration.between(start,end).toMillis());   //2145
}


Optional 容器



 @Test
    public void test(){
        // Option.of(T t):创建一个Optional实例
        Optional<Person> op = Optional.of(new Person());
        Person person = op.get();
        System.out.println(person); // Person(name=null, age=null, sale=null, status=null)
    }
    @Test
    public void test2(){
        // Option.empty():创建一个空Optional实例
        Optional<Object> op = Optional.empty();
        System.out.println(op.get());   // java.util.NoSuchElementException: No value present
    }
    @Test
    public void test3(){
        // Option.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
        Optional<Object> op = Optional.ofNullable(new Person());
        System.out.println(op.get());   // Person(name=null, age=null, sale=null, status=null)
    }
    @Test
    public void test4(){
        Optional<Person> op = Optional.ofNullable(new Person());
        // Option.isPresent():判断是否包含值
        if (op.isPresent()){
            System.out.println(op.get());   // Person(name=null, age=null, sale=null, status=null)
        }
    }
    @Test
    public void test5(){
        Optional<Person> op = Optional.ofNullable(null);
        // Option.orElse(T t):如果调用对象包含值,返回该值,否则返回t
        Person emp = op.orElse(new Person("qwe", 18, 2000.2, Person.Status.BUSY));
        System.out.println(emp);    // Person(name=qwe, age=18, sale=2000.2, status=BUSY)
    }
    @Test
    public void test6(){
        Optional<Person> op = Optional.ofNullable(null);
        //如果调用对象包含值,返回该值,否则返回s获取的值
        Person person = op.orElseGet(() -> new Person());
        System.out.println(person);     // Person(name=null, age=null, sale=null, status=null)
    }
    @Test
    public void test7(){
        Optional<Person> op = Optional.ofNullable(new Person("qwe", 18, 200.0, Person.Status.BUSY));
        // map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
        Optional<String> str = op.map(e -> e.getName());
        System.out.println(str.get());  // qwe
        // faltMap(Function mapper):map类似,要求返回值必须是Optional,方法中必须用Optional包装
        Optional<String> str2 = op.flatMap(e -> Optional.of(e.getName()));
        System.out.println(str2.get());
 }


接口中的默认方法与静态方法


接口默认方法的”类优先”原则:


若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。


接口冲突。


如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。



接口中的默认方法:


public interface MyFun {
    public static void show(){
        System.out.println("接口中的静态方法");
    }
}


新时间日期 API


LocalDate、LocalTime、LocalDateTime


LocalDateTime


@Test
public void test() {
    /*
    * LocalDateTime
    * */
    LocalDateTime now = LocalDateTime.now();
    System.out.println(now);    // 2021-12-24T14:51:37.161
    //LocalDataTime实例创建
    LocalDateTime ldt = LocalDateTime.of(2021, 12, 23, 13, 23, 32);
    System.out.println(ldt);    // 2021-12-23T13:23:32
    //年份加
    LocalDateTime ldt2 = ldt.plusYears(2);
    System.out.println(ldt2);   // 2023-12-23T13:23:32
    //月份减
    LocalDateTime ldt3 = ldt.minusMonths(2);
    System.out.println(ldt3);   // 2021-10-23T13:23:32
    //获取时间的各个值
    System.out.println(ldt.getYear());          //  2021
    System.out.println(ldt.getMonthValue());    //  12
    System.out.println(ldt.getDayOfMonth());    //  23
    System.out.println(ldt.getHour());          //  13
    System.out.println(ldt.getMinute());        //  23
    System.out.println(ldt.getSecond());        //  32
}


Instant:时间戳(以Unix元年:1970年1月1日 00:00:00 到某个时间之间的毫秒值)


public class test {
    @Test
    public void test() {
        Instant ins = Instant.now();    //  默认获取UTC时区
        System.out.println(ins);
        // 带偏移量,这里表示东八区
        OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));   
        System.out.println(odt);
        System.out.println(ins.toEpochMilli()); // 1640329758791
        Instant instant = Instant.ofEpochSecond(60);    // 1970-01-01T00:01:00Z
        System.out.println(instant);
    }
}


计算时间间隔


  • Duration:计算两个时间之间的间隔


  • Period:计算两个日期之间的间隔


@Test
public void test() {
    Instant ins1 = Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration duration = Duration.between(ins1, ins2);
    System.out.println(duration.toMillis());    //  1009
    LocalDate ld1 = LocalDate.of(2021,12,12);
    LocalDate ld2 = LocalDate.now();
    Period period = Period.between(ld1, ld2);
    System.out.println(period.getYears());  // 0
    System.out.println(period.getMonths()); // 0
    System.out.println(period.getDays());   // 12
    System.out.println();
}


日期的操纵:


TemporalAdiuster :时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。


public class test {
    @Test
    public void test() {
        LocalDateTime ldt = LocalDateTime.now();
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);    // 将日期设置为10
        System.out.println(ldt);    // 2021-12-24T16:23:16.451
        System.out.println(ldt2);   // 2021-12-10T16:22:40.417
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));    // 下一个周日
        System.out.println(ldt3);   // 2021-12-26T16:22:40.417
        //自定义:下一个工作日志
        LocalDateTime ldt5 = ldt.with( l -> {
            LocalDateTime ldt4 = (LocalDateTime) l;
            DayOfWeek dow = ldt4.getDayOfWeek();
            if (dow.equals(DayOfWeek.FRIDAY)) {
                return ldt4.plusDays(3);
            } else if (dow.equals(DayOfWeek.SATURDAY)) {
                return ldt4.plusDays(2);
            } else{
                return ldt4.plusDays(1);
            }
        });
        System.out.println(ldt5);   // 2021-12-27T16:30:55.074
    }
}


DateTimeFormatter:格式化时间/日期


@Test
public void test() {
    DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
    LocalDateTime ldt = LocalDateTime.now();
    String strDate = ldt.format(dtf);
    System.out.println(strDate);    // 2021-12-24
    DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
    String strDate2 = dtf2.format(ldt);
    System.out.println(strDate2);   // 2021年12月24日 16:35:55
    LocalDateTime newDate = LocalDateTime.parse(strDate2, dtf2);
    System.out.println(newDate);    // 2021-12-24T16:38:58
}


时区的处理


@Test
public void test() {
    LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    System.out.println(ldt);    // 2021-12-24T10:43:37.135
    LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
    ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
    System.out.println(zdt);    // 2021-12-24T10:45:08.507+02:00[Europe/Tallinn]
}
相关文章
|
3月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
76 2
|
4月前
|
Java 编译器 C++
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
这篇文章解释了Java能够实现“一次编写,到处运行”的原因,主要归功于Java虚拟机(JVM),它能够在不同平台上将Java源代码编译成的字节码转换成对应平台的机器码,实现跨平台运行。
【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?
|
4月前
|
Java
【Java基础面试四】、介绍一下Java的数据类型
这篇文章介绍了Java的数据类型,包括8种基本数据类型(整数、浮点、字符、布尔)和3类引用数据类型(数组、类、接口),并提供了基本数据类型所占内存空间和数据范围的详细信息。
|
1月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
480 37
|
2月前
|
前端开发 小程序 Java
java基础:map遍历使用;java使用 Patten 和Matches 进行正则匹配;后端传到前端展示图片三种情况,并保存到手机
这篇文章介绍了Java中Map的遍历方法、使用Pattern和matches进行正则表达式匹配,以及后端向前端传输图片并保存到手机的三种情况。
26 1
|
2月前
|
Oracle Java 关系型数据库
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
3月前
|
缓存 安全 Java
【Java面试题汇总】Java基础篇——基础、修饰符和关键字(2023版)
Java的特点和优点,、Java 8的新特性、面向对象、基本数据类型和引用类型、自动拆装箱与自动装箱、==与equals()的区别、为什么重写equals()就要重写hashcode()、抽象类和接口的区别、重载和重写的区别、四种引用方式、wt()和sleep()的区别、java方法是值传递还是引用传递?访问修饰符、static、final、this和super、volatile的用法及原理
【Java面试题汇总】Java基础篇——基础、修饰符和关键字(2023版)
|
2月前
|
Java 编译器 API
从Java 8到Java 17,这些新特性让你的代码起飞!
【10月更文挑战第10天】在软件开发领域,Java作为一种历史悠久且广泛使用的编程语言,不断进化以适应新的需求和挑战。从Java 8到Java 17,每一次版本更新都带来了诸多新特性和改进,极大地提升了开发效率和代码质量。今天,我们就来一起探讨这些新特性,看看它们是如何让我们的代码“起飞”的。
181 0
下一篇
DataWorks