使用java8有一段时间了, 简单记录一下
Lambda
Lambda类似于匿名内部类,一个简单的小栗子:
new Thread(() -> {
System.out.println("hello, Lambda");
}).start();
可以看到使用Lambda更简洁
lambda表达式和内部类有点类似,可以访问类属性,final方法局部变量.
注意 :更改lambda表达式中的变量不是线程安全的.
对于只包含一个抽象方法的接口, 可以通过lambda表达式创建该接口对象.这种接口称为函数式接口.
(接口可以重新声明Object类的方法,如ToString, clone, equals, 这些不是抽象方法, java8的default方法也不是) :
Comparator<Integer> c = (final Integer t1, final Integer t2) -> {return Integer.compare(t1, t2);};
上面栗子中, final是为了演示添加的,可以省略, 参数类型Integer java可以推导出来.
方法体中, 因为只要一句代码,所以return和{}可以省略
Comparator<Integer> c = (t1, t2) -> Integer.compare(t1, t2);
甚至是
Comparator<Integer> c = Integer::compare;
上面使用了方法引用, 使用::
操作符引用一个方法,有三种方式
- 类::静态方法, 调用参数作为静态方法的参数
Integer::parseInt
等价于s -> Integer.parseInt(s)
, 对象对象s作为parseInt参数 - 类::实例方法, 第一个参数就是执行方法的对象
Object::toString
等价于o -> o.toString()
, 直接调用调用对象o的toString方法 - 构造方法
java8提供了许多常用的函数式接口
这些函数式接口可以作为类属性,方法参数,甚至返回值
如, 提供一个解析xml的接口, 由用户判断一个节点是否要处理, 如果要处理, 就交由用户进行处理. 可以使用如下方法定义:
public void handle(Predicate<Node> p, Consumer<Node> c)
而不用只定义NodeCheck, NodeHanler等接口
Optional
使用Optional可以有效的减小null的判断,特别是map的取值,如从一个Map<String, Object> map
获取"amount"的值并转化为int(假定map或map.get("amount")都可能为null),
以往的写法:
Integer i = null;
Object o = map.get("amount");
if(o != null) {
String s = o.toString();
if(s != null) {
i = Integer.parseInt(s);
}
}
而使用Optional则非常简洁
Integer i = optional.map(k -> k.get("amount")).map(o -> o.toString()).map(s -> Integer.parseInt(s)).orElse(null);
个人觉得:虽然Optional使用方便,但始终觉得方法定义Optional<User> getUser(String code)
没有User getUser(String code)
直观
stream
对于stream, 先来看个小栗子, 对一个List<Integer> list
, 过滤掉其中的奇数:
list = list.stream().filter(i -> i % 2 == 0).collect(Collectors.toList());
上面栗子中,可以分为3步:
-
list.stream()
创建一个stream -
filter(Predicate<? super T> predicate)
进行过滤操作, 仅保留predicate返回true的元素 -
collect(Collector<? super T, A, R> collector)
进行结果收集
除了filter, java8还提供了常用的元素处理方案:
- map 对stream中元素的类型转换
- limit 取前n个元素
- skip 丢弃前n个元素
- distinct 丢弃重复的方法
- sort 排序
收集结果
- toArray 到数组
- collect(Collectors.toList()) 到List
- collect(Collectors.toMap) 到Map
还有其他的聚合方案
- count
- max
- min
- findFirst
- findAny
- allMatch
- noneMatch
还可以使用stream分组
对于上个栗子中的list, 现在按奇偶数分成两组:
Map<Boolean, List<Integer>> oddMap = list.stream().collect(Collectors.partitioningBy(i -> i % 2 == 0));
根据属性分组
Map<String, List<User>> cityGroup = list.stream().collect(Collectors.groupingBy(User::getCity));
这是个很有用的特性, 因为我们常常要进行分组操作, 如对于一组user, 可以按地区, 用户等级等属性进行分组.
注意
可以很方便将list转成map
Map<String, User> users = userList.stream().collect(Collectors.toMap(User::getCode, c -> c));
但如果userList中用两个User.getCode()返回结果相同, 则会抛出异常java.lang.IllegalStateException: Duplicate key 1
可以使用userList.stream().collect(Collectors.toMap(User::getCode, c -> c, (a, b) -> a))
解决这个问题,它表示,当两个User.getCode()结果相同时,取第一个User。
时间api
java8还有个比较大的改动,就是时间API。
java8加入了LocalDate和LocalTime, 它们使用的是ISO8601标准时间格式,和时区无关,和unix时间戳类似.
unix时间戳表示从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数。 也是说, 一个unix时间戳n, 对于每个地区, 它都表示该地区从1970年1月1日(UTC/GMT的午夜)开始经过n秒后的时间。
如1507564800表示2017/10/10 00:00:00
, 如果在北京, 则表示北京时间2017/10/10 00:00:00
, 如果在伦敦, 则表示伦敦时间2017/10/10 00:00:00
(北京时间和伦敦时间时差八小时, 此时北京时间是2017/10/10 08:00:00
)
LocalDate和LocalTime同理。
// 当前时间
LocalDate now = LocalDate.now();
// 当前时间加一天, 并按常用格式yyyy-MM-dd格式化
now.plusDays(1).format(DateTimeFormatter.BASIC_ISO_DATE);
// 当前时间减一天, 并按指定格式格式化
now.minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
// 字符串转时间
LocalDate date = LocalDate.parse("2019-01-26", DateTimeFormatter.ISO_DATE);
// 时间比较
now.isAfter(date);
LocalDate与Date转换
// 时区ZoneId LocalDate时区不相关, Date时区, 两者转化需使用ZoneId
ZoneId zoneId = ZoneId.systemDefault();
LocalDate localDate = LocalDate.now();
// localDate转ZonedDateTime
ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
// ZonedDateTime转Date
Date date = Date.from(zdt.toInstant());
Date nowDate = new Date();
// Instant:nanosecond表示的时间戳
Instant instant = nowDate.toInstant();
// instant转ZonedDateTime
ZonedDateTime nowZonedDate = instant.atZone(zoneId);
// ZonedDateTime转LocalDate
LocalDate nowLocalDate = nowZonedDate.toLocalDate();
java7的新特性
简单记录一下java7比较重要的新特性
Path
java7新增了Path类, 可以便捷的对目录进行操作
当前resources目录结构为
|-- resources
|-- local.properties
|-- xml
|-- local.xml
看一个小栗子
@Test
public void testPath() throws URISyntaxException {
// 获取local.properties文件
URI localProUri = this.getClass().getClassLoader().getResource("local.properties").toURI();
// 转换为Path对象, 指向 .../resources/test/local.properties
Path localProPath = Paths.get(localProUri);
// 解析localProPath兄弟目录, 指向 .../resources/test/xml
Path xmlPath = localProPath.resolveSibling("xml");
// 解析xmlPath目录目录, 指向 .../resources/test/xml/local.xml
Path localXmlPath = xmlPath.resolve("local.xml");
// 转换为绝对路径
localXmlPath.toAbsolutePath();
// 获取文件名
localXmlPath.getFileName();
// 获取根目录
localXmlPath.getRoot();
// 获取父目录
localXmlPath.getParent();
}
Files
java7也新增了Files类, 可以对文件进行读取,写入, 复制,移动等操作
@Test
public void testFileRead() throws URISyntaxException, IOException {
Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());
// 读取文件中所以的字节
byte[] localProBytes = Files.readAllBytes(localProPath);
}
@Test
public void testFileCopy() throws URISyntaxException, IOException {
Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());
// 复制文件, StandardCopyOption.REPLACE_EXISTING表示替换已存在的文件
Path targetPath = Files.copy(localProPath, localProPath.getParent().resolve("dev.properties"), StandardCopyOption.REPLACE_EXISTING);
}
@Test
public void testAppend() throws URISyntaxException, IOException {
Path localProPath = Paths.get(this.getClass().getClassLoader().getResource("local.properties").toURI());
// 追加文件,StandardOpenOption.APPEND表示追加
Files.write(localProPath, "group=local".getBytes(), StandardOpenOption.APPEND);
}
这个有点类似于org.apache.commons.io.FileUtils类了。
try-with-resources
AutoCloseable是java7添加的接口, 实现了该接口的资源类可以使用try-with-resources语法
try (InputStream inputStream = new FileInputStream("local.properties")) {
...
} catch (IOException e) {
e.printStackTrace();
}
对比一下原来的写法
InputStream inputStream = null;
try {
inputStream = new FileInputStream("local.properties");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
捕获多个异常
java7中可以在同一个catch分支中捕获多个异常类型。引用《写给大忙人看的JavaSE8》中的一个栗子:
try {
...
} catch(FileNotFoundException | UnknownHostException ex) {
// 处理丢失文件和未知主机的异常
} catch() {
// 处理所有的IO异常
}
Objects
java7新增了Objects类, 提供一些常用的Object操作, 如deepEquals,isNull,nonNull,requireNonNull等。
网友评论