Java8新特性

古城微笑少年丶 2021-10-19 08:22 607阅读 0赞

1.接口

1)增加default方法和static方法,这两种方法都可以有方法体

  1. interface Interface1 {
  2. static void st1() {System.out.println("static Interface1.st1()");}
  3. default void df1() {System.out.println("default Interface1.df1()");}
  4. }

2)default方法属于实例(对象访问),static方法属于类(接口,只可以通过接口名访问)

  1. interface Interface1 {
  2. static void st1() {System.out.println("static Interface1.st1()");}
  3. default void df1() {System.out.println("default Interface1.df1()");}
  4. }
  5. public class Jdk8Test {
  6. public static void main(String[] args) {
  7. //static方法属于类,接口名访问
  8. Interface1.st1();
  9. //报错:default方法不可以通过接口名访问
  10. //Cannot make a static reference to the non-static method df1() from the type Interface1
  11. //Interface1.df1();
  12. Interface1 obj1 = new Interface1() {};
  13. //default方法属于实例,可以通过对象访问
  14. obj1.df1();
  15. //报错:static方法只可以通过接口名访问
  16. //This static method of interface Interface1 can only be accessed as Interface1.st1
  17. //obj1.st1();
  18. }
  19. }

3)接口里面的static方法不会被继承,static变量可以被继承,default方法可以被继承

  1. interface Interface1 {
  2. static void st1() {System.out.println("static Interface1.st1()");}
  3. static String st_str1 = "ststr1";
  4. default void df1() {System.out.println("default Interface1.df1()");}
  5. }
  6. interface Interface2 extends Interface1 {
  7. static void st2() {System.out.println("static Interface2.st2()");}
  8. default void df2() {System.out.println("default Interface2.df2()");}
  9. }
  10. public class Jdk8Test {
  11. public static void main(String[] args) {
  12. //报错,static方法不会被继承
  13. //The method st1() is undefined for the type Interface2
  14. //Interface2.st1();
  15. //static 变量则可以被继承,且可以通过接口名与变量名访问
  16. System.out.println(Interface2.st_str1);
  17. System.out.println(new Interface2() {}.st_str1);;
  18. //default方法可以被继承
  19. new Interface2() {}.df1();
  20. }
  21. }
  22. 4)一个类实现多个具有相同签名的default方法的接口,如果这些接口间没有继承关系,则报错,如果不想让它报错,可以重写这些同名方法
  23. 实例1
  24. interface Interface1 {
  25. default void df() {System.out.println("default Interface1.df1()");}
  26. }
  27. interface Interface2 {
  28. default void df() {System.out.println("default Interface2.df2()");}
  29. }
  30. //报错
  31. //Duplicate default methods named df with the parameters () and () are inherited from the types Interface2 and Interface1
  32. class Class1 implements Interface1,Interface2{}
  33. 实例2
  34. interface Interface1 {
  35. default void df() {System.out.println("default Interface1.df1()");}
  36. }
  37. interface Interface2 extends Interface1{
  38. default void df() {System.out.println("default Interface2.df2()");}
  39. }
  40. //上面加了继承,不再报错子接口的default方法覆盖父接口同名default方法
  41. class Class1 implements Interface1,Interface2{}
  42. 实例3
  43. interface Interface1 {
  44. default void df() {System.out.println("default Interface1.df1()");}
  45. }
  46. interface Interface2{
  47. default void df() {System.out.println("default Interface2.df2()");}
  48. }
  49. //重写接口同名方法,将不会再报错,如果需要访问其中的一个default方法,可以在子类总使用<接口名>.super.方法名的方法指定访问哪一个
  50. class Class1 implements Interface1,Interface2{
  51. public void df() {
  52. //指定访问Interface1的default方法
  53. Interface1.super.df();
  54. }
  55. public void df2() {
  56. //指定访问Interface2的default方法
  57. Interface2.super.df();
  58. }
  59. }
  60. 实例4:接口的继承亦是如此
  61. interface Interface1 {
  62. default void df() {System.out.println("default Interface1.df1()");}
  63. }
  64. interface Interface2{
  65. default void df() {System.out.println("default Interface2.df2()");}
  66. }
  67. //重写接口同名方法,将不会再报错,如果需要访问其中的一个default方法,可以在子类总使用<接口名>.super.方法名的方法指定访问哪一个
  68. interface Interface3 extends Interface1,Interface2{
  69. default void df() {
  70. //指定访问Interface1的default方法
  71. Interface1.super.df();
  72. }
  73. default void df2() {
  74. //指定访问Interface2的default方法
  75. Interface2.super.df();
  76. }
  77. }
  78. //不重写将会报错
  79. //Duplicate default methods named df with the parameters () and () are inherited from the types Interface2 and Interface1
  80. interface Interface4 extends Interface1,Interface2{}
  81. 总之:上面的解决方法就是指定访问哪一个重名了的default方法,不过指定有一定的语法要求:
  82. 接口名.supper.重复的方法名;

5)如果一个接口只有一个抽象方法(包括继承的),那么这个接口是一个函数式接口

  函数式接口可以使用Lambda表达式实现

  如果接口使用了@FunctionalInterface 注解,表明这个接口是一个函数式接口,接口内必须有且只可以有一个抽象方法。

6)抽象方法可以在接口中实现(接口继承时在子接口的默认方法中实现,不可以在静态方法中实现)。

2.Lambda表达式

  Lambda表达式只可以对函数式接口使用。

2.1 Lambda表达式三部分:

  () :表示参数列表,不需要指定参数类型,会自动推断
  -> :表示连接符
  {} :表示方法体

  如下代码:

  

  1. @FunctionalInterface
  2. interface UserTest{
  3. void test();
  4. }
  5. public class TestLambda {
  6. public static void main(String[] args) {
  7. //java8前匿名内部类实现
  8. UserTest ut = new UserTest() {
  9. @Override
  10. public void test() {
  11. System.out.println("匿名内部类实现");
  12. }
  13. };
  14. ut.test();
  15. //java8后使用Lambda表达式实现
  16. //() :表示参数列表,不需要指定参数类型,会自动推断
  17. //-> :表示连接符
  18. //{} :表示方法体
  19. UserTest ut2 = () -> {
  20. System.out.println("使用Lambda表达式实现");
  21. };
  22. ut2.test();
  23. }
  24. }

如果方法体只有一句话,{}可以简化掉,甚至有返回值的时候连return也可省略:

  1. @FunctionalInterface
  2. interface UserTest{
  3. void test();
  4. }
  5. @FunctionalInterface
  6. interface UserTest2{
  7. String test();
  8. }
  9. public class TestLambda {
  10. public static void main(String[] args) {
  11. UserTest ut1 = () -> System.out.println("使用Lambda表达式实现UserTest");
  12. ut1.test();
  13. UserTest2 ut2 = () -> "使用Lambda表达式实现UserTest2";
  14. System.out.println(ut2.test());
  15. }
  16. }

如果只有一个参数,()可以省去,两个及以上不可以省略():

  1. @FunctionalInterface
  2. interface UserTest3{
  3. int test(int x);
  4. }
  5. public class TestLambda {
  6. public static void main(String[] args) {
  7. UserTest3 ut3 = x -> ++x;
  8. System.out.println(ut3.test(1));
  9. }
  10. }
  11. 输出结果:
  12. 2

Lambda表达式访问的外部变量是final的。

Lambda的带来的优点:

可以把java代码作为参数传入,有助于提高代码内聚,Lambda实际上遵循匿名内部类的规则,但不是匿名内部类(Lambda表达式编译后不会生成class文件)

2.2 方法的引用

1)引用实例方法:

  方法引用时会自动把调用方法的时候的参数,全部全给引用的方法

  <函数式接口> <变量名> = <实例> ::<实例方法名>

  <变量名>.<函数式接口方法名>([实参]);

2)引用类方法:

  方法引用时会自动把调用方法的时候的参数,全部全给引用的方法

  <函数式接口> <变量名> = <类> ::<类方法名>

  <变量名>.<函数式接口方法名>([实参]);

简单示例代码:

  1. import java.util.Arrays;
  2. interface MethodRef{
  3. void test(String s);
  4. }
  5. interface MethodRef2{
  6. void test(int[] arr);
  7. }
  8. public class TestFunctionRef {
  9. public static void main(String[] args) {
  10. //Lambda表达式
  11. MethodRef m1 = s -> System.out.println(s);
  12. m1.test("字符串的");
  13. //使用方法引用 :引用实例方法
  14. //这里System.out是一个实例
  15. MethodRef m2 = System.out::println;
  16. m2.test("方法的引用");
  17. //引用类的方法
  18. //这里sort方法是Arrays工具类的静态方法。
  19. MethodRef2 m2_1 = Arrays::sort;
  20. int[] arr = new int[] {3,2,4,7,1,6,5};
  21. m2_1.test(arr);
  22. System.out.println(Arrays.toString(arr));
  23. }
  24. }

3)引用类的实例方法

  定义、调用接口方法的时候,需要多传入一个参数,并且参数的类型和引用示例方法的类型必须一致

  把第一个参数作为引用的实例,后面的每个参数全部传给引用的方法

  interface <函数式接口> {

    <返回值类型> <方法名> (<类1> <名称>[,其他参数 …]);

  }

  <函数式接口> <变量名> = <类1>::<实例方法名>; //注意两个类1要是一样的或者兼容的

  <变量名> . <方法名>(<类1的实例 [,其他参数]>);

  简单代码示例:

  1. import java.io.PrintStream;
  2. interface MethodRefX{
  3. void test(PrintStream ps,String s);
  4. }
  5. public class TestFunctionRef2 {
  6. public static void main(String[] args) {
  7. MethodRefX rx = PrintStream::println;
  8. rx.test(System.out, "第二参数");
  9. }
  10. }

上面代码含义为:用类型为PrintStream的实例调用方法名为println的方法,参数为test方法的第二个【及以后】的参数,System.out为PrintStream的一个实例。

4)构造器引用

  引用构造器,根据函数式接口的方法名来推断引用哪一个构造器

  1. interface FunctionRefY{
  2. String test(char[] chars);
  3. }
  4. public class TestFunctionRefy {
  5. public static void main(String[] args) {
  6. //引用String类型的public String(char value[])构造器
  7. FunctionRefY fy = String :: new;
  8. String ok = fy.test(new char[] {'o','k'});
  9. System.out.println(ok);
  10. }
  11. }

3.Stream

Stream API是Java 8中加入的一套新的API,主要用于处理集合操作。

Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素:是特定类型的对象,形成一个队列,Java中的Stream并不会存储元素,而是按需计算。
数据源:流的来源,可以是集合,数组,I/O channel,产生器generator 等。
聚合操作,类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,通过访问者模式(Visitor)实现。

Stream API:
1)创建流
stream() 为集合创建流。
parallelStream() 为集合创建并行流。
List list = new ArrayList<>();
Stream stream = list.stream();
Stream stream = list.parallelStream();

并行流:
对集合进行相同的操作,使用并行流一般会比普通遍历要快一些,快的程度与集合长度和每一次操作耗时均有关系。如下代码:
List ls = new ArrayList();
for(int i = 1;i <= 100;i ++)
ls.add(i);
Stream stream = ls.parallelStream();
long t1 = System.currentTimeMillis();
stream.forEach(a -> {
try{
Thread.sleep(100);
}catch(Exception e){}
});
long t2 = System.currentTimeMillis();
for(int i : ls){
try{
Thread.sleep(100);
}catch(Exception e){}
}
long t3 = System.currentTimeMillis();
System.out.println((t3-t2)/(double)(t2-t1));
最后输出结果约等于7.5

2)forEach
Stream 提供新的方法‘forEach’来迭代每一个数据
Stream stream = list.stream();
stream.forEach(System.out::println);

3)map
map方法用于映射每一个元素到对应的结果,并返回一个新的流。如下代码:
List ls = new ArrayList<>();
for(int i = 0;i < 5; i ++) ls.add(i);
Stream stream = ls.stream();
Stream stream2 = stream.map(n -> n*n);
List ls2 = stream2.collect(Collectors.asList());
print(ls);
print(ls2);

最后输出:
ls:0,1,2,3,4
ls2:0,1,4,9,16

4)filter
filter 发发发用于通过过滤条件过滤元素,返回一个新的流。如下代码:
List ls = Arrays.asList(“aa”,”bb”,””,”cc”);
Stream stream = ls.stream();
Stream stream2 = stream.filter(str -> !str.isEmpty());
List ls2 = stream2.collect(Collectors.asList());
print(ls);

最后输出结果:
ls:”aa”,”bb”,”cc”

5)limit
limit方法用于获取指定数量的流
List ls = new ArrayList<>();
for(int i = 0;i < 50; i ++) ls.add(i);
Stream stream = ls.stream();
Stream stream2 = stream.limit(5);
print(stream2)
Stream stream3 = stream.limit(5);
print(stream3)
最后输出结果:
stream2:0,1,2,3,4
Exception 。。。
第二次调用报错说明同一个流只可以调用limit方法一次

6)sorted
sorted对流进行排序,返回排序后的流
Random r = new Random();
r.ints().limit(10).sorted().forEach(System.out::println);

7)skip
跳过前面几个元素
List ls = new ArrayList<>();
for(int i = 0;i < 30; i ++) ls.add(i);
Stream stream = ls.stream();
Stream skip= stream.skip(5);
skip.forEach(System.out::println);
syso(“—————————“);
stream .forEach(System.out::println);
输出:
10 … 19
-——————-
illegalStateException:stream has already closed
由报错信息可以确定,skip返回的还是原来的流,只不过读取位置跳过了10个元素

8)toArray 转换为数组

9)reduce
规约操作,将流中元素进行合并,形成一个新的值,常见规约有求和运算,如下求取总价:
List bks = new ArrayList<>();
for(int i = 0;i < 10;i ++)bks.add(“bk-“+i,new Random().nextInt());
Optional opt = bks.stream().map(Book::getPrice).reduce((m,n) -> m+n);
syso(opt.get());
//opt.get()可以获取到规约后的总价

10)查询匹配
anyMatch:查询是否有符合指定匹配规则的,返回布尔值
allMatch:查询是否全部匹配指定匹配规则,返回布尔值
noneMatch:查询是否都不匹配指定规则,返回布尔值
boolean hasMatch = Stream.of(“Java”, “C#“, “PHP”, “C++”, “Python”).anyMatch(s -> s.equals(“Java”));
findFirst(),findAny()返回的都是第一个元素,建议使用findAny()
Optional element = Stream.of(“Java”, “C#“, “PHP”, “C++”, “Python”).findAny();
syso(element.get());//输出java

11)数据收集
数据收集是流式数据处理的终端处理,与中间处理不同,终端处理会消耗流,终端处理之后,流会关闭。
数据收集主要使用collect方法
该方法也属于归约操作,像reduce()方法那样可以接收各种做法作为参数,将流中的元素累积成一个汇总结果,具体的做法是通过定义新的Collector接口来定义的。

规约汇总:
取最值,计数等操作。
分组:
和关系型数据库类似,流也提供了类似数据库的group by的特性,由Collectors.groupingBy()方法提供
groupingBy()方法还支持多级分组,他有一个重载方法,除了接收一个Function类型的参数外,还接收一个Collector类型的参数

示例代码:

  1. package Test_C02;
  2. import java.util.ArrayList;
  3. import java.util.Comparator;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.stream.Collectors;
  7. import com.alibaba.fastjson.JSON;
  8. public class StreamGroup {
  9. static class Book{
  10. private String name;
  11. private Integer price;
  12. private Integer tag;
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public Integer getPrice() {
  20. return price;
  21. }
  22. public void setPrice(Integer price) {
  23. this.price = price;
  24. }
  25. public Integer getTag() {
  26. return tag;
  27. }
  28. public void setTag(Integer tag) {
  29. this.tag = tag;
  30. }
  31. public Book(String name,Integer price,Integer tag) {
  32. this.name = name;
  33. this.price = price;
  34. this.tag = tag;
  35. }
  36. }
  37. public static void main(String[] args) {
  38. List<Book> bks = new ArrayList<StreamGroup.Book>();
  39. for(int i = 0;i < 10;i++)
  40. bks.add(new Book("nm-"+i, i, i%3));
  41. //计数
  42. Long count = bks.stream().filter(bk -> bk.getPrice()>5).collect(Collectors.counting());
  43. System.out.println("价格大约5的数量:"+count);
  44. //最贵的书
  45. Book maxPriceBk = bks.stream().collect(Collectors.maxBy(Comparator.comparing(Book::getPrice))).get();
  46. System.out.println("最贵的书:"+JSON.toJSONString(maxPriceBk));
  47. //最便宜的书
  48. Book minPriceBk = bks.stream().collect(Collectors.minBy(Comparator.comparing(Book::getPrice))).get();
  49. System.out.println("最便宜的书:"+JSON.toJSONString(minPriceBk));
  50. //分组
  51. Map<Integer, List<Book>> group = bks.stream().collect(Collectors.groupingBy(Book::getTag));
  52. System.out.println("以tag进行分组:"+JSON.toJSONString(group));
  53. //多级分组
  54. Map<Integer, Map<String, List<Book>>> group2 = bks.stream().collect(Collectors.groupingBy(Book::getTag,Collectors.groupingBy(bk -> bk.getPrice() > 5 ? "A" : "B")));
  55. System.out.println("多级分组:"+JSON.toJSONString(group2));
  56. }
  57. }

转载于:https://www.cnblogs.com/ShouWangYiXin/p/11300669.html

发表评论

表情:
评论列表 (有 0 条评论,607人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java 8特性

    JAVA8 十大新特性 1:接口的默认方法 Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:

    相关 java8特性

    对于Java开发者来说,Java8的版本显然是一个具有里程碑意义的版本,蕴含了许多令人激动的新特性,如果能利用好这些新特性,能够大大提升我们的开发效率。Java8的函数式编程能

    相关 Java 8 特性

    Java 8 新特性 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java...