赞
踩
一、引言
JDK1.8出来已经一段时间了,现在1.9也已经出来了,有部分公司不太愿意升级到高版本的JDK1.8,有些项目之前使用到是低版本JDK,如果升级维护老版本项目就比较麻烦,需要连带升级其他到框架。今天抽了一段时间把这些都总结一下
二、新特性
1、关键字 default 关键字
在JDK1.8之前,项目中我们定义的接口中一般都只有抽象的方法不能有方法体,其实则不然,接口里面可以定义静态的具体方法(使用static关键字定义方法),而JDK1.8之后可以通过关键字default定义具体方法,如下
public interface NewCharacter { /** *一般抽象方法没有实现体到方法 */ public void test1(); /** *通过static修饰有方法体到方法 */ public static void test2() { System.out.print("方法test2"); } /** *JDK1.8引入到新特性定义到方法 */ public default void test3(){ System.out.println("方法test3"); } }
那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?
一般情况在接口里面定义到抽象方法,都需要子类重写父类到抽象方法,在接口里面如果定义到抽象方法是一个算法或者别的方法,需要子类都来重写,并且实现到方式都一样,这样就造成代码到冗余,而且如果修改每个实现类都需要修改,重复的工作量(实际开发中可以通过工具类的定义全局的,每个方法调用即可),这样很麻烦
其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。
class NewCharacterImpl implements NewCharacter { public void test1() { System.out.println("test1"); } @Override public void test3() { System.out.println("重写父类default方法test3"); } public static void main(String[] args) { NewCharacter nca = new NewCharacterImpl(); nca.test1(); nca.test3(); } }
这样在接口里面定义方法目的是,当实现到方法中相同到代码,不需要再所有到子类中都去重复到写一遍,直接在接口中定义dufault 方法中编写具体代码子类就可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。
2.lambda
Lambda可以将方法或者代码片段作为数据进行参数传递。
lambda运算符 " -> ",运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体(可以是表达式也可以是代码块,实现函数式接口中的方法)
1.不接受参数,直接返回值1
()-> 1
2.接收两个int类型到参数,返回两个参数到和
(int a, int b) -> a + b
3.接收a, b两个参数,JVM会根据上下文推断参数到类型,返回两个数到和
(a, b) -> a + b
4. 接收一个字符串参数,打印该字符串,没有返回值
(String name) -> System.out.println(name)
5.接收一个参数,JVM根据上下文推断参数到类型,打印该字符串,没有返回值
(name) -> System.out.println(name)
6.接收两个字符串,分别打印结果,没有返回值
(name,address) -> {System.out.println(name);System.out.pirintln(address);}
7. 接收一个参数,返回它本身2倍
x -> 2*x
public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明 MathOperation addition = (int a, int b) -> a + b; // 不用类型声明 MathOperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 MathOperation multiplication = (int a, int b) -> { return a * b; }; // 没有大括号及返回语句 MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); } interface MathOperation { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google
使用 Lambda 表达式需要注意以下两点:
Lambda
表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
3 . 函数式接口
函数式接口(Functional Interface)特点:
1.接口内有且只有一个抽象方法
2.可以有多个默认方法(default修饰的方法),即可以有多个非抽象方法的接口。
3.java.lang.Object的public方法不算
4.函数式接口可以被隐式转换为 lambda 表达式。
JDK 1.8 之前已有的函数式接口
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
JDK 1.8 新增加的函数接口:
java.util.function
import java.util.Arrays; import java.util.List; public class LamdbaTest { // 计算余数等于1的值 public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 4, 2, 5, 7); // 定义算法 Eval eval = a -> a % 2 == 1; // 调用方法执行 out(list, eval); } public static void out(List<Integer> list, Eval eval) { for (Integer integer : list) { if(eval.bool(integer)) { System.out.println(integer); } } } interface Eval { boolean bool(Integer a); } }
执行以上脚本,输出结果为
$ javac LamdbaTest.java
$ java LamdbaTest
输出所有数据:
1
5
7
4. Java 8 Stream
Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
import java.util.Arrays; import java.util.Comparator; import java.util.IntSummaryStatistics; import java.util.List; import java.util.stream.Collectors; public class StreamList { public static void main(String[] args) { List<String> list = Arrays.asList("a", "", "c", "e", "", "f"); // forEach 遍历集合数据 list.forEach(str -> System.out.println(str)); // or list.forEach(str -> { System.out.println(str); }); // map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数 List<Integer> list1 = Arrays.asList(1, 2, 5, 3, 4); // 把集合中的元素处理后赋值给新的集合 List<Integer> collect = list1.stream().map(i -> i * i) .collect(Collectors.toList()); collect.forEach(str -> System.out.println(str)); // filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串: list = list.stream().filter(str -> !str.isEmpty()) .collect(Collectors.toList()); list.forEach(str -> System.out.println(str)); // limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据 list.stream().limit(2).forEach(str -> System.out.println(str)); // sorted 方法用于对流进行排序 // 升序 list1.stream().sorted().forEach(str -> System.out.println(str)); // 降序 list1.stream().sorted(Comparator.reverseOrder()) .forEach(str -> System.out.println(str)); List<Person> pList = Arrays.asList(new Person(1, 2), new Person(2, 1), new Person(1, 3)); // 对象属性使用Comparator 来排序一个list pList.stream().sorted(Comparator.comparing(Person::getAge)) .sorted(Comparator.comparing(Person::getNumber).reversed()) .forEach(str -> System.out.println(str)); // // 对象属性把上面的元素逆序 pList.stream().sorted(Comparator.comparing(Person::getAge).reversed()) .forEach(str -> System.out.println(str)); // 大小写转换 list.stream().map(String::toUpperCase) .forEach(str -> System.out.println(str)); List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x) .summaryStatistics(); System.out.println("列表中最大的数 : " + stats.getMax()); System.out.println("列表中最小的数 : " + stats.getMin()); System.out.println("所有数之和 : " + stats.getSum()); System.out.println("平均数 : " + stats.getAverage()); } } class Person { private Integer age; private Integer number; public Person(Integer age, Integer number) { super(); this.age = age; this.number = number; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } @Override public String toString() { return "Person [age=" + age + ", number=" + number + "]"; } }
5. Java 8 Date-Time
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
1. 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
2. 设计很差 −Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和
解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入
java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
3. 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,
但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API
Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
import java.time.LocalDate; import java.time.LocalTime; import java.time.LocalDateTime; import java.time.Month; public class Java8Tester { public static void main(String args[]){ Java8Tester java8tester = new Java8Tester(); java8tester.testLocalDateTime(); } public void testLocalDateTime(){ // 获取当前的日期时间 LocalDateTime currentTime = LocalDateTime.now(); System.out.println("当前时间: " + currentTime); LocalDate date1 = currentTime.toLocalDate(); System.out.println("date1: " + date1); Month month = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); int seconds = currentTime.getSecond(); System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds); LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012); System.out.println("date2: " + date2); // 12 december 2014 LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12); System.out.println("date3: " + date3); // 22 小时 15 分钟 LocalTime date4 = LocalTime.of(22, 15); System.out.println("date4: " + date4); // 解析字符串 LocalTime date5 = LocalTime.parse("20:15:30"); System.out.println("date5: " + date5); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
当前时间: 2016-04-15T16:55:48.668
date1: 2016-04-15
月: APRIL, 日: 15, 秒: 48
date2: 2012-04-10T16:55:48.668
date3: 2014-12-12
date4: 22:15
date5: 20:15:30
使用时区的日期时间API
import java.time.ZonedDateTime; import java.time.ZoneId; public class Java8Tester { public static void main(String args[]){ Java8Tester java8tester = new Java8Tester(); java8tester.testZonedDateTime(); } public void testZonedDateTime(){ // 获取当前时间日期 ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]"); System.out.println("date1: " + date1); ZoneId id = ZoneId.of("Europe/Paris"); System.out.println("ZoneId: " + id); ZoneId currentZone = ZoneId.systemDefault(); System.out.println("当期时区: " + currentZone); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
date1: 2015-12-03T10:15:30+08:00[Asia/Shanghai]
ZoneId: Europe/Paris
当期时区: Asia/Shanghai
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。