jdk1.8新特性

本文详细介绍了Java 1.8的一些重要特性,包括函数式接口、lambda表达式、Stream API、方法引用和构造器调用以及新时间日期API的使用。函数式接口允许接口中存在一个方法体,lambda表达式简化了代码编写,Stream API提供了处理集合数据的新方式,方法引用和构造器调用进一步优化了代码效率,新时间日期API如LocalDate、LocalTime、LocalDateTime则提供了更方便的时间操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }

后边就不再列举,需要用方法的可以转到此链接处
点击此处前往

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值