📢📢📢📣📣📣
哈喽!大家好,我是【Bug 终结者】 ,【CSDN新星创作者】🏆,阿里云技术博主🏆,51CTO人气博主🏆,INfoQ写作专家🏆
一位上进心十足,拥有极强学习力的【Java领域博主】😜😜😜
🏅【Bug 终结者】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。 偶尔会分享些前端基础知识,会更新实战项目,面向企业级开发应用!
🏅 如果有对【后端技术】、【前端领域】感兴趣的【小可爱】,欢迎关注【Bug 终结者】💞💞💞❤️❤️❤️ 感谢各位大可爱小可爱! ❤️❤️❤️
文章目录
一、Lambda表达式
什么是Lambda表达式?
Lambda 表达式是一个匿名函数(指的是没有函数名的函数),直接对应于其中的 Lambda 抽象,Lambda 表达式可以表示闭包
我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程,将一个函数作为参数进行传递。
Lambda 表达式允许把函数作为一个方法的参数,Lambda 表达式的基本语法如下:
(parameters)-> expression 或 (parameters)->{statements;}
Lambda 的使用如下例所示
Arrays.asList(1,2,6).forEach(i ->System.out.println(i))
以上的写法,是编辑器自动推测出来的参数类型,也可以指定参数类型
Arrays.asList(1,2,6).forEach((Integer i)->System.out.println(i))
在Java8之前,Java语言通过匿名函数的方法来替代Lambda表达式。
对于列表的排序,如果列表中里存放的是自定义的类,那么通常需要指定自定义的排序方法,传统方式如下
Person对象
packagecom.wanshi.common.bean;publicclassPerson{privateString name;privateint age;publicPerson(String name,int age){this.name = name;this.age = age;}publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicintgetAge(){return age;}publicvoidsetAge(int age){this.age = age;}@OverridepublicStringtoString(){return"Person{"+"name='"+ name +'\''+", age="+ age +'}';}}
Test测试类
packagecom.wanshi.common.bean;importjavax.imageio.plugins.jpeg.JPEGImageReadParam;importjava.util.Arrays;importjava.util.Comparator;publicclassTest2{publicstaticvoidmain(String[] args){Person[] people ={newPerson("James",25),newPerson("Jack",21)};//自定义类排序方法,通过年龄进行排序Arrays.sort(people,newComparator<Person>(){@Overridepublicintcompare(Person o1,Person o2){return o1.getAge()- o2.getAge();}});for(Person person : people){System.out.println(person);}}}
采用Lambda表达式优化后,如下
//两种方式Arrays.sort(people,(Person a,Person b)-> a.getAge()- b.getAge());
运行结果如下:
可见,我们采用Lambda表达式优化后,代码会更加简洁
Lambda表达式是用过函数式接口(只有一个方法的普通接口)来实现的,函数式接口可以被隐式转换为Lambda表达式,为了与普通的接口区分开,JDK1.8新增加了一种特殊的注解@FunctionalInterface 如下
@FunctionalInterfacepublicinterfaceFunction<T,R>{/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/Rapply(T t);}
二、方法的默认实现和静态方法
JDK1.8通过使用关键字default可以给接口中的方法添加默认实现,此外,接口中还可以定义静态方法,如下
packagecom.wanshi.common.bean;/**
* Lambda表达式是通过函数式接口(只有一个方法得接口)来实现的。函数式接口可以被隐式地转换为Lambda表达式,
* 为了与普通的接口区分开(普通接口中可能会有多个方法),jdk1.8新增加了一种特殊的注解@FunctionalInterface
*/@FunctionalInterfacepublicinterfaceFun{voidf();defaultvoidg(){System.out.println("this is default method in interface");}staticvoidh(){System.out.println("this is static method in interface");}}
那么为什么要引入接口中方法的默认实现呢?
其实,这样做的最重要的一个目的就是为了实现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,那么会导致所有实现这个接口i的类都要被修改,这给Java语言已有的一些框架进行升级带来了很大的麻烦,如果接口能够支持默认方法的实现,那么可以给这些类库的升级带来许多便利,例如,为了支持Lambda表达式,Collection中引入了forEach方法,可以通过这个语法增加默认的实现,从而降低对这个接口进行升级的代价,不需要所有实现这个接口的类进行修改。
三、方法引用
方法引用指的是可以直接引用Java类或对象的方法,它可以被看成是一种更加简介易懂的Lambda表达式,使用方法引用后,上个例子中的排序代码可以更加简洁的编写
Arrays.sort(people,Comparator.comparing(Person::getAge));
方法引用共有下面4种形式
- 引用构造方法:ClassName::new
- 引用类静态方法:ClassName::methodName
- 引用特定类 的任意对象方法:ClassName::methodName
- 引用某个对象的方法:ClassName::methodName
下面是一个方法引用的例子:
packagecom.wanshi.common.bean;importjava.util.Arrays;importjava.util.Comparator;importjava.util.function.Supplier;publicclassPerson{privateString name;privateint age;publicPerson(){}publicPerson(String name,int age){this.name = name;this.age = age;}publicstaticPersongetInstance(finalSupplier<Person> personSupplier){return personSupplier.get();}publicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicintgetAge(){return age;}publicvoidsetAge(int age){this.age = age;}@OverridepublicStringtoString(){return"Person{"+"name='"+ name +'\''+", age="+ age +'}';}publicstaticintcompareByAge(Person a,Person b){return b.getAge()- a.getAge();}}classCompareProvider{publicintcompareByAge(Person a,Person b){return a.getAge()- b.getAge();}}classTest{publicstaticvoidmain(String[] args){//引用构造方法Person person =Person.getInstance(Person::new);
person.setAge(19);System.out.println("测试引用构造方法:"+ person.getAge());Person[] people ={newPerson("James",25),newPerson("Jack",21)};//引用特定类的任意对象方法Arrays.sort(people,Comparator.comparing(Person::getAge));System.out.println("测试引用特定类的任意对象方法:");for(Person person1 : people){System.out.println(person1);}Arrays.sort(people,Person::compareByAge);System.out.println("测试引用类静态方法:");for(Person person1 : people){System.out.println(person1);}//引用某个对象的方法Arrays.sort(people,newCompareProvider()::compareByAge);System.out.println("测试引用引用某个对象的方法:");for(Person person1 : people){System.out.println(person1);}}}
测试结果如下
四、注解(Annotation)
- JDK1.5引入了注解机制,但是有一个限制:相同的注解在同一位置只能声明一次。JDK1.8引入了重复注解机制后,相同的注解在同一个地方可以声明多次。
- JDK1.8 对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量,泛型,方法异常提供注解。
五、类型推测
JDK1.8加强了类型推测机制,这种机制可以使得代码更为简洁,假如有以下类的定义
classList<E>{static<Z>List<Z>nil(){...};static<Z>List<Z>cons(Z head,List<Z> tail){...};Ehead(){...}}
在调用的时候,可以使用下面的代码
//通过赋值的目标类型来推测泛型的参数List<Integer> l =List.nil();
在JDK1.7的时候,这种写法将会产生编译错误,Java7的正确写法如下
List<Integer> l =List<Integer>List.nil();
同理,在调用 cons 方法的时候写法为:
//通过方法的第一个参数来推测泛型的类型List.cons(5,List.nil());
六、参数名字
JDK1.8通过在编译的时候增加 -parameters 选项,以及增加反射API与 Parameter,getName() 方法实现了获取方法参数名的功能。
示例代码如下
packagecom.wanshi.common.bean;importjava.lang.reflect.Method;importjava.lang.reflect.Parameter;publicclassTest6{publicstaticvoidmain(String[] args){Method method;try{
method =Test6.class.getMethod("main",String[].class);for(Parameter parameter : method.getParameters()){System.out.println("parameter::"+ parameter.getName());}}catch(Exception e){
e.printStackTrace();}}}
如果使用指令 javac Test6.java 来编译以上程序,那么运行的结果是 parameter::args()
如果使用的是 javac Test6.java -parameters 来编译 那么结果是parameter::args
七、新增Optional类
在使用Java语言的进行编程的时候,经常需要使用大量的代码来处理空指针异常,而这种操作往往会降低程序的可读性,JDK1.8引入了Optional类来处理空指针的情况,从而提高代码的可读性,如下
publicstaticvoidmain(String[] args){Optional<String> s1 =Optional.of("hello");//判断是否有值if(s1.isPresent()){//获取值System.out.println(s1.get());}Optional<Object> s2 =Optional.ofNullable(null);if(s2.isPresent()){System.out.println(s2.get());}}
这里只是介绍了 Optional 简单的使用示例,更多可参考JDK帮助文档
八、新增Stream 类
JDK1.8新增了Stream类,从而把函数式编程的风格引入到Java语言中,Stream类的API提供了强大的功能,使用Stream后,可以写出更加强大,更加简洁的代码
packagecom.wanshi.common.bean;importjava.util.*;importjava.util.stream.Collectors;publicclassTest3{publicstaticvoidmain(String[] args){List<Person> l =newArrayList<>();
l.add(newPerson("Wang",10));
l.add(newPerson("Li",13));
l.add(newPerson("Zhang",10));
l.add(newPerson("Zhao",15));System.out.println("找出年龄为10的第一个人类:");Optional<Person> s = l.stream().filter(person -> person.getAge()==10).findFirst();if(s.isPresent()){System.out.println(s.get().getName()+", "+ s.get().getAge());}System.out.println("找出年龄为10的所有人类:");List<Person> personList = l.stream().filter(person -> person.getAge()==10).collect(Collectors.toList());
personList.forEach(person ->{System.out.println(person.getName()+", "+ person.getAge());});System.out.println("对人类年龄分组");Map<Integer,List<Person>> map = l.stream().collect(Collectors.groupingBy(Person::getAge));Iterator<Map.Entry<Integer,List<Person>>> iterator = map.entrySet().iterator();while(iterator.hasNext()){Map.Entry<Integer,List<Person>> next = iterator.next();Integer age = next.getKey();System.out.println(age+":");List<Person> value = next.getValue();
value.forEach(person ->{System.out.print(person.getName()+" ");});System.out.println();}}}
运行结果为
九、日期新特性
在JDK1.8以前,处理日期相关的类主要有如下三个
- Caalendar:实现日期和时间字段之间的转换,它的属性是可变的,因此,它不是线程安全的。
- DateFormat:格式化和分析日期字符串
- Date:用来承载日期和时间信息,它的属性是可变的,因此,它不是线程安全的。
这些API使用起来都不是很方便的,而且有很多缺点,如下:
Date date =newDate(2022,5,1);System.out.println(date);
在Date类传入参数,月份为5月,但输出却是 Thu Jun 01 00:00:00 CST 3922
JDK1.8 对日期相关的API进行了改造,提供了更加强大的API。新的java.time 主要包含了处理日期、时间、日期/时间、时区、时刻 (instants)、和时钟(clock) 等操作。下面是使用案例
packagecom.wanshi.common.bean;importjava.time.*;publicclassTest4{publicstaticvoidmain(String[] args){//Clock类通过指定一个时区,可以获取到当前的时刻,日期与时间Clock clock =Clock.system(ZoneId.of("Asia/Shanghai"));System.out.println("测试Clock:");System.out.println(clock.millis());System.out.println(clock.instant());//Instant 使用方法System.out.println("测试Instant:");Instant now =Instant.now();//精确到秒System.out.println(now.getEpochSecond());//精确到毫秒System.out.println(now.toEpochMilli());//LocalDate 以ISO-8601 格式显示的日期类型,无时区信息LocalDate date =LocalDate.now();LocalDate dateFromClock =LocalDate.now(clock);System.out.println("测试LocalDate:");System.out.println(date);System.out.println(dateFromClock);//LocalTime是以ISO-8601 格式显示日期类型,无时区信息finalLocalTime time =LocalTime.now();LocalTime timeFromClock =LocalTime.now(clock);System.out.println("测试LocalTime:");System.out.println(time);System.out.println(timeFromClock);//LocalDateTime 以ISO-8601 格式显示的日期和时间LocalDateTime dateTime =LocalDateTime.now();LocalDateTime datetimeFromClock =LocalDateTime.now(clock);System.out.println("测试LocalDateTime:");System.out.println(dateTime);System.out.println(datetimeFromClock);}}
执行结果
十、调用JavaScript
JDK1.8增加API使得通过Java程序来调用JavaScript代码,使用示例:
publicstaticvoidmain(String[] args)throwsScriptException{ScriptEngineManager manager =newScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");System.out.println(engine.getClass().getName());System.out.println(engine.eval("function f() {return 'hello';}; f() + ' world!';"));}
运行结果
十一、Base64
Base64 编码是一种常见的字符编码,可用来作为电子邮件或Web Service 附件的传输编码,JDK1.8把Base64 编码添加到了标准类库中,示例代码:
publicstaticvoidmain(String[] args){String str ="Hello World!";// 编码String encodeStr =Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));System.out.println(encodeStr);//解码String decodeStr =newString(Base64.getDecoder().decode(encodeStr),StandardCharsets.UTF_8);System.out.println(decodeStr);}
执行结果
十二、并行数组
JDK1.8增加了对数组并行处理的方法(parallelXxx),下面以排序为例介绍其用法
publicstaticvoidmain(String[] args)throwsScriptException{int[] arr ={1,3,2,4,55,64,98,23};Arrays.parallelSort(arr);Arrays.stream(arr).forEach(i ->{System.out.print(i +" ");});System.out.println();}
执行效果
拓展1:常见加密算法
⛺三种加密算法分类
- 对称加密:密钥只有一个,解密、加密都是这一个密钥,加解密速度快,典型的有DES、AES、RC4
- 非对称加密:密钥成对出现,分别为公钥和私钥,从公钥无法推算私钥,相反,私钥也无法推算公钥,加解密使用不同的密钥,公钥加密需要私钥解密,私钥加密需要公钥解密,非对称加密速度极慢,典型的有RSA、DSA、DSS.
- Hash算法:这是一种不可逆的算法,它常用于验证数据的完整性。****验证文件完整性,是否上传过文件,每一个文件都有自己独有且唯一的MD5值
拓展2:Base64、AES、MD5区别
⏰为什么要对数据加密,不加密可以嘛?
首先回答,不加密不可以,因为在服务器与终端设备进行HTTP通讯时,常常会被网络抓包、反编译(Android APK反编译工具)等技术得到HTTP通讯接口地址和参数,为了确保信息的安全,我们必须对数据进行加密处理,并且取数据时进行解密!
⛽Base64编码算法
把任意序列的8为字节描述为不能直接用肉眼识别的形式, 通常用于邮件、图片、http加密. 登陆的用户名和密码字段通过它加密, 可以进行加密和解密。
⚽MD5哈希加密算法
md5是不可逆的,md5没有解密的方法,最好的反驳就是:数据源是无穷尽的,而 MD5密文是有限的。这里的加密解密是对md5算法先加密后解密,而不是对md5解密。
➿AES对称加密算法
AES是对称性加密算法,需要对加密和解密使用相同密钥的加密算法。由于其速度快,对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密,所谓**对称加密就是加密和解密都是使用
“ 同一个密钥 ”
。**
✏️总结
- Base64:可以逆运算,一般用于图片
- MD5:不可逆的,防篡改的,用来校验数据真伪的,不是用来加密数据的,检验文件的完整性!
- AES:更快,兼容更多的设备,安全级别很高,用于身份证号加密
⛵小结
以上就是【Bug 终结者】对Java8新特性简单的概述,Java8新特性要掌握住,同时在工作中也很常用,例如Lambda、Stream等,是很常用的技术,技术更新迭代,我们需要保持努力学习,让自己跟上社会的进步!
如果这篇【文章】有帮助到你,希望可以给【Bug 终结者】点个赞👍,创作不易,如果有对【后端技术】、【前端领域】感兴趣的小可爱,也欢迎关注❤️❤️❤️ 【Bug 终结者】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💝💝💝!
版权归原作者 Bug 终结者 所有, 如有侵权,请联系我们删除。