java1.8重要新特性

时间:2022-02-28 17:54:00

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 {

	@Override
	public void sayString(String str) {
		System.out.println("sayString--->" + str);
	}

	@Override
	public 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;
	}

	@Override
	public 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;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "名字:" + 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));// false
		System.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的工作原理相同。我们可以通过调用
方法来获取日期时间对象中特定的数据域。