java1.8新特性

本文深入介绍了Java 8的主要特性,包括Lambda表达式、函数式接口、方法引用、Stream API以及接口中的默认方法。Lambda简化了代码,Stream API提供了数据处理的新方式,而新的日期时间API改进了旧版日期处理的不便。通过实例解析,帮助开发者更好地理解和应用这些新特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、简介

二、函数式接口

1. 什么是函数式(Functional)接口

2.函数式接口的特点

3. 理解函数式接口

1)Runnable接口

三、Lambda表达式

1.概念

2.匿名内部类

3. lambda

4. 变量作用域 lambda

四、内置函数式接口

1. supplier接口

2.consumer消费型接口

3. predicate推断型接口

4. function函数型接口

五、方法引用

1.什么是方法引用

2. ⽅法引⽤和lambda表达式

六、 Stream API

1.简介

2.生成流

3.forEach

4. map

5. filter

6. sort

7. Collectors

七、接口中的默认⽅法

1.默认方法简介

2.语法

3. 多个默认方法

4. 静态默认方法

八、 Java8日期时间

一、简介

            Java 8 (⼜称为 jdk 1.8) 是 Java 语⾔开发的⼀个主要版本。 Java 8 是oracle公司于2014年3⽉发布,可以看成是⾃Java 5 以来最具⾰命性的版本。Java 8为Java语⾔、编译 器、类库、开发⼯具与JVM带来了⼤量新特性。

速度更快

  • 代码更少(增加了新的语法:Lambda 表达式)

  • 强⼤的 Stream API

  • 便于并⾏

  • 最⼤化减少空指针异常:Optional

  • Nashorn引擎,允许在JVM上运⾏JS应⽤

二、函数式接口

1. 什么是函数式(Functional)接口

   • 只包含⼀个抽象⽅法的接口 ,称为函数式接口。

   • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出⼀个受检异常(即:⾮运⾏时 异常),那么该异常需要在⽬标接口的抽象⽅法上进⾏声明)。

   • 我们可以在⼀个接口上使⽤ @FunctionalInterface 注解,这样做可以检查它是否是⼀个函数式接口。 

   • 在java.util.function包下定义了Java 8 的丰富的函数式接口

2.函数式接口的特点

   • 接口有且仅有⼀个抽象⽅法

   • 允许定义静态⽅法

   • 允许定义默认⽅法

   • 允许java.lang.Object中的public⽅法

   • 该注解不是必须的,如果⼀个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够 更好地让编译器进⾏检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会 报错

3. 理解函数式接口

   • Java从诞⽣⽇起就是⼀直倡导“⼀切皆对象”,在Java⾥⾯⾯向对象(OOP)编程是⼀切。但是随着python、 scala等语⾔的兴起和新技术的挑战,Java不得不做出调整以便⽀持更加⼴泛的技术要求,也即 java不但可 以⽀持OOP还可以⽀持OOF(⾯向函数编程)

   • 在函数式编程语⾔当中,函数被当做⼀等公⺠对待。在将函数作为⼀等公⺠的编程语⾔中,Lambda表达式 的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附 于⼀类特别的对象类型⸺函数式接口。

   • 简单的说,在Java8中,Lambda表达式就是⼀个函数式接口的实例,这就是Lambda表达式和函数式接口 的关系。也就是说,只要⼀个对象是函数式接口的实例,那么该对象就可以⽤Lambda表达式来表⽰。 以前⽤匿名实现类表⽰的现在都可以⽤Lambda表达式来写。

1)Runnable接口

packagejava.lang;
@FunctionalInterface
public interface Runnable{
public abstract void run();
}

 原始方法实现:

public class RunnableLambda implements Runnable {
 @Override
    public void run() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        RunnableLambda runnableLambda = new RunnableLambda();
        new Thread(runnableLambda).start();
    }
}

lambda⽅式实现:

public class RunnableLambdaNew{
 public static void main(String[] args) {
        newThread(() -> {
            System.out.println("helloworld");
        }).start();
    }
}

三、Lambda表达式

1.概念

         Lambda是⼀个匿名函数,可以理解为⼀段可以传递的代码(将代码像数据⼀样传递);可以写出更简洁、更灵活的 代码;作为⼀种更紧凑的代码⻛格,是Java语⾔表达能⼒得到提升。

2.匿名内部类

public class Person{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ",age=" + age +
                '}';
    }
}
public class Test{
  public static void main(String[] args) {
        Person person1 = new Person(11, "andy");
        Person person2 = new Person(8, "jack");
        Person person3 = new Person(3, "lucy");
        TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Persono1, Persono2) {
                if (o1.getId() > o2.getId()) {
                    return 1;
                }
                if (o1.getId() < o2.getId()) {
                    return -1;
                }
                return 0;
               }
           });
        treeSet.add(person1);
        treeSet.add(person2);
        treeSet.add(person3);
        for (Person person : treeSet) {
            System.out.println(person.toString());
        }
    }
 }

3. lambda

Lmabda表达式的语法总结:() -> {};

λ表达式有三部分组成:参数列表,箭头(->),以及⼀个表达式或语句块。

public class Person2 {

    private String name;
    private int age;

    public Person2() {
    }

    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ",age=" + age +
                '}';
    }
}
public class TreeSetTest {
    public static void main(String[] args) {
        Person2 p1=new Person2("andy",38);
        Person2 p2=new Person2("jack",24);
        Person2 p3=new Person2("lucy",48);
/*TreeSet<Person2> personSet=new TreeSet<>(new Comparator<Person2>(){
@Override
public int compare(Person2 o1,Person2 o2){
return-(o1.getAge()-o2.getAge());
}
});*/
//TreeSet<Person2> personSet=new TreeSet<>((Person2 person1,Person2 person2)->{return-(person1.getAge()-person2.getAge());});
//类型推断
//TreeSet<Person2> personSet=new TreeSet<>((person1,person2)->{return-(person1.getAge()-person2.getAge());});
//⽅法体只有⼀⾏语句可以省略⼤括号,且有return都可以可以省略
        TreeSet<Person2> personSet=new TreeSet<>((person1,person2)->person1.getAge()- person2.getAge());
//⽅法引⽤
//TreeSet<Person2> personSet=new TreeSet<>(Comparator.comparingInt(Person2::getAge));
        personSet.add(p1);
        personSet.add(p2);
        personSet.add(p3);
        for(Person2 person:personSet){
        System.out.println("person="+person);
        }
    }
}

以下是lambda表达式的重要特征:

   • 可选类型声明:不需要声明参数类型,编译器可以统⼀识别参数值。

   • 可选的参数圆括号:⼀个参数⽆需定义圆括号,但多个参数需要定义圆括号。

   • 可选的⼤括号:如果主体包含了⼀个语句,就不需要使⽤⼤括号。

   • 可选的返回关键字:如果主体只有⼀个表达式返回值则编译器会⾃动返回值,⼤括号需要指定        表达式返回 了⼀个数值。

//1.不需要参数,返回值为 5
()->5
//2.接收⼀个参数(数字类型),返回其2倍的值
x->2*x
//3.接受2个参数(数字),并返回他们的差值
(x,y)->x–y
//4.接收2个int型整数,返回他们的和
(intx,inty)->x+y
//5.接受⼀个 string对象,并在控制台打印,不返回任何值(看起来像是返回void)
(Strings)->System.out.print(s)

实战案例,计算两数之和,之差等。

   步骤⼀:定义⼀个接口,内部包含⼀个calc的⽅法

public interface CalcInterface{
  int calc(inta,intb);
}
public class Calc{
  public static int calc(int x,int y,CalcInterface calcInterface){
      return calc Interface.calc(x,y);
   }
}

步骤⼆:测试类

public class CalcTest {
    public static void main(String[] args) {
        int m = 1;
        int result2 = Calc.calc(1, 2, new CalcInterface() {
            @Override
            public int calc(int x, int y) {
                System.out.println("m=" + m);
//m=1;
                return x + y;
            }
        });
        System.out.println("result2=" + result2);
        int i = 1;
        int result = Calc.calc(10, 2, (intx, inty) -> {
//可以取值
            System.out.println("i=" + i);
//不能修改⼀个局部变量的值,像匿名内部类⼀样,局部都是final的
//i=2;
            System.out.println("i=" + i);
            return x + y;
        });
        System.out.println("result=" + result);
        result = Calc.calc(10, 2, (int x, int y) -> x - y);
        System.out.println("result=" + result);
        result = Calc.calc(10, 2, (int x, int y) -> x * y);
        System.out.println("result=" + result);
        result = Calc.calc(10, 2, (int x, int y) -> x / y);
        System.out.println("result=" + result);
    }
}

4. 变量作用域 lambda

           表达式只能引⽤标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变 量,否则会编译错误。

int aaa=200;
CalcInterface addCounter=(int a,int b)->{
int c=a+b;
System.out.println("引⽤局部变量aaa:"+aaa);
//lambda标识内部⽆法修改外部的局部变量的值
/* System.out.println("修改局部变量aaa:"+ aaa + +);*/
return c;
};

四、内置函数式接口

java.util.function 它包含了很多类,⽤来⽀持 Java的 函数式编程,该包中的函数式接口有4类函数式接口:

1. supplier接口

生产型接口,没有参数,有返回值

@FunctionalInterface
public interface Supplier<T> {
/**
  *Getsaresult.
  *
  *@returnaresult
  */
  T  get();
}

获取系统时间:

public static Date testSupplierDate(Supplier<Date> supplier){
return supplier.get();
}
//获取当前的系统时间
System.out.println(MyFunctionTest.testSupplierDate(()->{
return new Date();
}));

2.consumer消费型接口

消费一个数据,没有返回值

@FunctionalInterface
public interface Consumer<T> {
    /**
     *Performs this operation on the given argument.
     *
     *@param t the input argument
     */
    void accept(T t);

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

输入一个用户名,将用户名转大写,打印用户名

/**
 *2、消费型接⼝,接收⼀个参数,没有返回值
 *@paramusername
 *@paramconsumer
 */
public static void testConsumer(String username,Consumer<String> consumer){
    consumer.accept(username);
}
​
MyFunctionTest.testConsumer("Iloveyou",(str)->{
  String s=str.toUpperCase();
  System.out.println(s);
});

​

3. predicate推断型接口

根据⼊参做判断,并返回boolean值的时候使⽤

例如根据性别判断是否符合⼥朋友标准

public static boolean judgeSex(Person person, Predicate<Person> predicate) {
        return predicate.test(person);
}
Person person=new Person();
person.setId(123);
person.setName("路西");
person.setSex("⼥");
boolean sex=MyFunctionTest.judgeSex(person,(p)->{
  if(p.getSex().equals("⼥")){
    return true;
  }
  return false;
});
System.out.println(sex);

4. function函数型接口

此接口接收⼀个参数,返回⼀个结果,参数和结果的数据类型根据实际情况稳定。

例如:输⼊⼀个字符串,返回⼀个整形

public static Integer testFunction(String s , Function<String,Integer> function){
        return function.apply(s);
    }
Integer integer=MyFunctionTest.testFunction("12345",(str) -> {
  return Integer.parseInt(str);
});
System.out.println(integer.getClass());

五、方法引用

1.什么是方法引用

⽅法引⽤提供了⾮常有⽤的语法,可以直接引⽤已有Java类或对象(实例)的⽅法或构造器。与lambda联合使⽤, ⽅法引⽤可以使语⾔的构造更紧凑简洁,减少冗余代码。

语法:

2. ⽅法引⽤和lambda表达式

六、 Stream API

1.简介

Java 8 API添加了⼀个新的抽象称为流Stream,可以让你以⼀种声明的⽅式处理数据。

Stream 使⽤⼀种类似⽤ SQL 语句从数据库查询数据的直观⽅式来提供⼀种对 Java 集合运算和表达的⾼阶抽 象。

Stream API可以极⼤提⾼Java程序员的⽣产⼒,让程序员写出⾼效率、⼲净、简洁的代码。

这种⻛格将要处理的元素集合看作⼀种流,流在管道中传输,并且可以在管道的节点上进⾏处理,⽐如筛选,排 序,聚合等。 

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前 ⾯处理的结果。

Stream(流)是⼀个来⾃数据源的元素队列并⽀持聚合操作 

   • 元素是特定类型的对象,形成⼀个队列。Java中的Stream并不会存储元素,而是按需计算。

   • 数据源 流的来源。可以是集合,数组,I/O channel,产⽣器generator 等。

   • 聚合操作 类似SQL语句⼀样的操作,⽐如filter, map, reduce, find, match, sorted等。 和以前的Collection操作不同,Stream操作还有两个基础的特征:

   • Pipelining: 中间操作都会返回流对象本⾝。这样多个操作可以串联成⼀个管道,如同流式⻛格(fluent style)。这样做可以对操作进⾏优化,⽐如延迟执⾏(laziness)和短路( short-circuiting)。    • 内部迭代:以前对集合遍历都是通过Iterator或者for-Each的⽅式, 显式的在集合外部进⾏迭代,这叫做 外部迭代。Stream提供了内部迭代的⽅式,通过访问者模式(Visitor)实现。

2.生成流

在 Java 8 中, 集合接口有两个⽅法来⽣成流:

stream() − 为集合创建串⾏流。适合存在线程安全问题、阻塞任务、重量级任务,以及需要使⽤同⼀事务的 逻辑

parallelStream() − 为集合创建并⾏流。适合没有线程安全问题、较单纯的数据处理任务。

public class MethodUse{
 public static void main(String[] args) {
        List<String> string List = new ArrayList<>();
        stringList.add("zhangsan");
        stringList.add("gaojieling");
        stringList.add("chengqingqing");
        stringList.add("zhanghaotian");
     /* for(String  s : stringList){
     System.out.println(s);
     }*/
        /*stringList.forEach(System.out::println);*/
        //⽣成串⾏流
        Stream<String> stream = stringList.stream();
        //⽣成并⾏流 
        Stream<String> parallelStream = stringList.parallelStream();
    }
}

3.forEach

Stream 提供了新的⽅法 forEach 来迭代流中的每个数据

 List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
for(
    String string:strings)
    {
        System.out.println(string);
    }
System.out.println("=============");
strings.stream().

    forEach(System.out::println);

4. map

map ⽅法⽤于映射每个元素到对应的结果,以下代码⽚段使⽤ map 输出了元素对应的平⽅数:

 List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
    //Function<Integer,Integer>myFunction=(Integera)->{returna*a;};
    Stream<Integer> integerStream = numbers.stream()

5. filter

ilter ⽅法⽤于通过设置的条件过滤出元素。以下代码⽚段使⽤ filter ⽅法过滤出空字符串

 List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
System.out.println("没有过滤空字符串之前的");
strings.stream().forEach(System.out::println);
System.out.println("过滤空字符串之后的");
//Predicate<String>myFunction=(Stringstr)->{returnstr.length()>0;};
//lambda表达式优化之前
strings.stream().filter((Stringstr)->{returnstr.length() > 0;}).forEach(System.out::println);
//lambda表达式优化之后
strings.stream().filter(str -> str.length()>0).forEach(System.out::println);

6. sort

sorted ⽅法⽤于对流进⾏排序。

List<String>strings=Arrays.asList("abc","","bc","efg","abcd","","jkl");
System.out.println("排序之前");
strings.stream().filter(str->str.length()>0).forEach(System.out::println);
System.out.println("排序之后");
strings.stream().filter(str->str.length()>0).sorted().forEach(System.out::println);

7. Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可⽤于返回列表或字符串:

//collector将流转换成集合
Stream<String>stringStream=strings.stream().filter(str->str.length()>0);
System.out.println(stringStream);
List<String>list=strings.stream().filter(str->str.length()>0).collect(Collectors.toList());
System.out.println(list);
//collector将流转换成字符串
StringmyString=strings.stream().filter(str->str.length()>0).collect(Collectors.joining(":"));
System.out.println(myString);

七、接口中的默认⽅法

1.默认方法简介

Java 8 新增了接口的默认⽅法。java8之前的接口中的⽅法都是抽象⽅法。

简单说,默认⽅法就是接口可以有实现⽅法,而且不需要实现类去实现其⽅法。

我们只需在⽅法名前⾯加个 default 关键字即可实现默认⽅法。

2.语法

public interface ComputerInteface{
   void playGame();
   void movie();
   default void coding(){
     System.out.println("此处是实现");
   };
}

3. 多个默认方法

⼀个接口有默认⽅法,考虑这样的情况,⼀个类实现了多个接口,且这些接口有相同的

默认⽅法,以下实例说明了这种情况的解决⽅法

如果⼀个实现类实现了两个接口,并且两个接口中的默认实现同名,

public interface ComputerInteface{
   voidplayGame();
   voidmovie();
   default void coding(){
   System.out.println("此处是电脑的实现");
   }
}
public interface PadInteface{
   void playGame();
   void movie();
   default void coding(){
       System.out.println("此处是pad的实现");
   }
}
public class MySuperComputer implements 
 ComputerInteface,PadInteface{
 @Override
 public void playGame(){
 }
 @Override
 public void movie(){
 }
 /**
  *⽅案⼀:⾃⼰实现的
  */
  /* @Override
  public void coding(){
   System.out.println("我的超级电脑的编程实现");
   }*/
   /**
  *⽅案⼆:需要选择⼀个接⼝中的默认实现
  */
  @Override
  public void coding(){
   PadInteface.super.coding();
  }
}

4. 静态默认方法

Java 8 的另⼀个特性是接口可以声明(并且可以提供实现)静态⽅法。例如:

    public interface ComputerInteface{
        void playGame();
        void movie();
        default void coding(){
            System.out.println("此处是电脑的实现");
        };
        static void homework(){
            System.out.println("此处是静态⽅法");
        }
    }

八、 Java8日期时间

Java 8 在 java.time 包下提供了很多新的 API。它的⽬标是克服旧的⽇期/时间API实现中所有的缺陷,新的⽇期/ 时间API的⼀些设计原则如下:

   •不变性:新的⽇期/时间API中,所有的类都是不可变的,这种设计有利于并发编程。

   •关注点分离:新的API将⼈可读的⽇期时间和机器时间(unix timestamp)明确分离,它为⽇期(Date)、时 间(Time)、⽇期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。

   •清晰:在所有的类中,⽅法都被明确定义⽤以完成相同的⾏为。举个例⼦,要拿到当前实例我们可以使⽤ now()⽅法,在所有的类中都定义了format()和parse()⽅法,而不是像以前那样专⻔有⼀个独⽴的类。为了 更好的处理问题,所有的类都使⽤了⼯⼚模式和策略模式,⼀旦你使⽤了其中某个类的⽅法,与其他类协同 ⼯作并不困难。

   •实⽤操作:所有新的⽇期/时间API类都实现了⼀系列⽅法⽤以完成通⽤的任务,如:加、减、格式化、解析、从 ⽇期/时间中提取单独部分等操作。

   •可扩展性:新的⽇期/时间API是⼯作在ISO-8601⽇历系统上的,但我们也可以将其应⽤在⾮IOS的⽇历上。

新的java.time包涵盖了所有处理⽇期,时间,⽇期/时间,时区,时刻(instants),过程(during)与时钟(clock)的 操作。

  //可以使⽤ of⽅法构建它们的实例,如下⾯创建了⼀个 2019-9-2221:42:59东⼋区 的时间对象
    LocalDate localDate = LocalDate.of(2019, Month.SEPTEMBER, 22);
    LocalTime localTime = LocalTime.of(21, 42, 59);
    LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
    ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
    //获取现在的时间,这是⼀个静态⽅法
    LocalDate now = LocalDate.now();
    //每个实例可以获取它们的 part信息,如获取年
    int year = localDate.getYear();
    //可以修改 part信息,这将返回⼀个新对象,如增加⼀年
    LocalDate localDatePlus = localDate.plusYears(1);
    //设置 part信息,也会返回新的对象,如设置为 2017年
    LocalDate localDateWithYear = localDate.withYear(2017);
    //⽐较两个⽇期 isAfter,isBefore
    boolean after = localDate.isAfter(LocalDate.now());
//格式化⽇期时间
//yyyy-MM-dd
System.out.println(now.format(DateTimeFormatter.ISO_DATE));
//yyyy-MM-ddTHH:mm:ss
System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));
//yyyy-MM-ddHH:mm:ss
System.out.println(now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
//⽇期解析
System.out.println(LocalDate.parse("2019-09-22"));
System.out.println(LocalDateTime.parse("2019-09-22T21:05:22"));
System.out.println(LocalDateTime.parse("2019-09-22
        21:05:22",DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值