Java学习笔记----java高级编程续第二部分(Java8、9、11的新特性)(尚硅谷宋红康老师讲授)

Java8/9/11的新特性

9. Java8的其它新特性

9.1 Java 8新特性简介:
(1)Java8的说明:
  • Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发 工具与JVM带来了大量新特性。
(2)Java8新特性整体架构:

在这里插入图片描述

(3)Java的优化:
  • 速度更快;
  • 代码更少(增加了新的语法:Lambda 表达式) ;
  • 强大的 Stream API ;
  • 便于并行;
  • 最大化减少空指针异常:Optional ;
  • Nashorn引擎,允许在JVM上运行JS应用;
(4)并行流与串行流:
  • 并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
  • Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
9.2 Lambda表达式
(1)为什么使用 Lambda 表达式:
① Lambda表达式说明:
  • Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
  • 从匿名类到 Lambda 的转换举例1:
Runnable r1= new Runnable() {  //匿名实现类
           @Override
        public void run() {
               System.out.println("我爱北京天安门!");
           }
       };
   r1.run();

  System.out.println("****************************");
  //Lambda表达式
  Runnable r2 =() -> System.out.println("我爱中国!");
  r2.run();
  • 从匿名类到 Lambda 的转换举例2:
Comparator<Integer> com1 = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
           return Integer.compare(o1,o2);
        }
    };
  
    System.out.println(com1.compare(22, 33));
  
    System.out.println("******************************");
  
    //Lambda表达式:
    Comparator<Integer> com2 = (o1,o2)->Integer.compare(o1,o2);
  
    System.out.println(com2.compare(52, 33));
  
    //方法应用:
    Comparator<Integer> com3 =Integer::compare;
  
    System.out.println(com3.compare(33, 33));
② Lambda 表达式:语法:
  • 语法的说明

Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为->, 该操作符被称为 Lambda 操作符 或箭头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了Lambda 表达式需要的参数列表
  • 右侧:指定了Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。
  • i. 语法格式一:无参,无返回值
//语法格式一:无参,无返回值
Runnable r1 = new Runnable() {
      @Override
      public void run() {
          System.out.println("我爱北京天安门!");
      }
  };
  r1.run();
  System.out.println("****************************");

  //Lambda表达式
  Runnable r2 = () -> {
      System.out.println("我爱中国!");
  };
  r2.run();
  • ii. 语法格式二:Lambda 需要一个参数,但是没有返回值
Consumer<String> con = new Consumer<String>() {
@Override
   public void accept(String s) {
       System.out.println(s);
   }
};
con.accept("谎言和誓言的区别是什么呢?");

System.out.println("**********************");

//Lambda表达式:
Consumer<String> con1 =(String s)->{
   System.out.println(s);
};
con1.accept("一个是听得人当真了,一个是说的人当真了!");
  • iii. 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
//Lambda表达式:
Consumer<String> con2 =(s)->{
   System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了!");
  • iv. 语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
//Lambda表达式:
Consumer<String> con2 =s->{
  System.out.println(s);
};
con2.accept("一个是听得人当真了,一个是说的人当真了!");
  • v. 语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com1 = new Comparator<Integer>() {
     @Override
     public int compare(Integer o1, Integer o2) {
         return Integer.compare(o1,o2);
     }
 };
 com1.compare(11,12);

 //Lambda表达式
 Comparator<Integer> com2=(o1,o2)->{
     return Integer.compare(o1,o2);
 };
 com2.compare(33,33);
  • vi. 语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
 //Lambda表达式:
     Comparator<Integer> com3=(o1,o2)-> Integer.compare(o1,o2);
     System.out.println(com3.compare(33, 33));
   
     //
     Consumer<String> con2 =(s)->{
         System.out.println(s);
     };
     con2.accept("一个是听得人当真了,一个是说的人当真了!");
   
     System.out.println("****************************");
   
     Consumer<String> con3 =(s)->System.out.println(s);
     con3.accept("玉树临风美少年,揽镜自顾夜不眠!");
③ 类型推断:
  • 上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

在这里插入图片描述

9.3 函数式(Functional)接口
(1)什么是函数式(Functional)接口:
① 函数式接口说明:
  • 只包含一个抽象方法的接口,称为函数式接口
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
  • 在java.util.function包下定义了Java 8 的丰富的函数式接口。
② 如何理解函数式接口:
  • Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP) 编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不 得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还 可以支持OOF(面向函数编程)
  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的 编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在 Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的 对象类型——函数式接口
  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是 Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口 的实例,那么该对象就可以用Lambda表达式来表示。
  • 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
③ 函数式接口举例:

在这里插入图片描述

  • 自定义函数式接口:
/**
 * 自定义函数式接口:接口中只有一个抽象方法
 *
 * @author tangjie
 * @create 2023-05-24 14:49
 */
@FunctionalInterface
public interface MyInterface {
    void show();
    
}
  • 作为参数传递 Lambda 表达式:
    在这里插入图片描述
  • 作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
(2)Java 内置四大核心函数式接口:
① 如图所示:

在这里插入图片描述
在这里插入图片描述

② 代码演示:
public void happyTime(double money, Consumer<Double> cons){
          cons.accept(money); 
 }
          
happyTime(400, new Consumer<Double>() {
           @Override
           public void accept(Double aDouble) {
               System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:"+aDouble);
           }
       });
System.out.println("**************************");
//利用Lambda表达式:
happyTime(500,aDouble -> System.out.println("学习太累了,去天上人间买了雪碧,价格为:"+aDouble));
List<String> list = Arrays.asList("北京", "南京", "上海", "天津");
List<String> filterStr = filterString(list, new Predicate<String>() {
      @Override
      public boolean test(String s) {
          return s.contains("京");
      }
  });

  System.out.println(filterStr);

  System.out.println("******************************");
  //Lambda表达式:
  List<String> list1 = filterString(list, s -> s.contains("京"));
  System.out.println(list1);
  
  
  //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定;
public List<String> filterString(List<String> list, Predicate<String> pre){
      List<String> strList = new ArrayList<>();
      for (String str:list){
          if(pre.test(str)){
              strList.add(str);
          }
      }
      return strList;
  }
9.4 方法引用与构造器引用
(1)方法引用(Method References):
① 方法引用的说明:
  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
  • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就 是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向 一个方法,可以认为是Lambda表达式的一个语法糖。
  • 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
  • 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
  • 如下三种主要使用情况:
    • 对象::实例方法名
    • 类::静态方法名
    • 类::实例方法名
② 方法引用的举例:
  • 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
Consumer<String> con1 = new Consumer<String>() {
	@Override
	public void accept(String s) {
		System.out.println(s);
	}
};

//Lambda表达式:
Consumer<String> con2 =s-> System.out.println(s);
con2.accept("我爱我的祖国!--Lambda表达式");

System.out.println("***********************");
//方法引用:
PrintStream print = System.out;
Consumer<String> con3=print::println;
con3.accept("我爱我的祖国!--方法引用");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee employee = new Employee(1222,"Tom",33,9000);
//匿名实现类对象
Supplier<String> sup1 = new Supplier<String>() {
	@Override
	public String get() {
		return employee.getName();
	}
};

//Lambda表达式:
Supplier<String> sup2 =()->employee.getName();
String s = sup2.get();
System.out.println(s);

System.out.println("**********************");
//方法引用:
Supplier<String> sup3 =employee::getName;
String s1 = sup3.get();
System.out.println(s1);
}
  • 情况二:类 :: 静态方法
// 情况二:类 :: 静态方法
	//Comparator中的int compare(T t1,T t2)
	//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = new Comparator<Integer>() {
	@Override
	public int compare(Integer o1, Integer o2) {
		return Integer.compare(o1,o2);
	}
};

//Lambda表达式:
Comparator<Integer> com2 = (o1,o2)->Integer.compare(o1,o2);
System.out.println(com2.compare(22, 33));
System.out.println("*********************");

//方法引用:
Comparator<Integer> com3 = Integer::compare;
System.out.println(com3.compare(33, 33));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
	//Lambda表达式:
	Function<Double,Long> fun1 = d->Math.round(d);
	Long apply = fun1.apply(222.0);
	System.out.println(apply);

	//方法引用
	Function<Double,Long> fun2 = Math::round;
	Long a = fun2.apply(222.6);
	System.out.println(a);
}
  • 类 :: 实例方法
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
   Comparator<String> com = new Comparator<String>() {
     @Override
      public int compare(String o1, String o2) {
          return o1.compareTo(o2);
      }  
     }   
	//Lambda表达式:
	Comparator<String> com1 = (s1,s2)->s1.compareTo(s2);
	System.out.println(com1.compare("abc", "bdd"));

	System.out.println("***************************************");
	//方法引用:
	Comparator<String> com2 =String::compareTo;
	System.out.println(com2.compare("abc", "qwe"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
	//Lambda表达式:
	BiPredicate<String,String> bip1 =(s1,s2)->s1.equals(s2);
	System.out.println(bip1.test("abc", "abc"));
	System.out.println("****************************");
	//方法引用:
	BiPredicate<String,String> bip2 =String::equals;
	System.out.println(bip2.test("abd", "abc"));

}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
	//Lambda表达式:
	Employee employee = new Employee(111,"Tom",32,8000);
	Function<Employee,String> fun1 = e->e.getName();
	System.out.println(fun1.apply(employee));

	System.out.println("****************************");
	//方法引用
	Function<Employee,String> fun2 = Employee::getName;
	System.out.println(fun2.apply(employee));
}
(2)构造器引用:
① 构造器引用说明:
  • 格式 ClassName::new
  • 与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
② 代码演示:
//构造器引用
    //Supplier中的T get()
    @Test
    public void test1(){
        Supplier<Employee> sup1 = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        System.out.println("************************");
        Supplier<Employee> sup2 =()->new Employee();
        System.out.println(sup2.get());

        System.out.println("************************");
        Supplier<Employee> sup3 =Employee::new;
        System.out.println(sup3.get());
    }
//Function中的R apply(T t)
@Test
public void test2(){
    Function<Integer,Employee> fun1 =id->new Employee(id);
    Employee e1 = fun1.apply(188);
    System.out.println(e1);

    Function<Integer,Employee> fun2 =Employee::new;
    System.out.println(fun2.apply(222));
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
    BiFunction<Integer,String,Employee> bif1 =(id,name)->new Employee(id,name);
    System.out.println(bif1.apply(122, "tom"));

    System.out.println("**************************");
    BiFunction<Integer,String,Employee> bif2 =Employee::new;
    System.out.println(bif2.apply(4444, "Jerry"));
}
(3)数组引用:
① 数组引用说明:

格式: type[] :: new

② 代码演示:
//Function中的R apply(T t)
@Test
public void test4(){
	//Lambda表达式:
   Function<Integer,String[]> func1 = length->new String[length];
   String[] apply = func1.apply(10);
   System.out.println(Arrays.toString(apply));
   
   System.out.println("**********************");
 
   //数组引用:
   Function<Integer,String[]> func2 =String[]::new;
   String[] array = func2.apply(20);
   System.out.println(Arrays.toString(array));
}
9.5 强大的Stream API
(1)Stream API说明:
① Stream API具体介绍:
  • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则 是 Stream API
  • Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程 序员的生产力,让程序员写出高效率、干净、简洁的代码。
  • Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
② 为什么要使用Stream API:
  • 实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要 Java层面去处理。
  • Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中, 后者主要是面向 CPU,通过 CPU 实现计算。
③ 什么是 Stream:
  • Stream到底是什么呢?
    • 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,Stream讲的是计算!”
  • 注意:
    • Stream 自己不会存储元素。
    • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
(2)Stream 的操作三个步骤:
① 创建 Stream:
  • 一个数据源(如:集合、数组),获取一个流;
  • 创建 Stream方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流 的方法:

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流
@Test
public void test1() {

    List<Employee> employees = EmployeeData.getEmployees();

    // default Stream<E> stream() : 返回一个顺序流
    Stream<Employee> stream = employees.stream();

    //default Stream<E> parallelStream() : 返回一个并行流
    Stream<Employee> parallelStream = employees.parallelStream();
}
  • 创建Stream方式二:通过数组
  • Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
    • static Stream stream(T[] array): 返回一个流
  • 重载形式,能够处理对应基本类型的数组:
    • public static IntStream stream(int[] array)
    • public static LongStream stream(long[] array)
    • public static DoubleStream stream(double[] array)
//创建 Stream方式二:通过数组
@Test
public void test2() {
  //static <T> Stream<T> stream(T[] array): 返回一个流
   int[] arr = new int[]{1, 3, 4, 5, 5};
   IntStream stream = Arrays.stream(arr);

   Employee[] employees = new Employee[]{new Employee(222, "Tom"), new Employee(33)};
   Stream<Employee> stream1 = Arrays.stream(employees);
}
  • 创建Stream方式三:通过Stream的of()
  • 可以调用Stream类静态方法 of(), 通过显示值创建一个 流。它可以接收任意数量的参数。
    • public static Stream of(T... values) : 返回一个流
//创建 Stream方式三:通过Stream的of()
  @Test
  public void test3() {
      Stream<Integer> stream = Stream.of(1, 3, 4, 6, 4);
  }
  • 创建Stream方式四:创建无限流
  • 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
    • 迭代 public static Stream iterate(final T seed, final UnaryOperator f)
    • 生成 public static Stream generate(Supplier s)
@Test
public void test4() {
    //迭代
    //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        //遍历10个偶数:
    Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);

    //生成
    //public static<T> Stream<T> generate(Supplier<T> s)
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
② 中间操作:
  • 一个中间操作链,对数据源的数据进行处理;
  • 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止 操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

筛选与切片:
在这里插入图片描述

  • 代码演示:
//   filter(Predicate p)     接收 Lambda , 从流中排除某些元素
     List<Employee> list = EmployeeData.getEmployees();
     Stream<Employee> stream = list.stream();
     //练习:查询员工表中薪资大于7000的员工信息
     stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
//        limit(long maxSize)    截断流,使其元素不超过给定数量
     System.out.println("***************************");
     list.stream().limit(3).forEach(System.out::println);

//  skip(long n)  跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
     System.out.println("*****************************");
     list.stream().skip(3).forEach(System.out::println);
 
//  distinct()   筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    System.out.println("***************************");
    list.add(new Employee(1002, "马云", 12, 9876.12));
    list.add(new Employee(1002, "马云", 12, 9876.12));
    list.add(new Employee(1002, "马云", 12, 9876.12));
    list.add(new Employee(1002, "马云", 12, 9876.12));
    for (Employee e : list) {
        System.out.println(e);
    }
    System.out.println("******************");
    list.stream().distinct().forEach(System.out::println);

映 射:
在这里插入图片描述

  • 代码演示:
@Test
 public void test2(){
//        map(Function f)    接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
      List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
      list.stream().map(str->str.toUpperCase()).forEach(System.out::println);

      //练习1:获取员工姓名长度为3的员工的姓名
      List<Employee> lists = EmployeeData.getEmployees();
      Stream<String> nameStream = lists.stream().map(e -> e.getName());
      nameStream.filter(name->name.length()>3).forEach(System.out::println);

	 //练习2:
       //Lambda表达式:Stream<Stream<Character>> streamStream = list.stream().map(s ->fromStringToStream(s));
      //方法引用:
      Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
      streamStream.forEach(s->{
          s.forEach(System.out::println);
      });


      System.out.println("*********************************");
//       flatMap(Function f)   接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
      Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream);
      characterStream.forEach(System.out::println);

  }
  //此方法是将字符串每个字符所组成的集合转化为Stream
  public static Stream<Character> fromStringToStream(String str){
      List<Character> list = new ArrayList<>();
      char[] chars = str.toCharArray();
      for (Character c :chars){
          list.add(c);
      }
      return list.stream();
  }
  }

排序:
在这里插入图片描述

  • 代码演示:
@Test
public void test3(){
//    sorted() 产生一个新流,其中按自然顺序排序
     List<Integer> list = Arrays.asList(12, 33, 35, 32, 22, 11, 55, 33);
     list.stream().sorted().forEach(System.out::println);

     //抛异常,原因:Employee没有实现Comparable接口
//        List<Employee> list1 = EmployeeData.getEmployees();
//        list1.stream().sorted().forEach(System.out::println);

//    sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
     List<Employee> list1 = EmployeeData.getEmployees();
     list1.stream().sorted((e1,e2)->{
        int num=Integer.compare(e1.getAge(),e2.getAge());
        if (num!=0){
            return num;
        }
        return -Double.compare(e1.getSalary(),e2.getSalary());
     }).forEach(System.out::println);
 }
③终止操作(终端操作)
  • 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

在这里插入图片描述

  • 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例 如:List、Integer,甚至是 void 。
  • 流进行了终止操作后,不能再次使用。

匹配与查找:
在这里插入图片描述
在这里插入图片描述

  • 代码演示:
//1-1-匹配与查找
@Test
public void test1() {
    List<Employee> list = EmployeeData.getEmployees();
//        allMatch(Predicate p) 检查是否匹配所有元素
    //练习1:是否所有员工的年龄都大于18
    boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);
    System.out.println(allMatch);

//        anyMatch(Predicate p) 检查是否至少匹配一个元素
    //练习:是否存在员工的薪水大于10000
    System.out.println();
    boolean b = list.stream().anyMatch(e -> e.getSalary() > 10000);
    System.out.println(b);

//        noneMatch(Predicate p) 检查是否没有匹配所有元素
    //练习:是否存在员工的姓“雷”
    System.out.println();
    boolean b1 = list.stream().noneMatch(e -> e.getName().startsWith("雷"));
    System.out.println(b1);

//        findFirst() 返回第一个元素
    System.out.println();
    Optional<Employee> employee = list.stream().findFirst();
    System.out.println(employee);
//        findAny() 返回当前流中的任意元素
    Optional<Employee> employee1 = list.stream().findAny();
    System.out.println(employee1);
}
@Test
public void test2() {
     List<Employee> list = EmployeeData.getEmployees();
//        count() 返回流中元素总数
     long count = list.stream().filter(e -> e.getSalary() > 5000).count();
     System.out.println(count);
//        max(Comparator c) 返回流中最大值
       //练习:返回最高的工资
     Stream<Double> salaryStream = list.stream().map(e -> e.getSalary());
     Optional<Double> maxSalary = salaryStream.max((salary1, salary2) -> Double.compare(salary1, salary2));
     System.out.println(maxSalary);

//        min(Comparator c) 返回流中最小值
     //练习:返回最低工资的员工
     Optional<Employee> e = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
     System.out.println(e);
//        forEach(Consumer c)  内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
     System.out.println();
     list.stream().forEach(System.out::println);

     //使用集合的遍历操作
     System.out.println();
     list.forEach(System.out::println);

 }

归约:

在这里插入图片描述

  • 备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
  • 代码演示:
@Test
 public void test3() {
//        reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
        //练习1:计算1-10的自然数的和
      List<Integer> number = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
//        Optional<Integer> sum = number.stream().reduce(Integer::sum);
      Optional<Integer> sum = number.stream().reduce((i1,i2)->i1+i2);
      System.out.println(sum);


//        reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
      //练习2:计算公司所有员工工资的总和
      List<Employee> employees = EmployeeData.getEmployees();
      Stream<Double> salary = employees.stream().map(Employee::getSalary);
//        Optional<Double> sumSalary = salary.reduce(Double::sum);
      Optional<Double> sumSalary = salary.reduce((d1,d2)->d1+d2);
      System.out.println(sumSalary);
  }
  • 收集:
    在这里插入图片描述
  • Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、 Map)。
  • 另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例, 具体方法与实例如下表:

在这里插入图片描述

  • 代码演示:
@Test
public void test4() {
//    collect(java.util.stream.Collector c  将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法
     //练习1:查找工资大于6000的员工,结果返回为list或set集合
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);

System.out.println();
Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
     employeeSet.forEach(System.out::println);
 }
9.6 Optional类
(1)Optional类说明:
① Optional类使用背景:
  • Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
  • Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
②创建Optional类对象的方法:
  • Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):t可以为null
③ 判断Optional容器中是否包含对象:
  • boolean isPresent() : 判断是否包含对象;
  • void ifPresent(Consumer consumer) :如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它。
④ 获取Optional容器的对象:
  • T get(): 如果调用对象包含值,返回该值,否则抛异常;
  • T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
  • T orElseGet(Supplier other) :如果有值则将其返回,否则返回由 Supplier接口实现提供的对象。
  • T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返 回,否则抛出由Supplier接口实现提供的异常。
⑤ 代码演示:
@Test
public void test1() {
 //  Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
   Girl girl = new Girl("小芳",22);
 //空指针异常
   girl=null;
   Optional<Girl> girl1 = Optional.of(girl);
   System.out.println(girl1);
 }
@Test
public void test2() {
    //    Optional.ofNullable(T t):t可以为null
    Girl girl = new Girl("芳芳",33);
//        girl=null;
     Optional<Girl> girlOptional = Optional.ofNullable(girl);
     Girl girl1 = girlOptional.orElse(new Girl("茗茗", 22));
     System.out.println(girl1);
 }

//使用Optional类的getGirlName
public String getGirlName(Boy boy){
    Optional<Boy> boyOptional = Optional.ofNullable(boy);
    Boy boy1 = boyOptional.orElse(new Boy(new Girl("春华", 33)));
    Girl girl = boy1.getGirl();

    Optional<Girl> girlOptional = Optional.ofNullable(girl);
    Girl girl1 = girlOptional.orElse(new Girl("丽丽", 33));

    return girl1.getName();
}

@Test
public void test3(){
    Boy boy = null;
    boy=new Boy();
    boy= new Boy(new Girl("阿花",22));
    String girlName = getGirlName(boy);
    System.out.println(girlName);
}

10 Java9&Java10& Java11新特性

10.1 Java 9 的新特性
(1)JDK 9 的发布:
① JDK 9的说明:
  • 经过4次跳票,历经曲折的Java 9 终于终于在2017年9月21日发布。
  • 从Java 9 这个版本开始,Java 的计划发布周期是 6 个月,下一个 Java 的主版 本将于 2018 年 3 月发布,命名为 Java 18.3,紧接着再过六个月将发布 Java 18.9。
  • 这意味着Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的 (6 个月为周期)发布模式,并逐步的将 Oracle JDK 原商业特性进行开源。
  • 针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。
  • Java 9 提供了超过150项新功能特性,包括备受期待的模块化系统、可交互 的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安 全增强、扩展提升、性能管理改善等。可以说Java 9是一个庞大的系统工程, 完全做了一个整体改变。
② Java 9 中的新特性:

在这里插入图片描述

  • 官方提供的新特性列表:

https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUIDC23AFD78-C777-460B-8ACE-58BE5EA681F6

  • 或参考 Open JDK :

http://openjdk.java.net/projects/jdk9/

  • 在线Oracle JDK 9 Documentation :

https://docs.oracle.com/javase/9/

(2) JDK 和 JRE 目录结构的改变:
① jdk 8的目录结构:

在这里插入图片描述
在这里插入图片描述

②jdk 9目录结构:

在这里插入图片描述
在这里插入图片描述

(3)模块化系统: Jigsaw ——> Modularity
① 模块化系统说明:
  • 谈到 Java 9 大家往往第一个想到的就是 Jigsaw 项目。众所周知,Java 已经发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越来越暴露出一些问题:
    • Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第 一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程 序运行需要的class)。
    • 当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和 运行效率的提升。
    • 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间 的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
  • 本质上讲也就是说,用模块来管理各个package,通过声明某个package 暴露,模块(module)的概念,其实就是package外再裹一层,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。
  • 实现目标:
    • 模块化的主要目的在于减少内存的开销;
    • 只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开 发和维护;
    • 改进 Java SE 平台,使其可以适应不同大小的计算设备;
    • 改进其安全性,可维护性,提高性能;
  • 模块将由通常的类和新的模块声明文件(module-info.java)组成。该文件是位于 java代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系, 以及哪些模块被外部使用。在exports子句中未提及的所有包默认情况下将封装在模块中,不能在外部使用。

在这里插入图片描述

② 举例说明:
  • 要想在java9demo模块中调用java9test模块下包中的结构,需要在java9test 的module-info.java中声明:
/**
* @author songhongkang
* @create 2019 下午 11:57
*/
module java9test {
  //package we export
  exports com.atguigui.bean;
}
  • exports:控制着哪些包可以被其它模块访问到。所有不被导出的包默认都被封装在模块里面。
  • 对应在java 9demo 模块的src 下创建module-info.java文件:
/**
* @author songhongkang
* @create 2019 下午 11:51
*/
module java9demo {
	requires java9test;
}
  • requires:指明对其它模块的依赖。
(4)Java的REPL工具: jShell命令
① 产生背景:
  • Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码, 就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。
② 设计理念:
  • 即写即得、快速运行;
③ 实现目标:
  • Java 9 中终于拥有了 REPL工具:jShell。让Java可以像脚本语言一样运行,从控制台启动jShell,利用jShell在没有创建类的情况下直接声明变量,计算表达式, 执行语句。即开发时可以在命令行里直接运行Java的代码,而无需创建Java文 件,无需跟人解释”public static void main(String[] args)”这句废话。
  • jShell也可以从文件中加载语句或者将语句保存到文件中。
  • jShell也可以是tab键进行自动补全和自动添加分号。
④ jShell命令的操作步骤:
  • 调出jShell:
    在这里插入图片描述
  • 获取帮助:
    在这里插入图片描述
  • 基本使用:
    在这里插入图片描述
    在这里插入图片描述
  • 导入指定的包:
    在这里插入图片描述
  • Tips:在 JShell 环境下,语句末尾的“;” 是可 选的。但推荐还是最好加上。提高代码可读 性。
  • 只需按下 Tab 键,就能自动补全代码

在这里插入图片描述

  • 列出当前 session 里所有有效的代码片段:
    在这里插入图片描述
  • 查看当前 session 下所有创建过的变量:
    在这里插入图片描述
  • 查看当前 session 下所有创建过的方法:
    在这里插入图片描述
  • Tips:我们还可以重新定义相同方法名和参数列表的方法,即为对现有方法的修改(或 覆盖)。
  • 使用外部代码编辑器来编写 Java 代码:
    在这里插入图片描述
  • 使用/open命令调用:
    在这里插入图片描述
  • 没有受检异常(编译时异常)
    在这里插入图片描述
  • 说明:本来应该强迫我们捕获一个IOException,但却没有出现。因为jShell在 后台为我们隐藏了。
  • 退出jShell:
    在这里插入图片描述
(5)语法改进:接口的私有方法
① 接口私有方法的说明:
  • Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法 和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是 一个抽象类。
  • 在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分
② 代码演示:
interface MyInterface {
void normalInterfaceMethod();
default void methodDefault1() {
	init();
	}
public default void methodDefault2() {
	init();
}
// This method is not part of the public API exposed by MyInterface
private void init() {
		System.out.println("默认方法中的通用操作");
	}
}
class MyInterfaceImpl implements MyInterface {
@Override
public void normalInterfaceMethod() {
  	System.out.println("实现接口的方法");
  }
}
public class MyInterfaceTest {
  public static void main(String[] args) {
    MyInterfaceImpl impl = new MyInterfaceImpl();
    impl.methodDefault1();
    // impl.init();//不能调用
  }
}
(6)语法改进:钻石操作符使用升级
① 代码演示:
  • 我们将能够与匿名实现类共同使用钻石操作符(diamond operator)在Java 8 中如下的操作是会报错的
Comparator<Object> com = new Comparator<>(){
@Override
    public int compare(Object o1, Object o2) {
    return 0;
  }
};
  • 编译报错信息:Cannot use “<>” with anonymous inner classes.
  • Java 9中如下操作可以正常执行通过:
// anonymous classes can now use type inference
Comparator<Object> com = new Comparator<>(){
  @Override
  public int compare(Object o1, Object o2) {
  	return 0;
  }
};
(7)语法改进:try语句
① 代码演示:
  • Java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过。如下例所示:
try(InputStreamReader reader = new InputStreamReader(System.in)){
	//读取数据细节省略
}catch (IOException e){
	e.printStackTrace();
}
  • Java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try (reader; writer) {
  //reader是final的,不可再被赋值
  //reader = null;
  //具体读写操作省略
} catch (IOException e) {
	e.printStackTrace();
}
(8)String存储结构变更:
① 变更说明:
  • 结论:String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约 了一些空间。

在这里插入图片描述

(9)集合工厂方法:快速创建只读集合
① 只读集合说明:
  • 要创建一个只读、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。
List<String> namesList = new ArrayList <>();
namesList.add("Joe");
namesList.add("Bob");
namesList.add("Bill");
//返回的namesList为只读集合,不能添加和删除元素
namesList = Collections.unmodifiableList(namesList);
System.out.println(namesList);
  • 缺点:我们一下写了五行。即:它不能表达为单个表达式。
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", 
"b", "c")));
// 如下操作不适用于jdk 8 及之前版本,适用于jdk 9
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<>() {
  {
    put("a", 1);
    put("b", 2);
    put("c", 3);
  }
});
map.forEach((k, v) -> System.out.println(k + ":" + v));
  • Java 9因此引入了方便的方法,这使得类似的事情更容易表达:

在这里插入图片描述

  • List firsnamesList = List.of(“Joe”,”Bob”,”Bill”); 调用集合中静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能 可用于Set和List,也可用于Map的类似形式。此时得到的集合,是不可变的:在 创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException” 。 由于Java 8中接口方法的实现,可以直接在List,Set和Map的接口内定义这些方法, 便于调用。
List<String> list = List.of("a", "b", "c");

Set<String> set = Set.of("a", "b", "c");

Map<String, Integer> map1 = Map.of("Tom", 12, "Jerry", 21, "Lilei", 33, 
"HanMeimei", 18);

Map<String, Integer> map2 = Map.ofEntries(Map.entry("Tom", 89), 
Map.entry("Jim", 78), Map.entry("Tim", 98));
(10)InputStream 加强:
① 说明:
  • InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。
ClassLoader cl = this.getClass().getClassLoader();

try (InputStream is = cl.getResourceAsStream("hello.txt");
	OutputStream os = new FileOutputStream("src\\hello1.txt")) {
  
	is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
  
} catch (IOException e) {
	e.printStackTrace();
}
(11)增强的 Stream API:
① 说明:
  • Java 的 Steam API 是java标准库最好的改进之一,让开发者能够快速运算, 从而能够有效的利用数据并行计算。Java 8 提供的 Steam能够利用多核架构实现声明式的数据处理。
  • 在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4 个新的方法:takeWhile, dropWhile, ofNullable,还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
  • 除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。 现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个 (可能是空的) Stream 对象。

takeWhile()的使用:

  • 用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。在有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素。
List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88);
list.stream().takeWhile(x -> x < 50).forEach(System.out::println);

System.out.println();

list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
list.stream().takeWhile(x -> x < 5).forEach(System.out::println);

dropWhile()的使用:

  • dropWhile 的行为与 takeWhile 相反,返回剩余的元素
List<Integer> list = Arrays.asList(45, 43, 76, 87, 42, 77, 90, 73, 67, 88);
list.stream().dropWhile(x -> x < 50).forEach(System.out::println);

System.out.println();

list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
list.stream().dropWhile(x -> x < 5).forEach(System.out::println);

ofNullable()的使用:

  • Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream
//java 8 
// 报NullPointerException
// Stream<Object> stream1 = Stream.of(null);
// System.out.println(stream1.count());
// 不报异常,允许通过
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());// 3
// 不报异常,允许通过
List<String> list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count());// 2

//java 9 之后: ofNullable()
// ofNullable():允许值为null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());// 0
Stream<String> stream = Stream.ofNullable("hello world");
System.out.println(stream.count());// 1

iterate()重载的使用:

  • 这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什 么时候结束迭代
//java 8:
// 原来的控制终止方式:
Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println);

//java 9:
// 现在的终止方式:
Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
(12)Optional获取Stream的方法:
① Optional类中stream()的使用:
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
list.add("Tim");

Optional<List<String>> optional = Optional.ofNullable(list);
Stream<List<String>> stream = optional.stream();
stream.flatMap(x -> x.stream()).forEach(System.out::println);
(13)Javascript引擎升级:Nashorn
① 说明:
  • Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。 Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高 性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。
  • JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类,就能够分析 ECMAScript 代码。
10.2 Java 10 新特性
(1)Java 10的发布背景:
① 说明:
  • 2018年3月21日,Oracle官方宣布Java10正式发布。
  • 需要注意的是 Java 9 和 Java 10 都不是 LTS (Long-Term-Support) 版本。和 过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个 LTS 版本。
  • JDK10一共定义了109个新特性,其中包含12个JEP(对于程序员来讲,真 正的新特性其实就一个),还有一些新API和JVM规范以及JAVA语言规范上 的改动。
  • JDK10的12个JEP(JDK Enhancement Proposal特性加强提议)参阅官方 文档:http://openjdk.java.net/projects/jdk/10/
② JDK10的12个JEP:
Local-Variable Type Inference 局部变量类型推断 
Consolidate the JDK Forest into a Single Repository JDK库的合并 

Garbage-Collector Interface 统一的垃圾回收接口 

Parallel Full GC for G1 为G1提供并行的Full GC 

Application Class-Data Sharing 应用程序类数据(AppCDS)共享

Thread-Local Handshakes ThreadLocal握手交互 

Remove the Native-Header Generation Tool (javah) 移除JDK中附带的javah工具

Additional Unicode Language-Tag Extensions 使用附加的Unicode语言标记扩展 

Heap Allocation on Alternative Memory Devices 能将堆内存占用分配给用户指定 的备用内存设备 

Experimental Java-Based JIT Compiler 使用基于Java的JIT编译器 

Root Certificates 根证书 

Time-Based Release Versioning 基于时间的发布版本
(2)局部变量类型推断:
① 产生背景:
  • 开发者经常抱怨Java中引用代码的程度。局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。
② 好处:
  • 减少了啰嗦和形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读!
③ 举例如下:
  • 场景一:类实例化时
  • 作为 Java开发者,在声明一个变量时,我们总是习惯了敲打两次变量类型,第 一次用于声明变量类型,第二次用于构造器。
LinkedHashSet<Integer> set = new LinkedHashSet<>();
  • 场景二:返回值类型含复杂泛型结构
  • 变量的声明类型书写复杂且较长,尤其是加上泛型的使用
Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();
  • 场景三:
  • 我们也经常声明一种变量,它只会被使用一次,而且是用在下一行代码中, 比如:
URL url = new URL("http://www.atguigu.com");
URLConnection connection = url.openConnection();
Reader reader = new BufferedReader(new
InputStreamReader(connection.getInputStream()));
  • 尽管 IDE可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读 性还是会受到影响,因为变量类型的名称由各种不同长度的字符组成。而且, 有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意 力,不会带来额外的好处。
  • 适用于以下情况:
//1.局部变量的初始化
var list = new ArrayList<>();

//2.增强for循环中的索引
for(var v : list) {
	System.out.println(v);
}

//3.传统for循环中
for(var i = 0;i < 100;i++) {
	System.out.println(i);
}
  • 在局部变量中使用时,如下情况不适用:
    • 初始值为null
      在这里插入图片描述

    • Lambda表达式
      在这里插入图片描述

    • 方法引用
      在这里插入图片描述

    • 为数组静态初始化
      在这里插入图片描述

  • 不适用以下的结构中:
情况1:没有初始化的局部变量声明

情况2:方法的返回类型 

情况3:方法的参数类型 

情况4:构造器的参数类型 

情况5:属性 

情况6:catch块
④ 工作原理:
  • 在处理 var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断,作为左边变量的类型,然后将该类型写入字节码当中
⑤ 注 意:
  • var不是一个关键字:
  • 你不需要担心变量名或方法名会与 var发生冲突,因为 var实际上并不是一个关键字, 而是一个类型名,只有在编译器需要知道类型的地方才需要用到它。除此之外,它 就是一个普通合法的标识符。也就是说,除了不能用它作为类名,其他的都可以, 但极少人会用它作为类名。
  • 这不是JavaScript:
  • 首先我要说明的是,var并不会改变Java是一门静态类型语言的事实。编译器负责推 断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。 下面是使用 IntelliJ(实际上是 Fernflower的反编译器)反编译器反编译出的代码:
var url = new URL("http://www.atguigu.com");
var connection = url.openConnection();
var reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
  • 字节码文件
URL url = new URL("http://www.atguigu.com");
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
  • 从代码来看,就好像之前已经声明了这些类型一样。事实上,这一特性只发 生在编译阶段,与运行时无关,所以对运行时的性能不会产生任何影响。所 以请放心,这不是 JavaScript。
(3)集合新增创建不可变集合的方法:
① 说明:
  • 自 Java 9 开始,Jdk 里面为集合(List / Set / Map)都添加了 of (jdk9新增)和 copyOf (jdk10新增)方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。
//java10的新特性二:集合中新增的copyOf(),用于创建一个只读的集合
   @Test
   public void test5() {
       //示例1:
       var list1 = List.of("Java", "Python", "C");
       var copy1 = List.copyOf(list1);
       System.out.println(list1 == copy1); // true
 
       //示例2:
       var list2 = new ArrayList<String>();
       var copy2 = List.copyOf(list2);
       System.out.println(list2 == copy2); // false
 
       //示例1和2代码基本一致,为什么一个为true,一个为false?
       //结论:copyOf(Xxx coll):如果coll本身就是一个只读集合,则copyOf()返回值即为当前的coll;
               //如果参数coll本身不是一个只读的集合,则返回值即为一个新的只读集合;
   }
  • 注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。
10.3 Java 11 新特性
(1) Java 11说明:
① Java11的发展历史:
  • JDK 11 是一个长期支持版本(LTS, Long-Term-Support)
  • 对于企业来说,选择 11 将意味着长期的、可靠的、可预测的技术路线图。 其中免费的OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将 是可以放心选择的版本。
  • 从 JVM GC 的角度,JDK11 引入了两种新的 GC,其中包括也许是划时代意义 的 ZGC,虽然其目前还是实验特性,但是从能力上来看,这是 JDK 的一个巨大突破,为特定生产环境的苛刻需求提供了一个可能的选择。

在这里插入图片描述
在这里插入图片描述

(2)新增了一系列字符串处理方法:
① 知识图解:

在这里插入图片描述

② 代码演示:
//java11新特性一:String中新增的方法
  @Test
  public void test() {
//        判断字符串是否为空白 " ".isBlank(); // true
        System.out.println(" ".isBlank());

//        去除首尾空白 " Javastack ".strip(); // "Javastack"
        System.out.println("-----" + "\t \t \n abc".strip() + "-------");

//        去除尾部空格 " Javastack ".stripTrailing(); // " Javastack"
        System.out.println("-----" + "\t \t \n abc".stripTrailing() + "-------");

//        去除首部空格 " Javastack ".stripLeading(); // "Javastack "
        System.out.println("-----" + "\t \t \n abc".stripLeading() + "-------");

//        复制字符串 "Java".repeat(3);// "JavaJavaJava"
        System.out.println("java".repeat(8));

//        行数统计 "A\nB\nC".lines().count(); // 3
        System.out.println("java\nis\nstronger".lines().count());
 }
(3)Optional 加强:
① 说明:
  • Optional 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

在这里插入图片描述

② 代码演示:
//java11新特性二:Optional加强(新增的方法)
   @Test
   public void test2() {
       var op = Optional.empty();
       System.out.println(op.isPresent()); //判断内部的value是否存在
 
       System.out.println(op.isEmpty()); //判断内部的value是否为空
 
       op = Optional.of("abc");
       var obj = op.orElseThrow();
       System.out.println(obj);
 
       //or:value非空,返回对应的Optional; value为空,返回形参封装的Optional
//        op=Optional.empty();
        Optional<String> op1 = Optional.of("hello");
        Optional<Object> op2 = op.or(() -> op1);
        System.out.println(op2);
    }
(4) 局部变量类型推断升级:
① 说明:
  • 在var上添加注解的语法格式,在jdk10中是不能实现的。在JDK11中加入了这样 的语法。
② 代码演示:
//错误的形式: 必须要有类型, 可以加上var
//Consumer<String> con1 = (@Deprecated t) -> 
System.out.println(t.toUpperCase());
//正确的形式:
//使用var的好处是在使用lambda表达式时给参数加上注解。
Consumer<String> con2 = (@Deprecated var t) -> 
System.out.println(t.toUpperCase());
(5)全新的HTTP 客户端API:
① 说明
  • HTTP,用于传输网页的协议,早在1997年就被采用在目前的1.1版本中。直 到2015年,HTTP2才成为标准。
  • HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。HTTP/1.1依赖于请求/响应周期。 HTTP/2允许服务器“push”数据:它可以发送比客户端请求更多的数据。这使得它可以优先处理并发送对于首先加载 网页至关重要的数据。
  • 它 将 替 代 仅 适 用 于 blocking 模式的 HttpURLConnection (HttpURLConnection是在HTTP 1.0的时代创建的,并使用了协议无关的 方法),并提供对WebSocket 和 HTTP/2的支持。
② 代码演示:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = 
HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
HttpResponse<String> response = client.send(request, responseBodyHandler);
String body = response.body();
System.out.println(body);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = 
HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
CompletableFuture<HttpResponse<String>> sendAsync = 
client.sendAsync(request, responseBodyHandler);
sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);
//HttpResponse<String> response = sendAsync.get();
//String body = response.body();
//System.out.println(body);
(6)更简化的编译运行程序:
① 说明
  • 查看以下代码:
// 编译
javac Javastack.java
// 运行
java Javastack
  • 在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。 而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示: java Javastack.java
  • 一个命令编译运行源代码的注意点:
  • 执行源文件中的第一个类, 第一个类必须包含主方法
  • 并且不可以使用其它源文件中的自定义类, 本文件中的自定义类是可以使用的。
(7)废弃Nashorn引擎:
① 说明:
  • 废除Nashorn javascript引擎,在后续版本准备移除掉,有需要的 可以考虑使用GraalVM。
(8)ZGC:
① 说明:
  • GC是java主要优势之一。 然而, 当GC停顿太长, 就会开始影响应用的响应时 间。消除或者减少GC停顿时长, java将对更广泛的应用场景是一个更有吸引力 的平台。此外, 现代系统中可用内存不断增长,用户和程序员希望JVM能够以高 效的方式充分利用这些内存, 并且无需长时间的GC暂停时间。
  • ZGC, A Scalable Low-Latency Garbage Collector(Experimental) ZGC, 这应该是JDK11最为瞩目的特性, 没有之一。 但是后面带了Experimental, 说明这还不建议用到生产环境。
  • ZGC是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会 STW(stop the world), 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。
② 优势:
  • GC暂停时间不会超过10ms
  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
  • 和G1相比, 应用吞吐能力不会下降超过15%
  • 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
  • 初始只支持64位系统
  • ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制,以支持不少令人 兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存), 或压缩堆。
(9)其它新特性:
Unicode 10 
Deprecate the Pack200 Tools and API 
新的Epsilon垃圾收集器 
完全支持Linux容器(包括Docker) 
支持G1上的并行完全垃圾收集 
最新的HTTPS安全协议TLS 1.3 
Java Flight Recorder
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值