Java8新特性纵览
本篇文章只讲解比较重要的
Lambda表达式 为什么使用Lambda表达式?
Lambda 是一个匿名函数 ,我们可以把 Lambda 表达式理解为是一段可以传递的代码 (将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
在Java8之后的很多源码里用到了Lambda表达式,不学的话可能看不懂源码。
简单使用 @Test public void test1 () { Runnable r1 = new Runnable() { @Override public void run () { System.out.println("我爱北京天安门" ); } }; r1.run(); System.out.println("***********************" ); Runnable r2 = () -> System.out.println("我爱北京故宫" ); r2.run(); } @Test public void test2 () { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare (Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; int compare1 = com1.compare(12 ,21 ); System.out.println(compare1); System.out.println("***********************" ); Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2); int compare2 = com2.compare(32 ,21 ); System.out.println(compare2); System.out.println("***********************" ); Comparator<Integer> com3 = Integer :: compare; int compare3 = com3.compare(32 ,21 ); System.out.println(compare3); }
Lambda语法规则 import org.junit.Test;import java.util.ArrayList;import java.util.Comparator;import java.util.function.Consumer;public class LambdaTest1 { @Test public void test1 () { Runnable r1 = new Runnable() { @Override public void run () { System.out.println("我爱北京天安门" ); } }; r1.run(); System.out.println("***********************" ); Runnable r2 = () -> { System.out.println("我爱北京故宫" ); }; r2.run(); } @Test public void test2 () { Consumer<String> con = new Consumer<String>() { @Override public void accept (String s) { System.out.println(s); } }; con.accept("谎言和誓言的区别是什么?" ); System.out.println("*******************" ); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了" ); } @Test public void test3 () { Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了" ); System.out.println("*******************" ); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了" ); } @Test public void test4 () { ArrayList<String> list = new ArrayList<>(); int [] arr = {1 , 2 , 3 }; } @Test public void test5 () { Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了" ); System.out.println("*******************" ); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了" ); } @Test public void test6 () { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare (Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12 , 21 )); System.out.println("*****************************" ); Comparator<Integer> com2 = (o1, o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12 , 6 )); } @Test public void test7 () { Comparator<Integer> com1 = (o1, o2) -> { return o1.compareTo(o2); }; System.out.println(com1.compare(12 , 6 )); System.out.println("*****************************" ); Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2); System.out.println(com2.compare(12 , 21 )); } @Test public void test8 () { Consumer<String> con1 = s -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了" ); System.out.println("*****************************" ); Consumer<String> con2 = s -> System.out.println(s); con2.accept("一个是听得人当真了,一个是说的人当真了" ); } }
函数式接口 什么是函数式(Functional)接口
只包含一个抽象方法的接口,称为函数式接口 。
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
如何理解函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
Java内置函数式接口 核心函数式接口
其它函数式接口
Consumer
@Test public void test1 () { happyTime(500 , new Consumer<Double>() { @Override public void accept (Double aDouble) { System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble); } }); System.out.println("********************" ); happyTime(400 ,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money)); } public void happyTime (double money, Consumer<Double> con) { con.accept(money); }
结果:
学习太累了,去天上人间买了瓶矿泉水,价格为:500.0 ******************** 学习太累了,去天上人间喝了口水,价格为:400.0 Process finished with exit code 0
Predicate
@Test public void test2 () { List<String> list = Arrays.asList("北京" ,"南京" ,"天津" ,"东京" ,"西京" ,"普京" ); List<String> filterStrs = filterString(list, new Predicate<String>() { @Override public boolean test (String s) { return s.contains("京" ); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list,s -> s.contains("京" )); System.out.println(filterStrs1); } public List<String> filterString (List<String> list, Predicate<String> pre) { ArrayList<String> filterList = new ArrayList<>(); for (String s : list){ if (pre.test(s)){ filterList.add(s); } } return filterList; }
结果:
[北京, 南京, 东京, 西京, 普京] [北京, 南京, 东京, 西京, 普京] Process finished with exit code 0
自定义函数式接口 @FunctionalInterface public interface MyFunInterface <T > { public T getValue (T t) ; }
public class Test { public static void main (String[] args) { String s = toUpperString(str -> str.toUpperCase(), "abcd" ); System.out.println(s); } public static String toUpperString (MyFunInterface<String> mf,String str) { return mf.getValue(str); } }
方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:
对象 :: 实例方法名
类 :: 静态方法名
类 :: 实例方法名
我们直接拿例子来说明情况,先提前准备两个类:
public class Employee { private int id; private String name; private int age; private double salary; public int getId () { return id; } public void setId (int id) { this .id = id; } 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 double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } public Employee () { System.out.println("Employee()....." ); } public Employee (int id) { this .id = id; System.out.println("Employee(int id)....." ); } public Employee (int id, String name) { this .id = id; this .name = name; } public Employee (int id, String name, int age, double salary) { this .id = id; this .name = name; this .age = age; this .salary = salary; } @Override public String toString () { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}' ; } @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Employee employee = (Employee) o; if (id != employee.id) return false ; if (age != employee.age) return false ; if (Double.compare(employee.salary, salary) != 0 ) return false ; return name != null ? name.equals(employee.name) : employee.name == null ; } @Override public int hashCode () { int result; long temp; result = id; result = 31 * result + (name != null ? name.hashCode() : 0 ); result = 31 * result + age; temp = Double.doubleToLongBits(salary); result = 31 * result + (int ) (temp ^ (temp >>> 32 )); return result; } }
public class EmployeeData { public static List<Employee> getEmployees () { List<Employee> list = new ArrayList<>(); list.add(new Employee(1001 , "马化腾" , 34 , 6000.38 )); list.add(new Employee(1002 , "马云" , 12 , 9876.12 )); list.add(new Employee(1003 , "刘强东" , 33 , 3000.82 )); list.add(new Employee(1004 , "雷军" , 26 , 7657.37 )); list.add(new Employee(1005 , "李彦宏" , 65 , 5555.32 )); list.add(new Employee(1006 , "比尔盖茨" , 42 , 9500.43 )); list.add(new Employee(1007 , "任正非" , 26 , 4333.32 )); list.add(new Employee(1008 , "扎克伯格" , 35 , 2500.32 )); return list; } }
下面来通过实际例子讲解方法引用:
public class MethodRefTest { @Test public void test1 () { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京" ); System.out.println("*******************" ); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing" ); } @Test public void test2 () { Employee emp = new Employee(1001 ,"Tom" ,23 ,5600 ); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************" ); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } @Test public void test3 () { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12 ,21 )); System.out.println("*******************" ); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12 ,3 )); } @Test public void test4 () { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply (Double d) { return Math.round(d); } }; System.out.println("*******************" ); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3 )); System.out.println("*******************" ); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6 )); } @Test public void test5 () { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc" ,"abd" )); System.out.println("*******************" ); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd" ,"abm" )); } @Test public void test6 () { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc" ,"abc" )); System.out.println("*******************" ); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc" ,"abd" )); } @Test public void test7 () { Employee employee = new Employee(1001 , "Jerry" , 23 , 6000 ); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************" ); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); } }
构造器引用 格式:ClassName :: new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
import org.junit.Test;import java.util.Arrays;import java.util.function.BiFunction;import java.util.function.Function;import java.util.function.Supplier;public class ConstructorRefTest { @Test public void test1 () { Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get () { return new Employee(); } }; System.out.println("*******************" ); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************" ); Supplier<Employee> sup2 = Employee :: new ; System.out.println(sup2.get()); } @Test public void test2 () { Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001 ); System.out.println(employee); System.out.println("*******************" ); Function<Integer,Employee> func2 = Employee :: new ; Employee employee1 = func2.apply(1002 ); System.out.println(employee1); } @Test public void test3 () { BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001 ,"Tom" )); System.out.println("*******************" ); BiFunction<Integer,String,Employee> func2 = Employee :: new ; System.out.println(func2.apply(1002 ,"Tom" )); } @Test public void test4 () { Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5 ); System.out.println(Arrays.toString(arr1)); System.out.println("*******************" ); Function<Integer,String[]> func2 = String[] :: new ; String[] arr2 = func2.apply(10 ); System.out.println(Arrays.toString(arr2)); } }
强大的Stream API Stream API说明
Java8中有两大最为重要的改变。第一个是 Lambda 表达式 ;另外一个则是 Stream API 。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式
为什么要使用Stream API
实际开发中,项目中多数数据源都来自于Mysql,Oracle等,很多一些复杂的数据获取可以直接在sql层面去解决。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据本身不支持一些复杂的数据计算,这个时候就需要Java层面去处理。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
什么是Stream Stream到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,Stream讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
Stream 的操作三个步骤
1、创建Stream
一个数据源(如:集合、数组),获取一个流
2、中间操作
一个中间操作链,对数据源的数据进行处理
3、终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,才产生结果【也就是所谓的延迟执行】。之后,不会再被使用
创建Stream public class StreamAPITest { @Test public void test1 () { List<Employee> employees = EmployeeData.getEmployees(); Stream<Employee> stream = employees.stream(); Stream<Employee> parallelStream = employees.parallelStream(); } @Test public void test2 () { int [] arr = new int []{1 ,2 ,3 ,4 ,5 ,6 }; IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001 ,"Tom" ); Employee e2 = new Employee(1002 ,"Jerry" ); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } @Test public void test3 () { Stream<Integer> stream = Stream.of(1 , 2 , 3 , 4 , 5 , 6 ); } @Test public void test4 () { Stream.iterate(0 , t -> t + 2 ).limit(10 ).forEach(System.out::println); Stream.generate(Math::random).limit(10 ).forEach(System.out::println); } }
中间操作 public class StreamAPITest1 { @Test public void test1 () { List<Employee> list = EmployeeData.getEmployees(); Stream<Employee> stream = list.stream(); stream.filter(e -> e.getSalary() > 7000 ).forEach(System.out::println); System.out.println(); list.stream().limit(3 ).forEach(System.out::println); System.out.println(); list.stream().skip(3 ).forEach(System.out::println); System.out.println(); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,41 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.stream().distinct().forEach(System.out::println); } @Test public void test2 () { List<String> list = Arrays.asList("aa" , "bb" , "cc" , "dd" ); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3 ).forEach(System.out::println); System.out.println(); Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println(); Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } public static Stream<Character> fromStringToStream (String str) { ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test4 () { List<Integer> list = Arrays.asList(12 , 43 , 65 , 34 , 87 , 0 , -98 , 7 ); list.stream().sorted().forEach(System.out::println); List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if (ageValue != 0 ){ return ageValue; }else { return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); } }
终止操作 public class StreamAPITest2 { @Test public void test1 () { List<Employee> employees = EmployeeData.getEmployees(); boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18 ); System.out.println(allMatch); boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000 ); System.out.println(anyMatch); boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷" )); System.out.println(noneMatch); Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee); Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2 () { List<Employee> employees = EmployeeData.getEmployees(); long count = employees.stream().filter(e -> e.getSalary() > 5000 ).count(); System.out.println(count); Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary); Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println(); employees.stream().forEach(System.out::println); employees.forEach(System.out::println); } @Test public void test3 () { List<Integer> list = Arrays.asList(1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ); Integer sum = list.stream().reduce(0 , Integer::sum); System.out.println(sum); List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } @Test public void test4 () { List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); } }
Optional类 什么是Optional?
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
常用API
举例 首先准备两个类
public class Boy { private Girl girl; @Override public String toString () { return "Boy{" + "girl=" + girl + '}' ; } public Girl getGirl () { return girl; } public void setGirl (Girl girl) { this .girl = girl; } public Boy () { } public Boy (Girl girl) { this .girl = girl; } }
public class Girl { private String name; @Override public String toString () { return "Girl{" + "name='" + name + '\'' + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Girl () { } public Girl (String name) { this .name = name; } }
这里只是简单的测试两个API
public class OptionalTest { @Test public void test1 () { Girl girl = new Girl(); Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2 () { Girl girl = new Girl(); Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖" )); System.out.println(girl1); } }
实际场景使用
可能出现空指针的例子:
public String getGirlName (Boy boy) { return boy.getGirl().getName(); } @Test public void test3 () { Boy boy = new Boy(); boy = null ; String girlName = getGirlName(boy); System.out.println(girlName); }
结果:
java.lang.NullPointerException at com.atguigu.java4.OptionalTest.getGirlName(OptionalTest.java:47) at com.atguigu.java4.OptionalTest.test3(OptionalTest.java:54) ... ... Process finished with exit code -1
没有Optional的解决办法,但是如果调用层数过多,就得一层一层判断是否为null,写起来很麻烦。
public String getGirlName1 (Boy boy) { if (boy != null ){ Girl girl = boy.getGirl(); if (girl != null ){ return girl.getName(); } } return null ; } @Test public void test4 () { Boy boy = new Boy(); boy = null ; String girlName = getGirlName1(boy); System.out.println(girlName); }
使用Optional解决问题:
public String getGirlName2 (Boy boy) { Optional<Boy> boyOptional = Optional.ofNullable(boy); Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴" ))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); Girl girl1 = girlOptional.orElse(new Girl("古力娜扎" )); return girl1.getName(); } @Test public void test5 () { Boy boy = null ; boy = new Boy(); boy = new Boy(new Girl("苍老师" )); String girlName = getGirlName2(boy); System.out.println(girlName); }
这种是绝对不会出现空指针的。
接口的增强 JDK7及以前:只能定义全局常量和抽象方法 >全局常量:public static final的.但是书写时,可以省略不写 >抽象方法:public abstract的 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
public interface CompareA { public static void method1 () { System.out.println("CompareA:北京" ); } public default void method2 () { System.out.println("CompareA:上海" ); } default void method3 () { System.out.println("CompareA:上海" ); } }
public class SuperClass { public void method3 () { System.out.println("SuperClass:北京" ); } }
public interface CompareB { default void method3 () { System.out.println("CompareB:上海" ); } }
public class SubClassTest { public static void main (String[] args) { SubClass s = new SubClass(); CompareA.method1(); s.method2(); s.method3(); } } class SubClass extends SuperClass implements CompareA ,CompareB { public void method2 () { System.out.println("SubClass:上海" ); } public void method3 () { System.out.println("SubClass:深圳" ); } public void myMethod () { method3(); super .method3(); CompareA.super .method3(); CompareB.super .method3(); } }
日期API【TODO】 注解【TODO】