Java8如何用
2014年发布的Java8,现在应该是业界使用最普遍的Java版本,可很多Java开发人员并没有充分发挥这4年前发布的版本的优点,
本文就是带着“学以致用”的目的去了解Java8,挖掘编码中可提高效率的特性。
什么是Java8?官网第一句话解释
Java Platform, Standard Edition 8 is a major feature release.
注:虽然Java现在半年迭代一次,但LTS版本依然保持3年一个版本的计划(比如从Java8到Java11)
首先是编程语法的增强
1) Lambda表达式:更多的场景是跟“函数式接口”结合。(注:函数式接口只有一个抽象方法)例:
public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明(MathOperation是在本Class中定义的内部接口) 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); } }
注意:lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
2) 方法引用:特点是一对引号引用方法,包括构造方法,静态方法,实例方法等的引用,可跟Lambda表示式结合使用,例:
import java.util.List; import java.util.ArrayList; public class Java8Tester { public static void main(String args[]){ List names = new ArrayList(); names.add("Google"); names.add("Runoob"); names.add("Taobao"); names.add("Baidu"); names.add("Sina"); names.forEach(System.out::println); } }
3)函数式接口
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法(无实现),但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。
Java8之前一直被匿名类实现,并不是说之前没有函数式接口,只是实现方法不同罢了,比如以下类就是函数式接口,就经常被匿名类实现
java.lang.Runnable // 用于线程实现
java.util.concurrent.Callable // 也用于线程,跟Runnable不同的是可以返回值,抛出异常
java.security.PrivilegedAction // 用于异步编程,一般用于AccessController.doPrivileged
java.util.Comparator // 类之间的比较,排序
java.io.FileFilter // 文件或者路径名匹配
java.nio.file.PathMatcher // NIO的文件路径匹配
java.lang.reflect.InvocationHandler // 反射中的动态代理接口(Spring的AOP就是通过动态代理实现的)
java.beans.PropertyChangeListener // 属性变更监听
java.awt.event.ActionListener // 动作监听
javax.swing.event.ChangeListener // 用户界面的变更监听
简单实例如下:
// Java8中添加此注解是为了编译级错误检查
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
使用方法(lambda来表示一个实现):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
Java8加上java.util.function包的很多函数式接口,比如典型Predicate:
Predicate <T> 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。
该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。
该接口用于测试对象是 true 或 false。
import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class Java8Tester { public static void main(String args[]){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); // Predicate<Integer> predicate = n -> true // n 是一个参数传递到 Predicate 接口的 test 方法 // n 如果存在则 test 方法返回 true System.out.println("输出所有数据:"); // 传递参数 n eval(list, n->true); System.out.println("输出所有偶数:"); eval(list, n-> n%2 == 0 ); System.out.println("输出大于 3 的所有数字:"); eval(list, n-> n > 3 ); } public static void eval(List<Integer> list, Predicate<Integer> predicate) { for(Integer n: list) { if(predicate.test(n)) { System.out.println(n + " "); } } } }
上面输出大于3的更简单写法(过滤集合数据常用):
list.stream().filter(n -> n > 3).forEach(System.out::println);
4)接口的默认实现方法,这个不需多解释
5)允许重复注解:主要是为了代码的可读性比如@Authorize(role="USER") @Authorize(role="ADMIN")
6)注解可用于任何地方,不只类,属性,方法
7)改进的类型引用。例:
// <>中类型可以被编译器识别
List<Integer> = new ArrayList<>()
8)Method parameter reflection(通过反射取得方法参数):例:http://tutorials.jenkov.com/java-reflection/methods.html
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; /** * @author liuwei * @date 2019/1/1 */ public class TestMethodFlection { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class aClass = TestMethodFlection.class; Method method = aClass.getMethod("test", int.class, int.class); // 包装类作为参数的话,也需要完全匹配 for (Parameter parameter : method.getParameters()) { System.out.println("参数类型:" + parameter.getType()); } System.out.println("返回类型" + method.getReturnType()); System.out.println("invoke方法结果为:" + method.invoke(null, 1, 2 )); // 静态方法的话,null作为第一个参数;否则,传具体object } public static int test(int x, int y){ return x + y; } }
4)Optional:对于保存NULL对象的容器(很好的解决空指针异常)
public Integer sum(Optional<Integer> a, Optional<Integer> b){ // Optional.isPresent - 判断值是否存在 System.out.println("第一个参数值存在: " + a.isPresent()); System.out.println("第二个参数值存在: " + b.isPresent()); // Optional.orElse - 如果值存在,返回它,否则返回默认值 Integer value1 = a.orElse(new Integer(0)); //Optional.get - 获取值,值需要存在 Integer value2 = b.get(); return value1 + value2; }
2. Collections
1)Stream(分串行模式和并行模式)
一个来自数据源的元素队列并支持聚合操作。典型用法:Pipelining,内部迭代。例:
// foreach Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
// map(取得元素加工后的Value)
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
// 过滤空字符串
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
int count = strings.stream().filter(string -> string.isEmpty()).count();
// limit
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
// 排序
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
// 转化为Map
Map<String, String> versionMap = versions.stream().collect(Collectors.toMap(PersonAppVersion::getAppName, PersonAppVersion::getAppVersion));
// 并行程序(可以充分利用多CPU来加快处理速度)
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
// 统计
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());
2)HashMap的性能提升:key冲突机制变更
精简版的profile,使得可以将一个大的java项目部分编译成较小版本
4. Security
1. 客户单默认使用TLS1.2协议(注:TLS和SSL的区别)
5. Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎
// Java中执行JS代码
public static void main(String args[]){ ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn"); String name = "Runoob"; Integer result = null; try { nashorn.eval("print('" + name + "')"); result = (Integer) nashorn.eval("10 + 2"); }catch(ScriptException e){ System.out.println("执行脚本错误: "+ e.getMessage()); } System.out.println(result.toString()); }
除此之外,jjs是个基于Nashorn引擎的命令行工具,可以直接执行js代码或者文件。另外,js中也可以执行Java代码,这个应用场景较少,就不再举例
6. 日期工具类
之前的日期工具类存在很多问题,比如线程安全,时区问题,让不熟悉的程序员掉入坑里。Java8中引入的最主要API如下:
-
Local(本地) − 简化了日期时间的处理,没有时区的问题。(包括Locatate,LocalDateTime等)
-
Zoned(时区) − 通过制定的时区处理日期时间。(包括ZonedDateTime等)
以上类都是线程安全的,java.time.Instant用于时间戳
Java8新特性参考:官网介绍
国内网站:http://www.runoob.com/java/java8-new-features.html
函数式接口:http://www.cnblogs.com/chenpi/p/5890144.html
重复注解:What can be real case use of Repeating annotations?
TLS和SSL:https://kb.cnblogs.com/page/197396/
动态代理机制:https://www.cnblogs.com/xiaoluo501395377/p/3383130.html