java8新特性 方法引用 流 optional等

总览

  1. jdk1.8对hashMap等map集合的优化
  2. Lambda表达式
  3. 函数式接口
  4. 方法引用和构造器调用
  5. Stream API
  6. 并行流和串行流
  7. Optional容器
    1. Java 8引入Optional类来防止空指针异常
    2. Optional类最先是由Google的Guava项目引入的。
    3. Optional类实际上是个容器:它可以保存类型T的值或者保存null
    4. 使用Optional类我们就不用显式进行空指针检查了。
  8. 接口中的默认方法和静态方法
  9. 新时间日期API
  10. 定义可重复的注解
    1. Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次
    2. Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次
    3. 重复注解机制本身需要用@Repeatable注解
    4. Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
  11. 扩展注解的支持
    1. Java 8扩展了注解的上下文
    2. 几乎可以为任何东西添加注解,包括
      1. 局部变量
      2. 泛型类
      3. 父类与接口的实现
      4. 连方法的异常也能添加注解。
  12. jvm中的方法区变成了元数据区
    1. PermGen变成了Metaspace
  13. 更好的类型推测机制
    1. (不需要太多的强制类型转换了)
  14. 编译器优化
    1. Java 8 将方法的参数名加入了字节码中
    2. 这样在运行时 通过反射 就能获取到参数名
    3. 只需要在编译时使用-parameters参数。

对hashMap等map集合的优化

hashMap数据结构的优化:
原来的hashMap采用的数据结构是哈希表(数组+链表)

hashMap默认大小是16,

一个0-15索引的数组,如何往里面存储元素,

  • 首先调用元素的hashcode方法,计算出哈希码值,
  • 经过哈希算法算成数组的索引值,
  • 如果对应的索引处没有元素,直接存放,
  • 如果有对象在,那么比较它们的equals方法比较内容
    • 如果内容一样,后一个value会将前一个value的值覆盖,
    • 如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,
    • 在某些情况下如果链表无限下去,那么效率极低,碰撞是避免不了的

加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生。

在1.8之后,在数组+链表+红黑树来实现hashmap,

  • 当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入。

除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾

ConcurrentHashMap (锁分段机制)

concurrentLeveljdk1.8采用CAS算法(无锁算法,不再使用锁分段),

数组+链表中也引入了红黑树的使用

Lambda表达式

lambda表达式本质上是一段匿名内部类也可以是一段可以传递的代码

先来体验一下lambda最直观的优点:简洁代码

 //匿名内部类
  Comparator<Integer> cpt = new Comparator<Integer>() {
      @Override
      public int compare(Integer o1, Integer o2) {
          return Integer.compare(o1,o2);
      }
  };

  TreeSet<Integer> set = new TreeSet<>(cpt);

  System.out.println("=========================");

  //使用lambda表达式
  Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
  TreeSet<Integer> set2 = new TreeSet<>(cpt2);

只需要一行代码,极大减少代码量!!

我们发现实际上循环过滤方法的核心只有if语句中的条件判断

其他均为模版代码,

每次变更一下需求,都需要新增一个方法,

然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?

方法1:使用lamabda表达式

定义一个MyPredicate接口

public interface MyPredicate <T> {
    boolean test(T t);
}

定义过滤方法:

    //过滤方法。
    public List<Product> filterProductByPredicate(List<Product> list, MyPredicate<Product> mp){
        //定义一个list
        List<Product> prods = new ArrayList<>();
        //遍历参数中的list
        for (Product prod : list){
            //如果经过了 test方法的 返回为 true
            if (mp.test(prod)){
                //就 假如到 定义的 list中
                prods.add(prod);
            }
        }
        //返回 定义的list
        return prods;
    }

使用lambda表达式进行过滤

@Test
public void test4(){
        //定义一个list
        List<Product> proList = new ArrayList<>();
        Product pp=new Product();
        pp.setPrice(500);
        proList.add(pp);

        List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
        for (Product pro : products){
            System.out.println(pro.getPrice());
        }
  }//500 是 满足 < 8000 ,会被加入到 list中。
//filterProductByPredicate 调用的 上面的方法。第一个参数为:proList。
//第二个参数为: MyPredicate<Product>,MyPredicate是接口接口中有test方法。boolean test(T t);
//而 (p) -> p.getPrice() < 8000 即是 对它的实现。

方法2:使用Stream API

甚至不用定义过滤方法直接在集合上进行操作

    @Test
    public void test(){

        //定义一个list
        List<Product> proList = new ArrayList<>();
        Product pp=new Product();
        pp.setPrice(500);
        pp.setColor("红色");
        pp.setName("张三");
        proList.add(pp);

        // 根据价格过滤
        proList.stream()
                .filter((p) -> p.getPrice() <8000)
                .limit(2)
                .forEach(System.out::println);

        // 根据颜色过滤
        proList.stream()
                .filter((p) -> "红色".equals(p.getColor()))
                .forEach(System.out::println);

        // 遍历输出商品名称
        proList.stream()
                .map(Product::getName)
                .forEach(System.out::println);
    }
com.example.demo.lambdademo.Product@2ef5e5e3
com.example.demo.lambdademo.Product@2ef5e5e3
张三

Lamabda表达式的语法总结: () -> ();

img

口诀:左右遇一省括号,左侧推断类型省

注:当一个接口中存在多个抽象方法时如果使用lambda表达式并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念

函数式接口

函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。

什么是函数式接口?

简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解@FunctionalInterface

常见的四大函数式接口

Consumer 《T》:消费型接口,有参无返回值

public class TestMain {
    @Test
    public void test(){
        changeStr("hello",(str) -> System.out.println(str));
    }

    /**
     *  Consumer<T> 消费型接口
     * @param str
     * @param con
     */
    public void changeStr(String str, Consumer<String> con){
        con.accept(str);
    }
}

Supplier 《T》:供给型接口,无参有返回值

@Test
public void test2(){
    String value = getValue(() -> "hello");
    System.out.println(value);
}

/**
 *  Supplier<T> 供给型接口
 * @param sup
 * @return
 */
public String getValue(Supplier<String> sup){
    return sup.get();
}

Function 《T,R》::函数式接口,有参有返回值

@Test
public void test3(){
    Long result = changeNum(100L, (x) -> x + 200L);
    System.out.println(result);
}

/**
 *  Function<T,R> 函数式接口
 * @param num
 * @param fun
 * @return
 */
public Long changeNum(Long num, Function<Long, Long> fun){
    return fun.apply(num);
}

Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型

public void test4(){
        boolean result = changeBoolean("hello", (str) -> str.length() > 5);
        System.out.println(result);
    }

    /**
     *  Predicate<T> 断言型接口
     * @param str
     * @param pre
     * @return
     */
    public boolean changeBoolean(String str, Predicate<String> pre){
        return pre.test(str);
    }

四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式不需要自己再手动创建一个函数式接口,直接拿来用就好了

方法引用

若lambda体中的内容有方法已经实现了,

那么可以使用“方法引用”也可以理解为

方法引用是lambda表达式的另外一种表现形式

并且其语法比lambda表达式更加简单

(a) 方法引用

三种表现形式:
对象 : : 实例方法名
类 : : 静态方法名
类 : : 实例方法名

  • (lambda参数列表中第一个参数是实例方法的调用者
  • 第二个参数是实例方法的参数时可用)
    @Test
    public void test() {
        /**
         *注意:
         *   1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
         *   2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
         *
         */
        Consumer<Integer> con = (x) -> System.out.println(x);
        con.accept(100); //打印100

        // 方法引用-对象::实例方法
        Consumer<Integer> con2 = System.out::println;
        con2.accept(200); //打印200

        // 方法引用-类名::静态方法名
        BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
        BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
        Integer result = biFun2.apply(100, 200);//不相同,返回-1

        // 方法引用-类名::实例方法名
        BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
        BiFunction<String, String, Boolean> fun2 = String::equals;
        Boolean result2 = fun2.apply("hello", "world");
        System.out.println(result2); //false
    }

(b)构造器引用

格式:ClassName::new

    @Test
    public void test2() {

        // 构造方法引用  类名::new
        Supplier<Employee> sup = () -> new Employee();
        System.out.println(sup.get());
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());

        // 构造方法引用 类名::new (带一个参数)
        Function<Integer, Employee> fun = (x) -> new Employee(x);
        Function<Integer, Employee> fun2 = Employee::new;
        System.out.println(fun2.apply(100));
    }

public class Employee {
    public Employee() {
    }

    public Employee(Integer x) {
        System.out.println(x);
    }
}

com.example.demo.methoddemo.Employee@6ea12c19
com.example.demo.methoddemo.Employee@7921b0a2
100
com.example.demo.methoddemo.Employee@6a2bcfcb

( c )数组引用

格式:Type[]::new

public void test(){
        // 数组引用
        Function<Integer, String[]> fun = (x) -> new String[x];
        Function<Integer, String[]> fun2 = String[]::new;
        String[] strArray = fun2.apply(10); //定义10个长度
        Arrays.stream(strArray).forEach(System.out::println);//遍历
}

null
null
null
null
null
null
null
null
null
null

Stream API

Stream操作的三个步骤

★ 创建stream
★ 中间操作(过滤、map)
★ 终止操作

stream的创建:

        // 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        // 2.通过Arrays的静态方法stream()获取数组流
        String[] str = new String[10];
        Stream<String> stream2 = Arrays.stream(str);

        // 3.通过Stream类中的静态方法of
        Stream<String> stream3 = Stream.of("aa","bb","cc");

        // 4.创建无限流
        // 迭代
        Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);

        //生成
        Stream<Double> generate = Stream.generate(() -> Math.random());

Stream的中间操作:

/**
   * 筛选 过滤  去重
   */
  emps.stream()
          .filter(e -> e.getAge() > 10)
          .limit(4)
          .skip(4)
          // 需要流中的元素重写hashCode和equals方法
          .distinct()
          .forEach(System.out::println);
//StuInfo [name=张三] 加了:limit(4) skip(4) 不会出现这些结果。
//StuInfo [name=张三2]


  /**
   *  生成新的流 通过map映射
   */
  emps.stream()
          .map((e) -> e.getAge())
          .forEach(System.out::println);


  /**
   *  自然排序  定制排序
   */
  emps.stream()
          .sorted((e1 ,e2) -> {
              if (e1.getAge().equals(e2.getAge())){
                  return e1.getName().compareTo(e2.getName());
              } else{
                  return e1.getAge().compareTo(e2.getAge()); //正序
              }
          })
          .forEach(System.out::println);

Stream的终止操作:

class Employee
{
    private String name;

    private Integer age;

    private Integer status;

    private Double salary;
    
}

        List<Employee> emps=new ArrayList();

        Employee e1=new Employee();
        e1.setAge(300);
        e1.setName("张三");
        e1.setSalary(1D);
        e1.setStatus(1);

        Employee e2=new Employee();
        e2.setAge(100);
        e2.setName("张三2");
        e2.setSalary(2D);
        e2.setStatus(2);

        emps.add(e1);
        emps.add(e2);

 /**
         *      查找和匹配
         *          allMatch-检查是否匹配所有元素
         *          anyMatch-检查是否至少匹配一个元素
         *          noneMatch-检查是否没有匹配所有元素
         *          findFirst-返回第一个元素
         *          findAny-返回当前流中的任意元素
         *          count-返回流中元素的总个数
         *          max-返回流中最大值
         *          min-返回流中最小值
         */

        /**
         *  检查是否匹配元素
         */
        boolean b1 = emps.stream()
                .allMatch((e) -> e.getStatus().equals(1));
        System.out.println(b1); //false 因为有一个 不匹配

        boolean b2 = emps.stream()
                .anyMatch((e) -> e.getStatus().equals(1));
        System.out.println(b2); //true ,因为有一个 匹配

        boolean b3 = emps.stream()
                .noneMatch((e) -> e.getStatus().equals(1));
        System.out.println(b3); //都不匹配吗? 为 false

        Optional<Employee> opt = emps.stream()
                .findFirst();
        System.out.println(opt.get()); // 得到第一个 为张三

        // 并行流
        Optional<Employee> opt2 = emps.parallelStream()
                .findAny();
        System.out.println(opt2.get()); //并行得到任意一个。为 张三2

        long count = emps.stream()
                .count();
        System.out.println(count); //数量为2

        Optional<Employee> max = emps.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get()); //求 最大的,为 张三2

        Optional<Employee> min = emps.stream()
                .min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(min.get()); //求 最小的,为 张三

还有功能比较强大的两个终止操作 reduce和collect

reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值


        /**
         *  reduce :规约操作
         */
        List<Integer> list2 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer count2 = list2.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(count2);

        Optional<Double> sum = emps.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(sum);


55
Optional[3.0]

collect操作: Collect-将流转换为其他形式接收一个Collection接口的实现,用于给Stream中元素做汇总的方法

        /**
         *  collect:收集操作
         */

        List<Integer> ageList = emps.stream()
                .map(Employee::getAge)
                .collect(Collectors.toList());
        ageList.stream().forEach(System.out::println);



300
100

并行流和串行流

jdk1.8新的stream包针对集合的操作也提供了并行操作流串行操作流

并行流就是把内容切割成多个数据块,并且使用多个线程分别处理每个数据块的内容。

Stream api中声明可以通过parallel()**与**sequential()**方法在**并行流串行流之间进行切换。

parallel 
英 /ˈpærəlel/  美 /ˈpærəlel/  全球(英国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. 平行线;对比
vt. 使…与…平行
adj. 平行的;类似的,相同的

sequential 
英 /sɪˈkwenʃl/  美 /sɪˈkwenʃl/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
adj. 连续的;相继的;有顺序的

支持对数组进行并行处理,主要是parallelSort()方法,它可以在多核机器上 极大提高数组排序的速度

jdk1.8并行流使用的是fork/join框架进行并行操作。

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

关键字:递归分合、分而治之。

采用 “工作窃取”模式(work-stealing):

  • 当执行新的任务时,它可以将其拆分分成更小的任务执行,

  • 并将小任务加到线程队列中,

  • 然后再从一个随机线程的队列偷一个并把它放在自己的队列中

  • 相对于一般的线程池实现,fork/join框架的优势 体现在对其中包含的任务的处理方式上

    • 在一般的线程池中,如果一个线程正在执行的任务,由于某些原因无法继续运行,那么该线程会处于等待状态
    • 而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,
    • 那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行
    • 这种方式减少了线程的等待时间提高了性能
/**
 * 要想使用Fork—Join,类必须继承
 * RecursiveAction(无返回值)
 * Or
 * RecursiveTask(有返回值)
 *
 */
public class ForkJoin extends RecursiveTask<Long> {

    /**
     * 要想使用Fork—Join,类必须继承RecursiveAction(无返回值) 或者
     * RecursiveTask(有返回值)
     *
     * @author Wuyouxin
     */
    private static final long serialVersionUID = 23423422L;//序列化

    private long start;//开始
    private long end;//结束

    public ForkJoin() {//无参数构造
    }

    public ForkJoin(long start, long end) {//全参数构造
        this.start = start;
        this.end = end;
    }

    // 定义 阙值
    private static final long THRESHOLD = 10000L;//1万

    @Override
    protected Long compute() {
        //结束 - 开始 <= 1万
        if (end - start <= THRESHOLD) {
            long sum = 0;//就累加上 开始 到 结束的 值。比如 100 到 2 ,那就是 2+3+4... 100
            for (long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            结束 - 开始 > 1万
            long middle = (end - start) / 2; //去中间的值。比如 20000 到 1
            //1,10000 创建一个 forkJoin
            ForkJoin left = new ForkJoin(start, middle);
            //拆分子任务,压入线程队列
            left.fork();
            //创建右边的。 10001 到 20000
            ForkJoin right = new ForkJoin(middle + 1, end);
            right.fork();

            //合并并返回
            return left.join() + right.join();
        }
    }
}


public class TestMain {
    /**
     * 实现数的累加
     */
    @Test
    public void test1() {
        //开始时间
        Instant start = Instant.now();

        //这里需要一个线程池的支持
        ForkJoinPool pool = new ForkJoinPool();

        ForkJoinTask<Long> task = new ForkJoin(0L, 11000L);//000000
        // 没有返回值     pool.execute();
        // 有返回值
        long sum = pool.invoke(task);

        System.out.println("结果:"+sum);

        //结束时间
        Instant end = Instant.now();
        //1 毫秒=1000000 纳秒
        System.out.println(Duration.between(start, end).getNano()/1000000);//求出毫秒。getSeconds()
    }

    /**
     * java8 并行流 parallel()
     */
    @Test
    public void test2() {
        //开始时间
        Instant start = Instant.now();

        // 并行流计算    累加求和
        long reduce = LongStream.rangeClosed(0, 10009L).parallel()
                .reduce(0, Long::sum);

        System.out.println(reduce);

        //结束时间
        Instant end = Instant.now();
        System.out.println(Duration.between(start, end).getSeconds());
    }

    @Test
    public void test3() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        //list.stream().forEach(System.out::print);

        list.parallelStream()
                .forEach(System.out::print);  //顺序不定
    }

}

展示多线程的效果:



    @Test
    public void test(){
        // 并行流 多个线程执行
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        numbers.parallelStream()
                .forEach(System.out::print); //无需遍历

        //
        System.out.println("=========================");
        numbers.stream()
                .sequential() //顺序遍历
                .forEach(System.out::print);
    }

Optional容器

使用Optional容器可以快速的定位NPE,并且在一定程度上可以减少对参数 非空检验 的代码量

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {

    private String name;

    private Integer age;

    private Double salary;

    private Integer status;
}
public class TestMain {

    @Test
    public void test20() {
        /**
         *      Optional.of(T t); // 创建一个Optional实例
         *
         *      Optional.empty(); // 创建一个空的Optional实例
         *      Optional.ofNullable(T t); // 若T不为null,创建一个Optional实例,否则创建一个空实例
         *
         *      isPresent();    // 判断是够包含值
         *      orElse(T t);   //如果调用对象包含值,返回该值,否则返回T
         *      orElseGet(Supplier s);  // 如果调用对象包含值,返回该值,否则返回s中获取的值
         *
         *      map(Function f): // 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty();
         *      flatMap(Function mapper);// 与map类似。返回值是Optional
         *
         *      总结:Optional.of(null)  会直接报NPE
         */


        //Optional.of(T t); // 创建一个Optional实例
        Optional<Employee> op = Optional.of(new Employee("zhansan", 11, 12.32, 100));
        System.out.println(op.get());

        // NPE
        Optional<Employee> op2 = Optional.of(null); //java.lang.NullPointerException
        System.out.println(op2);
    }

    @Test
    public void test2() {
        //Optional.empty(); // 创建一个空的Optional实例
        Optional<Object> op = Optional.empty();
        System.out.println(op);

        // No value present
        System.out.println(op.get());
    }

    @Test
    public void test3() {
        //Optional.ofNullable(T t); // 若T不为null,创建一个Optional实例,否则创建一个空实例
        Optional<Employee> op = Optional.ofNullable(new Employee("lisi", 33, 131.42, 100));
        System.out.println(op.get());

        Optional<Object> op2 = Optional.ofNullable(null);
        System.out.println(op2); //打印:Optional.empty
        // System.out.println(op2.get());
    }

    @Test
    public void test5() {
        Optional<Employee> op1 = Optional.ofNullable(new Employee("张三", 11, 11.33, 100));

        /*
         *      isPresent();    // 判断是够包含值
         *      orElse(T t);   //如果调用对象包含值,返回该值,否则返回T
         *      orElseGet(Supplier s);  // 如果调用对象包含值,返回该值,否则返回s中获取的值
         */
        System.out.println(op1.orElse(new Employee()));//op1.包含值,所以就打印:Employee(name=张三, age=11, salary=11.33, status=100)
        System.out.println(op1.orElse(null));
    }

    @Test
    public void test6() {
        Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, 100));
        op1 = Optional.empty();//赋值为empty。
        Employee employee = op1.orElseGet(() -> new Employee());//op1为空,所以返回后面new的对象 Employee(name=null, age=null, salary=null, status=null)
        System.out.println(employee);
    }

    @Test
    public void test7() {
        /*
         * map(Function f): // 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty();
         * flatMap(Function mapper);// 与map类似。返回值是Optional
         */
        Optional<Employee> op1 = Optional.of(new Employee("田七", 11, 12.31, 100));
        System.out.println(op1.map((e) -> e.getSalary()).get());//12.3
    }
}

接口:定义默认实现方法和静态方法

在接口中可以使用defaultstatic关键字来修饰接口中定义的普通方法

public interface Interface {
    default  String getName(){
        return "zhangsan";
    }

    static String getName2(){
        return "zhangsan";
    }
}

在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容

1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,

  • 引入了default默认实现static的用法是直接用接口名去调方法即可
  • 当一个类继承父类实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”;
  • 如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法
    一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:

以下实例转自:
https://blog.csdn.net/yitian_66/article/details/81010434#t17

public interface vehicle {
    default void print() {
        System.out.println("我是一辆车!");
    }
}

public interface fourWheeler {
    default void print() {
        System.out.println("我是一辆四轮车!");
    }
}

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法

public class Car implements vehicle, fourWheeler {

    @Override
    public void print() {
        System.out.println("我是一辆四轮汽车!");
    }
}

第二种解决方案可以使用 super 来调用指定接口的默认方法

public class Car implements vehicle, fourWheeler {
    @Override
    public void print() {
        vehicle.super.print();
    }
}

新的日期API:

  • LocalDate | LocalTime | LocalDateTime
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值