很兴奋,我们的项目升级到了JDK17,接下来就介绍一些关于JDK中个人觉得有用的功能,毕竟升级了要有升级的样子,不要一层不变吃老本,会被diss的。当然也会穿插一些个人感觉很鸡肋的功能,会阐述其鸡肋的理由。
1.Optional类的增强
1.1 Stream — Optional流化 (JDK9)
//1.单个对象的流化
Optional.ofNullable("testString").stream().toList();
//2.对象中的属性流化
LoginUser loginUser = new LoginUser();
loginUser.setName("Test");
List<String> list = Arrays.asList("eat", "sleep", "dadoudou");
loginUser.setHobbies(list);
//方式一:使用orElse
Optional.ofNullable(loginUser)
.map(LoginUser::getHobbies)
.orElse(Collections.emptyList())
.stream().filter(value -> value.equals("eat"))
.collect(Collectors.toList());
//方式二:使用stream
Optional.ofNullable(loginUser)
.map(LoginUser::getHobbies)
.stream()
.flatMap(Collection::stream)
.filter(value -> value.equals("eat"))
.collect(Collectors.toList());
个人认为该方法实在是显得鸡肋,我们日常开发中Optional主要用于对象的判空,并且对于集合对象要求不返回null而是空集合,这时基本用不到Optional;再者stream一般都用于集合的,如果只有一个对象没有必要做流化操作;
1.2 ifPresentOrElse — Optional的if-else (JDK9)
public static void optionalMethod_ifPresentOrElse() {
Person person = new Person();
person.setId("test");
//if-else
if (null != person) {
System.out.println("Hello " + person.getId());
} else {
System.out.println("World");
}
Optional.ofNullable(person)
.ifPresentOrElse(value -> System.out.println("Hello " + value.getId()),
() -> System.out.println("World"));
//only if
if (null != person) {
System.out.println("Hello " + person.getId());
}
Optional.ofNullable(person)
.ifPresent(value -> System.out.println("Hello " + value.getId()));
}
该方法有助于解决if-else的判断,但是不带有返回结果。如果你想要带有结果返回,该方式不合适。
1.3 or方法 (JDK9)
Person nullPerson = null;
//普通的方式:
if(null == nullPerson){
Person person1 = new Person();
person1.setId("test1");
if(null == person1){
Person person2 = new Person();
person2.setId("test2");
return person2.getId();
}
return person1.getId();
}
//使用or的方式:
String result = Optional.ofNullable(nullPerson)
.or(() -> {
Person person1 = new Person();
person1.setId("test1");
return Optional.ofNullable(person1);
})
.or(() -> {
Person person2 = new Person();
person2.setId("test2");
return Optional.ofNullable(person2);
})
.map(Person::getId)
.orElse("id");
总的来看该方法可以有效解决如下的if结构:
/**
* Xxxx xxxx= xxxxService.getXxxxById();
* if( null == xxxx ){
* xxxx = bbbbService.getXxxxById();
* if( null == xxxx ){
* .....
* }
* }
*/
该方法可以解决多重if嵌套的情况,但是对于多种if嵌套的情况我还是建议大家提前return,这样显得逻辑更加清晰。
1.4 orElseThrow方法 (JDK10)
Optional.ofNullable(null).orElseThrow();
该方法会抛出Optional的内置异常NoSuchElementException
2 集合的增强
2.1 集合的静态工厂(JDK9)
List<String> list = List.of("test1", "test2", "test3");
//list.set(0, "test1_update"); 报UOE错
//set/map不能保证输出的顺序和定义元素/键值对的顺序一致
Set<String> set = Set.of("test1", "test2", "test3");
Map<String, String> map = Map.of("1", "test1", "2", "test2", "3", "test3");
Map<String, String> map2 = Map.ofEntries(Map.entry("1", "test1"), Map.entry("2", "test2"));
System.out.println(map);
- 注意创建的集合对象都不允许任何写操作, 所有的都不可以为null;
- set/map不能保证输出的顺序和定义元素/键值对的顺序一致;
- 和Arrays.asList产生的List不同,静态工厂是完全不能写,Arrays产生的list可以update不能add/remove;
2.2 takeWhile方法(JDK9)
List<Integer> list = List.of(1, 2, 33, 44, 4);
List<Integer> takeWhileResult = list.stream().takeWhile(value -> value < 33).collect(Collectors.toList());
System.out.println(takeWhileResult); //[1, 2]
- takeWhile 从头遍历 遇到不满足就结束流 返回从头到不满足的截断结果 作用类似break
- 如果使用不可修改的Set或者Map 在使用这个方法是要注意 会存在每次输出的结果都是不一样的
2.3 dropWhile方法(JDK9)
List<Integer> list = List.of(1, 2, 33, 44, 4);
List<Integer> dropWhileResult = list.stream().dropWhile(value -> value < 33).collect(Collectors.toList());
System.out.println(dropWhileResult); //[33, 44, 4]
- dropWhile 从头开始删除 遇到不满足的就结束流 返回原始流剩下的结果;
- 如果使用不可修改的Set或者Map 在使用这个方法和takeWhile一样是要注意;
- dropwhile可以理解成是takeWhile的补集;
2.4 copyOf的方法(JDK10)
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
List<String> copyList = List.copyOf(list);
list.add("ccc");
System.out.println(copyList); //[aaa, bbb]
集合提供了copyOf的方法 返回的不可以修改的集合 另外副本集合不会因为原集合的改变而改变
2.5 toList方法(JDK16)
List<String> list = List.of("hhh", "www", "ttt");
List<String> result1 = list.stream().toList();
List<String> result2 = list.stream().collect(Collectors.toUnmodifiableList());
List<String> result3 = list.stream().collect(Collectors.toList());
stream().toList
和Collectors.toUnmodifiableList()
都是不可修改的集合,Collectors.toList()
是生成的是普通的list,可写。- 性能比较:toList(优) –> Collectors.toList() –> Collectors.toUnmodifiableList(劣)
3. 类型推断(JDK10)
String value = "Hello World";
List<Integer> list1 = List.of(1, 2, 3);
List<String> list2 = List.of("1", "2", "3");
var value = "Hello World";
var list1 = List.of(1, 2, 3);
var list2 = List.of("1", "2", "3");
类型推断是JDK使用var的关键字来代替各种类型,实际上在编译后的class文件还是会把这些类型给加上。虽然在IDEA里面使用var后会有类型提示,但是在git的merge request里面简直就是灾难,到时候全是var恐怕连你自己都不知道返回的对象类型是什么了。
我个人认为很大程度影响了代码的可读性性,不推荐大家在日常开发中使用。
4. 支持文本块(JDK14)
//before
String text = " {\n" +
" \"value\":\"Hello World\",\n" +
" \"result\":\"data\";\n" +
" }";
//after
String text = """
{
"value":"Hello World",
"result":"data";
}
""";
文本代码块让整个String看上去都是比较整洁的,我第一能想到的就是可以帮助我们解决mock的json数据。因为在单元测试的过程中需要mock一些对象,主要是通过json的数据反序列化到这个对象。如果是之前的String格式需要加一些转义符,后期比如要增加个字段或者修改数据很麻烦,不能快速定位修改。
5. instance of 类型转换(JDK14)
//before
Object value = "abc";
if (value instanceof String) {
String newValue = (String) value;
System.out.println(newValue.length());
}
//after
if (value instanceof String newValue) {
System.out.println(newValue.length());
}
该特性很大程度上帮助了将代码化繁为简,推荐在日常中使用
6. NPE精准定位(JDK14)
//Before:
Exception in thread "main" java.lang.NullPointerException
at com.dev.wizard.feature.JDK17Demo.optionalMethod_orElseThrow(JDK17Demo.java:99)
at com.dev.wizard.feature.JDK17Demo.main(JDK17Demo.java:23)
//After:
Exception in thread "main" java.lang.NullPointerException: Cannot read field "student" because "persons" is null
at com.dev.wizard.feature.JDK17Demo.optionalMethod_orElseThrow(JDK17Demo.java:99)
at com.dev.wizard.feature.JDK17Demo.main(JDK17Demo.java:23)
7. 接口支持新特性 可以定义私有方法(JDK9)
这个理解很简单,就是在接口种可以定义一些私有的方法给default方法调用,仅此而已。
8. switch的增强
String value = "def";
================================对字符串的判断===============================================
//Before
switch (value){
case "abc":
System.out.println("Hello"); break;
case "def":
System.out.println("World"); break;
default:
System.out.println("Java"); break;
}
//After
switch (value){
case "abc" -> System.out.println("Hello");
case "def" -> System.out.println("World");
default -> System.out.println("Java");
}
================================对类型的判断===============================================
//Before
Object obj = "value";
if(obj instanceof String){
String newValue = obj + "TTT";
System.out.println(newValue);
}else if(obj instanceof Integer){
Integer number = (Integer) obj + 3;
System.out.println(number);
}else {
System.out.println(obj);
}
//After
switch (obj){
case String str -> {
String newStr = str + "TTT";
System.out.println(newStr);
}
case Integer number -> {
Integer newNumber = number + 3;
System.out.println(newNumber);
}
default -> System.out.println(obj);
}
================================对null值的处理===============================================
String value = null;
switch (value){
case null -> {return;}
case "abc" -> System.out.println("Hello");
case "def" -> System.out.println("World");
default -> System.out.println("Java");
}
================================对条件判断的处理===============================================
Object obj = 5;
switch (obj){
case String str -> {
String newStr = str + "TTT";
System.out.println(newStr);
}
case Integer number && number >= 5 -> {
Integer newNumber = number + 3;
System.out.println(newNumber);
}
default -> System.out.println(obj);
}
从上述的写法来看的话,主要使用了->
替代了break
语句,看上去更简洁。但是我有种感觉:switch是想最贱代替if-else吗。
9 总结
上述主要讲述了JDK的一些特性以及各种用法,同时描述特性中的一些注意点避免大家踩坑,当然JDK提供了很多新特性,我这里只是提供了一些有助于我们日常开发的一些特性,最后希望大家都能写出简约不简单的代码。