jdk1.8新特性知识点:
一、特殊的(函数式)接口
学习需知:
1.类的二个要素是什么?
属性和方法 属性三要素:(修饰符)类型,名字,值; 方法五要素:修饰符,返回值,方法名,形参(不是实际存在的变量),方法体;
2.接口:
接口是一个抽象的类 接口中的方法全部都是抽象的 接口可以有多个父接口 接口没有初始化块
接口类里的属性默认有修饰语publicstaticfinal 接口中的属性全部都是public static final
jdk1.8新特性: 在接口中的方法可以有方法体
3.什么是函数式接口?
简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface。
注意:该注解只能标在"有且仅有一个抽象方法"的接口上,如下
pacckge jdk.demo;
@FunctionalInterface
public interface TestInterface{
public void add();
}
3.特殊的接口和方法的调用
(1)特殊的接口——接口里有方法体
(2)可以使用::调用方法。
//构造方法引用使用方式:Class::new
look l=look.book(look::new);
//静态方法引用使用方式:Class::staticMethod
test.operate(1, 2, Test::add);
//对象的实例方法引用
test.operate(1, 2, test::sub);
//、类的实例方法引用;先满足实例方法,而非静态方法;Lambda表达式的第一个参数将成为调用实例的对象。
test.test(Test::testM);
二、什么是lambda表达式?
1.lambda表达式的特点是语法超级简洁,如下
//我们知道,对于一个Java变量,我们可以赋给其一个“值”。
int a=1;
String a="hello";
//如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?
//比如我要把如下代码赋给一个叫b的java变量
public void m(String a){
System.out.println(s);
}
//在java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了
b=public void m(String a){
System.out.println(s);
}
//但是这样并不是一个简洁的写法,我们为了简洁可以移除一些没有用的声明。如:
b=public void m(String a){
System.out.println(s);
}
//的最简洁样式为
b=(a)→System.out.println(s);
/*原因:public 是多余的;函数名字是多余的,因为已经赋给了b;编译器可以自己判断返回类型和参数类型是什么
如果只有一行输出语句可以不要大括号,在参数和函数之间加上一个箭头符号
*/
//这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。
2.匿名类:
(1)目标方法的参数得是一个接口; (2)方法的参数里面直接new后面跟上接口名 (3)必须马上写类体,重写方法体
3.lambda表达式的先决条件
(1)目标方法的参数是一个接口
(2)接口中只有一个抽象方法,这个抽象方法不是Object里面的方法
(3)接口最好顶一个注解:@FunctionalInterface
三、Stream
Stream概述
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
特点:
1 . 不是数据结构,不会保存数据。
2. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
3. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
1.Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
2.Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
3.Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
4.这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
stream of elements -----> filter-> sorted-> map-> collect
//以上流程的java代码是:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
5.和以前的Collection操作不同, Stream操作还有两个基础的特征:
①Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
②内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
6.怎么生成流?
在 Java 1.8 中, 集合接口有两个方法来生成流:
①stream() − 为集合创建串行流。
②parallelStream() − 为集合创建并行流。
代码实例:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
四、方法引用和构造器调用
方法引用是Java8 的新特性。
我们知道,使用Lambda表达式可以极大地简化我们的代码,但有时候Lambda体中的功能已经有现成的方法实现了,这时我们可以直接使用方法引用,而不是重复地去实现该功能。
方法引用可以理解为Lambda表达式的另一种表现形式。
使用前提:
方法引用引用的方法的参数列表和返回值类型,必须和函数式接口中抽象方法的参数列表和返回值类型保持一致。
方法引用的语法格式:
(1)对象::实例方法名
public static void main(String[] args) {
String str = "Hello world";
Consumer con1 = System.out::println;
con1.accept(str);
}
(2)类名::静态方法名
public static void main(String[] args) {
float a = 3.6f;
Function<Float, Integer> fun1 = Math::round;
System.out.println(fun.apply(a));
}
(3)类名::实例方法名
public class Person {
public void say() {
System.out.println("hello");
}
public static void main(String[] args) {
Consumer<Person> con1 = Person::say;
con1.accept(new Person());
}
}
构造器引用
使用前提
使用当构造器的参数列表与函数式接口中抽象方法的参数列表一致。构造器方法名必须和类名方法,并且没有返回值
构造器方法的创建:
构造器方法配合new 关键字可以创建对象
无参构造方法
public one() {}
无参构造方法的调用:
phone p=new phone();
有参构造器方法:
public phone(参数){}
有参构造器的调用:
phone p1=new phone(值);
五、接口中的默认方法和静态方法
接口默认方法的”类优先”原则 、
若一个接口中定义了一个默认方法,而另外一个父类或接口中 又定义了一个同名的方法时
第一种情况:
选择父类中的方法。如果一个父类提供了具体的实现,那么 接口中具有相同名称和参数的默认方法会被忽略。
示例代码:
1、新建一个接口MyInterface1.java,里面有个默认实现方法method1.
public interface MyInterface1 {
default void method1() {
System.out.println("MyInterface1中的默认方法");
}
}
2、再建一个类FatherClass.java,里面同样有一个同名的实现方法method1.
public class FatherClass {
public void method1() {
System.out.println("FatherClass中的方法method1");
}
}
3、创建一个子类SonClass.java,这个类继承FatherClass.java父类,并且实现MyInterface1接口。
import com.nieshenkuan.inter.MyInterface1;
public class SonClass extends FatherClass implements MyInterface1{
}
4、测试这个子类创建后,调用method1方法,会调用哪个类或者接口中的实现方法。
public void test01() {
SonClass sc=new SonClass();
sc.method1();
}
5、结果:调用类中的方法,并不是接口中的实现方法。
FatherClass中的方法method1
结论:接口默认方法的”类优先”原则 ,若一个接口中定义了一个默认方法,而另外一个父类或接口中 又定义了一个同名的方法时,先调用类中的同名方法
参考默认方法官方文档地址:
点击进入
第二种情况:
接口冲突。如果一个父接口提供一个默认方法,而另一个接 口也提供了一个具有相同名称和参数列表的方法(不管方法 是否是默认方法),那么必须覆盖该方法来解决冲突,(接口中是可以多实现的)
1、新建一个MyInterface2.java接口,同MyInterface2接口中方法同名。
public interface MyInterface2 {
default void method1() {
System.out.println("MyInterface2中的默认方法");
}
}
2、新建SonClass2类,实现了 MyInterface1,MyInterface2两个接口,这是会提醒你要实现哪个接口中的默认方法,如下:
import com.nieshenkuan.inter.MyInterface1;
import com.nieshenkuan.inter.MyInterface2;
public class SonClass2 implements MyInterface1,MyInterface2{
@Override
public void method1() {
// TODO Auto-generated method stub
MyInterface1.super.method1();
}
}
3、测试
@Test
public void test02() {
SonClass2 sc=new SonClass2();
sc.method1();
}
4、结果
MyInterface1中的默认方法
结论:
如果一个类实现多个接口。并且这些接 口提供了一个具有相同名称和参数列表的方法(不管方法 是否是默认方法),那么必须覆盖该方法来解决冲突。子类必须指定覆盖哪个父类接口中的方法。
静态方法
接口里可以声明静态方法,并且可以实现。
private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create(Supplier< Defaulable > supplier ) {
return supplier.get();
}
}
六、新时间日期
使用 LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用 ISO-8601 (ISO-8601 日历系统是国际化组织制定的现代化公民的日期和时间的表达法)日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前时间的时间信息。也不包含与时区相关的信息。
LocalDate LocalTime LocalDateTime 三个类的使用方式一样,只是代表的不同而已。
下面就以LocalDateTime作为例子进行示范:
1.获取当前时间
@test
public void test1() {
LocalDateTime now = LocalDateTime.now();
}
2.获取指定时间日期
@test
public void test2() {
LocalDateTime now = LocalDateTime.of(2018, 6, 10, 10, 10, 10);
}
3.对时间进行加减操作
@test
public void test3() {
LocalDateTime now = LocalDateTime.of(2018, 6, 10, 10, 10, 10);
//加时间
LocalDateTime time1 = now.plusYears(1).plusMonths(2).
plusDays(3).plusHours(5).plusMinutes(45).
plusSeconds(32);
//剪时间
LocalDateTime time2 = now.minusYears(1).minusMonths(2).
minusDays(3).minusHours(5).minusMinutes(45).
minusSeconds(32);
}
4. Duration 计算两个时间之间的间隔
@Test
public void test8() {
Instant now1 = Instant.now();
Instant now2 = Instant.now().
atOffset(ZoneOffset.ofHours(8)).toInstant();
Duration.between(now1, now2);
LocalDateTime now3 = LocalDateTime.now();
LocalDateTime now4 = LocalDateTime.of(2018, 6, 10, 10, 10, 10);
Duration.between(now3, now4);
}
后边就不再列举,需要用方法的可以转到此链接处
点击此处前往