当前位置: 代码迷 >> 综合 >> java1.8重要新特性
  详细解决方案

java1.8重要新特性

热度:67   发布时间:2024-01-18 22:54:59.0

1.接口中可以有默认方法实现

interface A {public void sayString(String str);default public void SayInt(int i) {// 接口内默认实现,不需要子类实现,子类可以覆盖或子接口或子抽象类可以覆盖为抽象方法System.out.println("sayInt--->" + i);}
}abstract class B implements A {public abstract void SayInt(int i);
}class C extends B {@Overridepublic void sayString(String str) {System.out.println("sayString--->" + str);}@Overridepublic void SayInt(int i) {System.out.println("sayInt2--->" + i);}}public class Test {public static void main(String[] args) {C c = new C();c.SayInt(21);c.sayString("senssic");}
}

2.Lambada表达式

每一个lambda都能通过一个特定的接口,与一个给定的类型进行匹配,即所谓函数式接口必须要有且仅有一个抽象方法声明,每个与之对应的lambda表达式必须与抽象方法声明相匹配(即传入的参数类型,和返回类型相一致)。由于默认方法不是抽象的,所以在函数式接口里你可以添加任意的默认方法。
任意一个包含一个抽象方法的接口,都可以做成lambda表达式,为了让你定义的接口满足要求,应当在接口前面加上@FunctionalInterface,编译器便会注意到此标注,如果接口中定义了第二个抽象方法,编译器会报错。
// 标注此interface为函数式接口,最好写上
@FunctionalInterface
interface A {public String fLambada(String str);// 函数式接口有且仅有一个抽象方法default public void sayInt(int i) {// 函数式接口可以声明多个默认方法System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class B {public void invoke(A a) {System.out.println(a.fLambada("senssic"));}
}public class Test {public static void main(String[] args) {B b = new B();// 以下三中lambada均可,都是通过lambada来省略代码直接实现接口抽象方法b.invoke((String str) -> {System.out.println(str);// 使用大括号写实现代码return "hello " + str;});b.invoke((String str) -> "hello " + str);// 直接返回b.invoke(str -> "hello " + str);// 省略类型直接返回}
}

2.1 方法和构造方法的引用

使用静态方法实现接口抽象方法

@FunctionalInterface
interface A {public String fLambada(String str);default public void sayInt(int i) {System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class B {public void invoke(A a) {System.out.println(a.fLambada("senssic"));}
}public class Test {public static void main(String[] args) {B b = new B();// 通过静态方法来实现接口的抽象方法,类型必须匹配,// 比如此处,valueOf返回的必须为String类型,valueOf传入的参数必须为一个且为String类型A a = String::valueOf;b.invoke(a);}
}

使用普通方法实现抽象接口

@FunctionalInterface
interface A {public String fLambada(String str);default public void sayInt(int i) {System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class B {public void invoke(A a) {System.out.println(a.fLambada("senssic"));}
}class C {public String invSay(String str) {return "world" + str;}
}public class Test {public static void main(String[] args) {B b = new B();C c = new C();A a = c::invSay;//使用非静态方法实现抽象接口,此处C必须先实例化才能调用其invSay方法b.invoke(a);}
}

使用构造方法实现抽象接口

@FunctionalInterface
interface A {public Person fLambada(String name, int age);default public void sayInt(int i) {System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class Person {private String name;private int age;public Person(String name, int age) {this.setName(name);this.setAge(age);}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "姓名:" + name + "--->年龄:" + age;}
}public class Test {public static void main(String[] args) {A a = Person::new;// 此处使用构造方法来实现抽象接口,注意传入参数和返回类型要与接口一致此处构造方法无返回,但jvm会解析为对应的对象Person person = a.fLambada("senssic", 21);// 再也不用写那么多代码来实现工厂了System.out.println(person.toString());}
}

3.lambda的访问范围

对于lambda的访问权限与匿名对象的方式非常类似,能够访问局部对应外部区域的final变量,以及成员变量和静态变量
3.1 访问局部变量
@FunctionalInterface
interface A {public String fLambada(String str);default public void sayInt(int i) {System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class B {public void invoke(A a) {System.out.println(a.fLambada("senssic"));}public void partVab() {// 局部变量// 可以省略final 也正确,编译器会隐式的认为是final修饰的String name = "qiyu";// 或 final String name = "qiyu";this.invoke(str -> {// name = "德玛西亚";// 此处报错,name不能被修改因为隐式为final的return "hello " + str + "and " + name;});}
}public class Test {public static void main(String[] args) {B b = new B();b.partVab();}
}

3.2 访问成员变量和静态变量

@FunctionalInterface
interface A {public String fLambada(String str);default public void sayInt(int i) {System.out.println("sayInt--->" + i);}default public void sayString(String str) {System.out.println("sayInt--->" + str);}
}class B {private String cname = "初始化的成员变量";static private String sname = "初始化的静态变量";public void invoke(A a) {System.out.println(a.fLambada("senssic"));}public void partVab() {// 局部变量// 可以省略final 也正确,编译器会隐式的认为是final修饰的String name = "qiyu";// 或 final String name = "qiyu";// static String sr="";//方法内不能声明静态变量this.invoke(str -> {// name = "德玛西亚";// 此处报错,name不能被修改因为隐式为final的cname = "德玛西亚";// 可以访问有读写权限sname = "皮城女警";// 可以访问有读写权限return "hello " + str + "and " + name + "and " + cname + "and "+ sname;});}
}public class Test {public static void main(String[] args) {B b = new B();b.partVab();}
}

3.3 访问默认接口方法

不能被访问到,将出现编译错误

4.内置的函数式接口

JDK8中提供了很多的内置函数式接口,位于java.util.function包中

4.1Predicates

predicates是一个布尔类型的函数,该函数只有一个输入参数。predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and or negate)
import java.util.function.Predicate;public class Test {public static void main(String[] args) {Predicate<String> pre = str -> str.length() == 4;pre = pre.and(p -> p.contains("f"));System.out.println(pre.test("qiyu"));}
}

4.2 Suppliers

Suppliers接口产生一个给定类型的结果,需要类有无参构造函数
import java.util.function.Supplier;class Person {private String name;private int age;public Person() {// 必须有无参构造函数}public Person(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;}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "名字:" + name + "年龄:" + age;}
}public class Test {public static void main(String[] args) {Supplier<Person> pSupplier = Person::new;Person person = pSupplier.get();System.out.println(person.toString());}
}

4.3 Consumers 

Consumer代表了在一个输入参数上需要进行的操作。 
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); 
greeter.accept(new Person("Luke", "Skywalker")); 

4.4 Comparators 

Comparator接口在早期的Java 版本中非常著名。Java 8 为这个接口添加了不同的默认方法。
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); 
Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2);             // > 0 
comparator.reversed().compare(p1, p2);  // < 0 

4.5 Optionals 

Optional不是一个函数式接口,而是一个精巧的工具接口,用 NullPointerEception产生。这
个概念在下一节会显得很重要,所以我们在这里快速地浏览一下Optional的工作原理。 
Optional是一个简单的值容器,这个值可以是null,也可以是non-null。考虑到一个方法可能会返
回一个non-null的值,也可能返回一个空值。为了不直接返回 null,我们在 Java 8中就返回一个Optional. 
Optional<String> optional = Optional.of("bam");  
optional.isPresent();           // true 
optional.get();                 // "bam" 
optional.orElse("fallback");    // "bam" optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b" 

5. Streams

   java.util.Stream表示了某一种元素的序列,在这些元素上可以进行各种操作。Stream 操作可以是中
间操作,也可以是完结操作。完 结操作会返回一个某种类型的值,而中间操作会返回流对象本身,并且你
可以通过多次调用 StringBuffer 的append方法一样)。Stream是在一个源的基础上创建出来的,例如java.util.Collection中的list或者 set
(map不能作为Stream的源)。Stream 操作往往可以通过顺序或者并行两种方式来执行。 
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;public class Test {public static void main(String[] args) {List<String> sList = new ArrayList<String>();sList.add("a1");sList.add("b2");sList.add("ab");sList.add("acc");sList.add("bc");sList.add("cf6");sList.add("a7");sList.add("17");sList.add("236");sList.add("589");sList.add("5898955");// Filter 流过滤,中间操作返回流本身sList.stream().filter(str -> str.length() == 3).filter(str -> str.contains("c")).forEach(System.out::println);// acc// cf6// sorted 流排序,中间操作返回流本身sList.stream().filter(str -> str.contains("c")).sorted((str1, str2) -> {if (str1.length() == str2.length()) {return 0;} else if (str1.length() > str2.length()) {return 1;} else {return -1;}}).forEach(System.out::println);// bc acc cf6// map 流转换对象,中间操作返回流本身sList.stream().filter(str -> str.matches("\\d+")).map(str -> Integer.parseInt(str)).forEach(System.out::println);// 12// 236// 589// match 流匹配,终结操作System.out.println(sList.stream().allMatch(str -> str.length() == 3));// falseSystem.out.println(sList.stream().anyMatch(str -> str.length() > 5));// true// count 返回当前流数量,终结操作long l = sList.stream().filter(str -> str.length() > 3).count();System.out.println(l);// 1// reduce 减少数量合并流对象,终结操作Optional<String> reOptional = sList.stream().filter(str -> str.length() == 3).reduce((str, str2) -> str + "-->" + str2);reOptional.ifPresent(System.out::println);// acc-->cf6-->236-->589// parallelStream 并行排序,效率更快Optional<String> reOptiona = sList.parallelStream().filter(str -> str.length() == 3).reduce((str, str2) -> str + "-->" + str2);reOptiona.ifPresent(System.out::println);// acc-->cf6-->236-->589}}

6.Time API

Java 8 包含了全新的时间日期API,这些功能都放在了java.time包下
Clock 
Clock提供了对当前时间和日期的访问功能。Clock是对当前时区敏感的,并可用
System.currentTimeMillis() 方法来获取当前的毫秒时间。当前时间线上的时刻可以用 Instance类来表示。
Instance也能够用 java.util.Date对象。
Timezones 
时区类可以用 ZoneId 来表示。时区类的对象可以通过静态工厂方法方便地获取。时区类还定义
了一个偏移量,用 来在当前时刻或某时间与目标时间进行转换
LocalTime  
本地时间类表示一个没有指定时区的时间,例如,10 p.m.或者17:30:15,下面的例子会用上面例
子定义的时区创建两个本地时间对象。然后我们会比较两个时间,并计算它们之间的小时和分钟的不同。
LocalDate 
本地时间表示了一个独一无二的时间,例如:2014-03-11。这个时间是不可变的,与LocalTime 是
同源的。下面的例子演示了如何通过加减日,月,年等指标来计算新的日期。记住,每一次操作都会返回
一个新的时间对象。 
LocalDateTime 
LocalDateTime表示的是日期-时间。它将刚才介绍的日期对象和时间对象结合起来,形成了一个对
象实例。LocalDateTime是不可变的,与LocalTime和LocalDate的工作原理相同。我们可以通过调用
方法来获取日期时间对象中特定的数据域。