Java8新特性
Lambda表达式
首先介绍匿名内部类:必须是类或者接口,其中只有一个抽象方法。 new 类名(接口名)(){ 重写抽象方法 } //第一种方式 new Animal(){ public void eat(){ System.out.println("吃肉"); } }.eat(); //整个是相当于是抽象类的子类对象,然后可以调用eat方法 //第二种方式 Animal A=new Animal(){ //相当于多态 父类引用指向子类对象 public void eat(){ System.out.println("吃肉"); } } a.eat(); Lambda表达式: 基础语法:箭头操作符或者叫Lambda操作符(->),将Lambda表达式拆分成两部分,左侧对应的是表达式的参数列表,右侧是表达式中所执行的功能(Lambda体)。Lambda左侧的参数列表就是对应接口中抽象方法的参数列表,右侧对应抽象方法的实现逻辑。 语法格式: 1、无参数、无返回值:()->System.out.println("你好");比如Runnable: Runnable r=()-> System.out.println("hello world"); 2、有一个参数,并且无返回值:举例:(若只有一个参数,那么参数的小括号可以省略不写) public interface Consumer<T> { void accept(T t); } Consumer<String> c=(x)-> System.out.println(x); //Consumer<String> c= x-> System.out.println(x); c.accept("hhahaha"); 3、有两个参数或者多个参数,并且Lambda体中有多条语句(Lambda体必须使用大括号),有返回值。 Comparator<Integer> c=(x, y)->{ System.out.println("函数式接口"); return Integer.compare(x,y); }; System.out.println(c.compare(5,3)); 4、如果有两个参数并且有返回值,Lambda体中只有一条返回值,return和大括号都可以省略不写。 Comparator<Integer> c=(x, y)->Integer.compare(x,y); System.out.println(c.compare(5,3)); 5、Lambda表达式的参数列表数据的数据类型可以省略不写。 Lambda表达式需要函数式接口的支持,接口中只有一个抽象方法的接口,可以使用一个注解使用@FunctionalInterface修饰一下,可以检查是不是函数式接口。
四大内置核心函数式接口
方法引用和构造器引用
方法引用:如果在Lambda中的内容有方法已经实现了,可以使用方法引用。 固定的格式: 对象::实例方法名;要求是抽象体中的方法中的参数列表和返回值类型和Lambda体中的参数列表和返回值类型是一样的。 PrintStream ps=System.out; Consumer<String> con=ps::println; Consumer<String> con=System.out::println; 类::静态方法名; Comparator接口中有一个抽象方法compare(), int compare(T o1, T o2);和Lambda表达式中的(实现类中)方法的参数列表和返回值类型是一致的: public final class Integer extends Number implements Comparable<Integer> { public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); } .... } --------------------------------------------- 举例: Comparator<Integer> com=(x,y)->Integer.compare(x,y); Comparator<Integer> com1=Integer::compare; 类::实例方法名 如果说在Lambda中第一个参数是方法的调用者,第二个参数是被调用方法的参数时,可以使用类:实例方法名。x.equals(y). 构造器引用:类名::new;==(new Person()),自动匹配符合的构造器。 数组引用:type[]::new; (String[]::new;)
Stream流
介绍:在util包中 Stream是处理集合的关键抽象概念,他可以指定你希望对集合处理的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用StreamAPI堆积和数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用StreamAPI类执行并行操作。 流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。集合讲的是数据、流讲的是计算。Stream自己不会存储元素。Stream不会改变源对象,相反他们会返回一个持有结果的新Stream。Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。 操作步骤: 创建Stream:有四种形式 1、可以通过collection系列集合提供的stream()或者parallelStream() List<String> list=new ArrayList<>(); Stream<String> stream = list.stream(); 2、通过Arrays中的静态方法获取数组流 String[] str=new String[10]; Stream<String> stream1 = Arrays.stream(str); 3、通过Stream类中的静态方法of(),也可以传递数组 Stream<String> stream3 = Stream.of("aa"); 4、创建无限流, 迭代:第一个参数是seed种子,也就是起始值,第二个参数是操作 Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2); 生成: Stream<Double> stream5 = Stream.generate(() -> Math.random()); 中间操作:没有任何效果,必须有终止操作 List<Person> list=Arrays.asList( new Person("a",10), new Person("b",20), new Person("c",30), new Person("d",40), new Person("e",50), new Person("f",60) ); 筛选和切片: filter()-》接收Lambda,从流中排除某些元素 list.stream() .filter((p)->p.getAge()>20) .forEach(System.out::println); } limit()-》截断流,使其元素不超过给定数量 list.stream() .filter((p)->p.getAge()>20) .limit(2) .forEach(System.out::println); } distinct()-》筛选,通过流所生成元素的hashCode()和equals()去除重复元素 skip()-》跳过元素,返回一个扔掉前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补。 list.stream() .filter((p)->p.getAge()>20) .skip(2) //扔掉前两个 .forEach(System.out::println); 映射: map()-》接收Lambda,将元素转换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list1=Arrays.asList("aaa","bbb"); list1.stream() .map((str)->str.toUpperCase()) .forEach(System.out::println); flatMap()-》接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流。 排序: sorted()-》自然排序(Comparable) List<Integer> list2=Arrays.asList(1,2,5,9,4,6,7); list2.stream() .sorted() .forEach(System.out::println); sorted(Comparator com)-》定制排序 peek() 终止操作(终端操作) 查找与匹配: min()-》返回流中的最小值 max()-》返回流中的最大值 count()-》返回流中元素的总个数 anyMatch()-》检查是否至少匹配一个元素 allMatch()-》检查是否匹配所有元素 noneMatch()-》检查时候没有匹配所有元素 findFirst()-》返回第一个元素 findAny()-》返回当前流中的任意一个元素 规约: reduce()-》将流中元素反复结合起来得到一个值,把起始值作为x,然后从流中取出一个值作为y,然后以此类推。 List<Integer> list2=Arrays.asList(1,2,5,9,4,6,7); list2.stream() .reduce(0,(x,y)->x+y); 收集: collect()-》将流转换为其他形式。接收一个Collector接口的实现,用于给stream中元素做汇总的方法 list.stream() .map(Person::getName) .collect(Collectors.toList()) .forEach(System.out::println); forEach()-》 forEachOrdered() toArray()
Optional类
//不能传递null 否则出现空指针异常,在构建的时候 Optional<Person> optional=Optional.of(new Person()); Person person = optional.get(); System.out.println(person);
接口
可以有默认方法(使用default关键字修饰,该方法可以实现),其中有一个是类优先原则,如果一个接口中定义了一个默认方法,另一个父类或接口中又定义了一个同名的方法时,选择父类中的方法,如果父类中提供了具体的实现,那么接口或接口中具有相同名称和参数的默认方法就会被忽略。接口冲突,如果一个父接口提供一个默认方法,另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认的),那么必须覆盖该方法来解决冲突。 静态方法:接口中可以有静态方法
时间和日期的API
整体都在Time包下:是不可变的线程安全的。 java。time包中的是类是不可变且线程安全的。新的时间及日期API位于java.time中,下面是一些关键类 ●Instant——它代表的是时间戳 //默认获取的是UTC(世界协调时间)时区为基础的 时间戳 Instant now = Instant.now(); System.out.println(now); //进行偏移量运算,得到的为北京现在的时间 OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); //转换成毫秒 long l = now.toEpochMilli(); System.out.println(l); ●LocalDate——不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。 //今天的日期是:2021-03-16,只包含日期 LocalDate localDate=LocalDate.now(); System.out.println("今天的日期是:"+localDate); //获取年月日 localDate.getYear(); localDate.getMonthValue(); localDate.getDayOfMonth(); ●LocalTime——它代表的是不含日期的时间 //获取当前时间,不包含日期 当前的时间是:16:40:34.591 LocalTime localTime=LocalTime.now(); System.out.println("当前的时间是:"+localTime); ●LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。 //指定当前时间 LocalDateTime localDateTime=LocalDateTime.of(2021,10,10,10,10,10); System.out.println(localDateTime); //在当前的基础上加减年月日等;LocalDate只能加减年月日,LocalTime只能加减时分秒 LocalDateTime localDateTime1 = localDateTime.plusYears(1); System.out.println(localDateTime1); LocalDateTime localDateTime2 = localDateTime.minusYears(1); System.out.println(localDateTime2); ●Duration:计算两个时间之间的间隔 ●Period:计算两个日期之间的间隔 时间校正器: TemporalAdjuster: dayOfWeekInMonth() – 一周中的某一天,例如,三月中第二个星期二 firstDayOfMonth() – 当前月的第一天 firstDayOfNextMonth() – 下一个月的第一天 firstDayOfNextYear() – 下一年的第一天 firstDayOfYear() – 当年的第一天 lastDayOfMonth() – 当月的最后一天 nextOrSame() – 下一次或当天发生的一周中的某天 //指定为月中的第一天 LocalDateTime localDateTime2 = localDateTime.with(TemporalAdjusters.firstDayOfMonth()); System.out.println(localDateTime2); 可以自定义指定,查文档 时间日期格式化:DateTimeFormatter DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_LOCAL_DATE; LocalDateTime localDateTime3=LocalDateTime.now(); String format = localDateTime3.format(isoDateTime); System.out.println(format); DateTimeFormatter.ofPattern("自定义格式"); //将字符串解析成时间格式 LocalDateTime parse = localDateTime3.parse("需要解析的字符串", "格式"); 时区的处理: ●ZonedTime ●ZonedDate ●ZonedDateTime——这是一个包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。
Java9新特性
模块化特性
简单说模块是代码和数据的封装体。这里的代码指的是一些包含类型的Packages 。Packages是类路径名称,模块就是一个或者多个Package组成的封装体。 实现目标: 主要目的是在于减小内存的开销 只须必要模块,而非全部jdk模块,可以简化各种类库和大型应用的开发和维护 改进JavaSE平台,使其可以适应不同大小的计算设备 改进其安全性,可维护性,提高性能
如图所示,如果在ModuleTest中直接调用Person是不能调用的,因为处在不同的模块中。可以通过导入导出模块的方式进行调用。创建module-info.java文件。 被调用的: module java9demo { //导出包 exports com.hqm.pojo; } 调用的: module javatest { //导入模块名 requires java9demo; }
jShell命令
JShell其实就是一个命令行工具,输入片段代码马上就可以看到结果,相当于脚本一行行解析执行,用户可以体验一把Java交互式编程环境。 JShell的目标是提供一个交互工具,通过它来运行和计算java中的表达式。开发者可以轻松地与JShell交互,其中包括:编辑历史,tab键代码补全,自动添加分号,可配置的imports和definitions。其他的很多主流编程语言如python都已经提供了console,便于编写一些简单的代码用于测试。值得一提的是,JShell并不是提供了新的一个交互语言,在JShell中编写的所有代码都必须符合java语言规范;图形界面和调试支持也没有,JShell的一个目标是可以在IDE中使用JShell交互,而不是实现IDE实现的功能。 使用jShell,可以在命令行中输入jshell,就可以进入jshell环境。
默认情况下导入的包
多版本兼容jar包
多版本兼容 JAR 功能能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。
接口的私有方法
jdk7只能定义抽象方法;jdk8可以定义默认方法和静态方法;jdk9可以定义私有方法 jdk9: public interface MyInterface { //定义默认方法 default void method1(){ System.out.println("接口中的私有方法"); method2(); } //定义私有方法 private void method2(){ System.out.println("接口中的私有方法2"); } }
钻石操作符(泛型)
在Java7之前每次声明泛型变量的时必须左右两边都同时声明泛型: List<String> list = new ArrayList<String>(); 在Java8中,对这一点进行了改进,就不必两边都要声明泛型,这种只适用<>标记的操作,称之为钻石操作符Diamond Operator: List<String> list = new ArrayList<>(); 类型推断 在Java9中,钻石操作符能与匿名实现类共同使用: List<String> list = new ArrayList<>() { @Override public int size() { return super.size(); } };
异常处理
JDK8异常处理对象必须在try的小括号内进行初始化。 JDK9新特性: -try前边可以定义流对象 -try后边的( )中可以直接引入流对象的名称(变量名),此时的对象是final的。 -在try代码执行完毕后会自动释放资源,不用写finally public static void main(String[] args) throws FileNotFoundException { FileInputStream inputStream = new FileInputStream("d:\\a.txt"); FileOutputStream outputStream = new FileOutputStream("d:\\d.txt"); try (inputStream; outputStream) { //引用多个的时候可以用分号分开 int len = 0; byte[] bytes = new byte[1024]; while ((len = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, len); } }catch (IOException e) { e.printStackTrace(); } }
下划线使用的限制
命名标识符的时候进行限制: 在java 8 中,标识符可以独立使用“_”来命名: String _="hello"; 这样是可以的 在java 9 中规定“_”不再可以单独命名标识符了,如果使用,会报错: String _="hello"; 这样是不行的
String底层存储结构的变化
之前存储string的结构是字符数组。jdk9中使用的存储结构是字节数组。如果是中文的话就会使用编码标识标注一下,UTF-16明确一个字符是两个字节。StringBuffer和StringBuild同样使用了字节数组。 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { @Stable private final byte[] value; ...... String:不可变的字符序列 StringBuffer:线程安全的,执行效率低,可变的字符序列 StringBuild:线程不安全的,执行效率高,可变的字符序列
创建只读集合
jdk8集合变为只读: List<String> s=new ArrayList<>(); List<String> s1=Collections.unmodifiableList(s); jdk9集合变为只读: List<Integer> list = List.of(1, 2, 3); //调用of方法 //map中是entry类型的数组,map集合除了可以使用of方法,还可以使用ofEntries方法。 Map.ofEntries(Map.entry(1,2), Map.entry(3,4));
增强的StreamAPI
//jdk9中Stream新添加的四个方法 takeWhile::从 Stream 中获取一部分数据, 返回从头开始的尽可能多的元素, //直到遇到第一个false结果,如果第一个值不满足断言条件,将返回一个空的 Stream List<Integer> list= Arrays.asList(1,3,5,2,4,6); list.stream() .takeWhile(x->x<4) .forEach(System.out::println); 结果为:1,3 dropWhile:与 takeWhile相反,返回剩余的元素,和takeWhile方法形成互补 List<Integer> list1= Arrays.asList(1,3,5,2,4,6); list1.stream() .dropWhile(x->x<4) .forEach(System.out::println); 结果为:5,2,4,6 ofNullable:此方法返回一个包含单个元素的顺序Stream。如果提供的元素为null,则此方法返回空Stream。 //如果提供多个元素其中有null是没问题的 Stream.of(1,2,3,null) .forEach(System.out::println); //jdk8中单个元素的时候不允许为null,jdk9中可以存放单个为null的元素 Stream.of(null) .forEach(System.out::println); iterate:jdk8中的用法 Stream.iterate(0,x->x+1) .limit(10) //进行限制输出 .forEach(System.out::println); jdk9中可以有方法终止迭代 Stream.iterate(0,x->x<10,x->x+1) .forEach(System.out::println);
Optional中增加Stream
List<String> list2 = new ArrayList<>(); list2.add("1"); list2.add("2"); list2.add("3"); list2.add("4"); Optional.ofNullable(list2) .stream() //新添加的stream方法 .forEach(System.out::println);
全新的HTTP客户端API
HttpClient是一个对多个请求配置了公共信息的容器。所有的请求通过一个HttpClient进行发送。HttpClients是不可变的,通过HttpClient的newBuilder()创建返回。请求Builders被HttpRequest#newBuilder()来创建。