Java11相对Java8,在语法上的新特性并不多。主要有:
Java10以后可以用var定义一个局部变量,不用显式写出它的类型。但要注意,被var定义的变量仍然是静态类型,编译器会试图去推断其类型。
StringstrBeforeJava10="strBeforeJava10";varstrFromJava10="strFromJava10";System.out.println(strBeforeJava10);System.out.println(strFromJava10);因此,要注意:
//例如下面的语句编译会失败,"InCompatibletypes."strFromJava10=10;//例如下面这些都无法通过编译:vartestVarWithoutInitial;vartestNull=null;vartestLamda=()->System.out.println("test");vartestMethodByLamda=()->giveMeString();vartestMethod2=this::giveMeString;而推荐使用类型推断的场景有:
//如下所示,Map
HttpClient具有以下特性:
一旦构建完成,就可以使用HttpClient发送多个请求。
HttpRequest是由它的构建器创建的。请求的构建器可用于设置:
HttpRequest构建之后是不可变的,但可以发送多次。
请求既可以同步发送,也可以异步发送。当然同步的API会导致线程阻塞直到HttpResponse可用。异步API立即返回一个CompletableFuture,当HttpResponse可用时,它将获取HttpResponse并执行后续处理。
请求和响应的主体作为响应式流(具有非阻塞背压的异步数据流)供外部使用。HttpClient实际上是请求正文的订阅者和响应正文字节的发布者。BodyHandler接口允许在接收实际响应体之前检查响应代码和报头,并负责创建响应BodySubscriber。
HttpRequest和HttpResponse类型提供了许多便利的工厂方法,用于创建请求发布者和响应订阅者,以处理常见的主体类型,如文件、字符串和字节。这些便利的实现要么累积数据,直到可以创建更高级别的Java类型(如String),要么就文件流传输数据。BodySubscriber和BodyPublisher接口可以实现为自定义反应流处理数据。
HttpRequest和HttpResponse还提供了转换器,用于将java.util.concurrent.Flow的Publisher/Subscriber类型转换为HTTPClient的BodyPublisher/BodySubscriber类型。
JavaHTTPClient支持HTTP/1.1和HTTP/2。默认情况下,客户端将使用HTTP/2发送请求。发送到尚不支持HTTP/2的服务器的请求将自动降级为HTTP/1.1。以下是HTTP/2带来的主要改进:
由于HTTP/2是默认的首选协议,并且在需要的地方无缝地实现回退到HTTP/1.1,那么当HTTP/2被更广泛地部署时,JavaHTTP客户端就无需修正它的应用代码。
List.of根据传入的参数列表创建一个新的不可变List集合;List.copyOf根据传入的list对象创建一个不可变副本。
varlistImmutable=List.of("a","b","c");varlistImmutableCopy=List.copyOf(listImmutable);由于拷贝的集合本身就是一个不可变对象,因此拷贝实际上并没有创建新的对象,直接使用了原来的不可变对象。
//结果为trueSystem.out.println(listImmutable==listImmutableCopy);//不可变对象不能进行修改try{listImmutable.add("d");}catch(Throwablet){System.out.println("listImmutablecannotbemodified!");}try{listImmutableCopy.add("d");}catch(Throwablet){System.out.println("listImmutableCopycannotbemodified!");}如果想快速新建一个可变的集合对象,可以直接使用之前的不可变集合作为构造参数,创建一个新的可变集合。
varlistVariable=newArrayList<>(listImmutable);varlistVariableCopy=List.copyOf(listVariable);新创建的可变集合当然是一个新的对象,从这个新对象拷贝出来的不可变副本也是一个新的对象,并不是之前的不可变集合。
System.out.println(listVariable==listImmutable);//falseSystem.out.println(listVariable==listVariableCopy);//falseSystem.out.println(listImmutable==listVariableCopy);//false//新的可变集合当然是可以修改的try{listVariable.add("d");}catch(Throwablet){System.out.println("listVariablecannotbemodified!");}//可变集合拷贝出来的副本依然是不可变的try{listVariableCopy.add("d");}catch(Throwablet){System.out.println("listVariableCopycannotbemodified!");}Set的of和copyOfSet的of和copyOf与List类似。
varset=Set.of("a","c","r","e");varsetCopy=Set.copyOf(set);System.out.println(set==setCopy);但要注意,用of创建不可变Set时,要确保元素不重复,否则运行时会抛出异常:"java.lang.IllegalArgumentException:duplicateelement"
try{varsetErr=Set.of("a","b","a");}catch(Throwablet){t.printStackTrace();}当然创建可变set后添加重复元素不会抛出异常,但会被去重
varsetNew=newHashSet<>(set);setNew.add("c");System.out.println(setNew.toString());Map的of和copyOfMap的of和copyOf与list,set类似,注意of方法的参数列表是依次传入key和value:
varmap=Map.of("a",1,"b",2);varmapCopy=Map.copyOf(map);System.out.println(map==mapCopy);当然也要注意创建不可变Map时,key不能重复
注意null与""的区别:
longsize1=Stream.ofNullable(null).count();System.out.println(size1);//0longsize2=Stream.ofNullable("").count();System.out.println(size2);//1dropWhile与takeWhiledropWhile,对于有序的stream,从头开始去掉满足条件的元素,一旦遇到不满足元素的就结束
Listlst1=Stream.of(1,2,3,4,5,4,3,2,1).dropWhile(e->e<3).collect(Collectors.toList());System.out.println(lst1);//[3,4,5,4,3,2,1]takeWhile,对于有序的stream,从头开始保留满足条件的元素,一旦遇到不满足的元素就结束
Listlst2=Stream.of(1,2,3,4,5,4,3,2,1).takeWhile(e->e<3).collect(Collectors.toList());System.out.println(lst2);//[1,2]即使把剩下的元素都收集到了无序的set中,但在此之前,stream对象是有序的,因此结果包含了原来stream中最后的[a2]和[a1]:
Setset1=Stream.of("a1","a2","a3","a4","a5","a4","a3","a2","a1").dropWhile(e->"a3".compareTo(e)>0).collect(Collectors.toSet());System.out.println(set1);//[a1,a2,a3,a4,a5]如果先创建一个无序不重复的set集合,set无序更准确的说法是不保证顺序不变,事实上是有顺序的。因此这里会发现,dropWhile还是按set当前的元素顺序判定的,一旦不满足条件就结束。
Set
Stream
判断目标字符串是否是空白字符。以下结果全部为true:
//半角空格System.out.println("".isBlank());//全角空格System.out.println("".isBlank());//半角空格的unicode字符值System.out.println("\u0020".isBlank());//全角空格的unicode字符值System.out.println("\u3000".isBlank());//制表符System.out.println("\t".isBlank());//回车System.out.println("\r".isBlank());//换行System.out.println("\n".isBlank());//各种空白字符拼接System.out.println("\t\r\n".isBlank());strip,stripLeading与stripTrailing去除首尾的空白字符:
//全角空格+制表符+回车+换行+半角空格+<内容>+全角空格+制表符+回车+换行+半角空格varstrTest="\t\r\n你好jdk11\t\r\n";//strip去除两边空白字符System.out.println("["+strTest.strip()+"]");//stripLeading去除开头的空白字符System.out.println("["+strTest.stripLeading()+"]");//stripTrailing去除结尾的空白字符System.out.println("["+strTest.stripTrailing()+"]");repeat重复字符串内容,拼接新的字符串:
varstrOri="jdk11";varstr1=strOri.repeat(1);varstr2=strOri.repeat(3);System.out.println(str1);System.out.println(str2);//repeat传入参数为1时,不会创建一个新的String对象,而是直接返回原来的String对象。System.out.println(str1==strOri);lineslines方法用r或n或rn对字符串切割并返回stream对象:
inputStream.transferTo(outputStream);完整示例代码packagejdk11;importjava.io.*;/***InputStream增强**@authorzhaochun*/publicclassTestCase07InputStream{publicstaticvoidmain(String[]args){TestCase07InputStreamme=newTestCase07InputStream();me.test01_transferTo();}privatevoidtest01_transferTo(){varfilePath="/home/work/sources/test/jdk11-test/src/main/resources/application.yml";vartmpFilePath="/home/work/sources/test/jdk11-test/src/main/resources/application.yml.bk";FiletmpFile=newFile(tmpFilePath);if(tmpFile.exists()&&tmpFile.isFile()){tmpFile.delete();}try(InputStreaminputStream=newFileInputStream(filePath);OutputStreamoutputStream=newFileOutputStream(tmpFilePath)){//transferTo将InputStream的数据直接传输给OutputStreaminputStream.transferTo(outputStream);}catch(IOExceptione){e.printStackTrace();}}}三、模块化开发简介Java9引入了模块化,JavaPlatformModuleSystem,java平台模块系统,简称JPMS。
这里使用IDEA说明如何基于module进行开发。
使用idea新建工程module-test-jdk11,并删除根目录下的src目录。
选择工程根目录,右键,选择new->module:
next,输入module名称:
这里新建了两个module,一个叫core,一个叫main。
这里选择core,在其src目录下,新建包com.czhao.test.module.core,并新建classEmployee:
packagecom.czhao.test.module.core;importjava.util.Objects;/***@authorzhaochun*/publicclassEmployee{privateStringname;privateintlevel;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetLevel(){returnlevel;}publicvoidsetLevel(intlevel){this.level=level;}@Overridepublicbooleanequals(Objecto){if(this==o)returntrue;if(o==null||getClass()!=o.getClass())returnfalse;Employeeemployee=(Employee)o;returnlevel==employee.level&&name.equals(employee.name);}@OverridepublicinthashCode(){returnObjects.hash(name,level);}@OverridepublicStringtoString(){return"Employee{"+"name='"+name+'\''+",level="+level+'}';}}然后在src目录下,新建module-info.java(idea支持选择src->右键->new->module-info.java):
modulecore{exportscom.czhao.test.module.core;}这里表示通过exports把包com.czhao.test.module.core暴露出去。
首先,在idea的工程配置中,选择需要依赖core的module,在其依赖中添加modulecore:
然后,在modulemain的src目录下新建module-info.java:
modulemain{requirescore;}这里使用requires表示引入对modulecore的依赖。
然后可以在main的src目录下新建包和对应的类,并使用core暴露出来的类:
packagecom.czhao.test.module.main;importcom.czhao.test.module.core.Employee;/***@authorzhaochun*/publicclassAppMain{publicstaticvoidmain(String[]args){Employeeemployee=newEmployee();employee.setName("kobe");employee.setLevel(99);System.out.println(employee);}}如果没有依赖core,这里是无法使用core里的类Employee的。
main的module-info.java:
modulemain{requirescore;requiresjava.sql;}在modulemain的依赖中添加jdbc驱动包:
然后可以在代码中使用JDBC开发:
packagecom.czhao.test.module.main;importcom.czhao.test.module.core.Employee;importjava.sql.*;/***@authorzhaochun*/publicclassAppMain{publicstaticvoidmain(String[]args){Employeeemployee=newEmployee();employee.setName("kobe");employee.setLevel(99);System.out.println(employee);try(Connectionconnection=DriverManager.getConnection("jdbc:mysql://localhost:3306/db_jdk11_testuseUnicode=true&characterEncoding=UTF-8&useSSL=false","root","xxxxxx");PreparedStatementps=connection.prepareStatement("select*fromtb_employee")){ResultSetrs=ps.executeQuery();while(rs.next()){System.out.println(rs.getString("employee_name"));}}catch(SQLExceptione){e.printStackTrace();}}}四、新工具或新功能从Java9到Java11,陆续提供了一些新的工具或功能。
Java提供了一个新的工具jshell,Java终于可以像python,scala等语言那样,交互式演示语法了。
$/usr/java/jdk-11.0.7+10/bin/jshell|欢迎使用JShell--版本11.0.7|要大致了解该版本,请键入:/helpintrojshell>varstr1="helloworld";str1==>"helloworld"jshell>System.out.println(str1);helloworldjshell>4.2单文件源代码程序的直接执行一个单文件源代码,即,单独的java文件,有main方法,且只依赖jdk类库以及自己文件内部定义的类,可以直接用java执行而无需先编译再执行编译后的class文件了。
这对于一些简单的脚本开发是个利好。
在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行的JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java10的发布,这个问题总算得以解诀,JVM现在可以识别由容器控制组(cgroups)设置的约束,可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:
Unicode10新增了8518个字符,总计达到了136690个字符。包括56个新的emoji表情符号。
JDK11在java.lang下增加了4个类来处理:
Java实现了RFC7539中指定的ChaCha20和Poly1305两种加密算法,代替RC4。RFC7748定义的密钥协商方案更高效,更安全,JDK增加了两个新的接口XECPublicKey和XECPrivateKey。
免费的低耗能飞行记录仪和堆分析仪。
通过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式提供一个低开销的,为了排错java应用问题,以及JVM问题的数据收集框架。
希望达到的目标如下:
FlightRecorder源自飞机的黑盒子。FlightRecorder以前是商业版的特性,在java11当中开源出来,它可以导出事件到文件中,之后可以用JavaMissionControl来分析。
两种启动方式:
$jcmd
在保证低开销的基础上,JFR提供的能力也令人眼前一亮,例如:我们无需BCI就可以进行ObjectAllocationProfiling,终于不用担心BTrace之类把进程搞挂了。对锁竞争、阻塞、延迟,JVMGC、SafePoint等领域,进行非常细粒度分析。甚至深入JITCompiler内部,全面把握热点方法、内联、逆优化等等。JFR提供了标准的Java,C++等扩展API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供All-in-One解决方案。而这一切都是内建在JDK和JVM内部的,并不需要额外的依赖,开箱即用。
Java11新增了两种垃圾回收器,并改善了Java8开始提供的G1垃圾回收器。
Experimental(实验性质),生产环境不建议使用
ZGC是Java11最引人瞩目的新特性。
启用方法:-XX:+UnlockExperimentalVMOptions-XX:+UseZGC
启用方法:-XX:+UnlockExperimentalVMOptions-XX:+UseEpsilonGC
说明:开发一个处理内存分配但不实现任何实际内存回收机制的GC,一旦可用堆内存用完,JVM就会退出,如果有System.gc()调用,实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC效果一样),因为没有内存回收,这个实现可能会警告用户尝试强制GC是徒劳的。
主要用途如下:
对于G1GC,相比于JDK8,升级到JDK11即可免费享受到:并行的FullGC,快速的CardTable扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对G1的不断增强,其中串行FullGC等甚至是曾经被广泛诟病的短板,你会发现GC配置和调优在JDK11中越来越方便。
Java9到Java11,陆续移除了一些类库或功能。
在java11中移除了不太使用的JavaEE模块和CORBA技术。
CORBA来自于二十世纪九十年代,Oracle认为,现在用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。
但是删除CORBA将使得那些依赖于JDK提供部分CORBAAPI的CORBA实现无法运行。目前还没有第三方CORBA版本,也不确定是否会有第三方愿意接手CORBAAPI的维护工作。
在java11中将java9标记废弃的JavaEE及CORBA模块移除掉,具体如下:
只剩下java.xml,java.xml.crypto.jdk.xml.dom这几个模块。
其中,使用java.lang.invoke.MethodHandles.Lookup.defineClass来替代移除的sun.miss.Unsafe.defineClass。
将Javascript引擎标记为Deprecate,后续版本会移除,有需要的可以考虑使用开源的GraalVM。
java11中将pack200以及unpack200工具以及java.tiljar中的Pack200API标记为Deprecate。因为Pack200主要是用来压缩jar包的工具,由于网络下载速度的提升以及java9引入模块化系统之后不再依赖Pack200,因此这个版本将其标记为Deprecate。