朋也的博客 » 首页 » 文章
作者:朋也
日期:2019-02-21
类别:java学习笔记
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
首先感谢尚硅谷录制的Java11视频教程,可以免费下载观看
之前在ytb上看了个java9的新特性,里面提到了两个主要更新,jshell和module
现在java11又发布很久了,再不学真要过时了
java8是个官方长期支持版本,java11也是,9,10只是过渡版本,所以完全可以路过9,10直接学习11,这一篇博客也是根据视频内容整理出来的,我觉得文字看的方便些,如果你觉得视频好看,也可以去尚硅谷官方下载视频查看
jshell在java9里被提出来的,就是可以直接在终端里写java程序了,回车就可以执行,不用先创建java文件,然后编译成class文件,最后再执行了,它把这些步骤都省了,打开终端,输入jshell,回车之后会进入jshell的环境,然后就可以直接写你要测试的代码了
类似于nodejs中直接在终端里输入 node
和python中直接在终端里输入 python
回车是一样的
如果不会用可以通过 /help
查看里面都有哪些命令
jshell默认会导入一些包,如下
import java.lang.*;
import java.io.*;
import java.math.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.prefs.*;
import java.util.regex.*;
import java.util.stream.*;
默认导入了有这些包,也就是说在jshell环境里可以默认使用这些包里的东西
Module也是在Java9里就加入的功能,这里说一下用法,我这里用的是idea操作的
新建一个java项目,然后将项目下的src删掉,然后对项目右键,选择 New -> Module 填上Module的名字
我这创建了两个Module,分别是 core, main
在core下有个包 co.yiiu.core
包里有个类,如下
package co.yiiu.core;
/**
* Created by tomoya at 2019-02-22
*/
public class User {
private String username;
private Integer age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
然后在core里的src目录下新建一个 module-info.java
里面内容如下
module core {
exports co.yiiu.core;
}
可以看到这里用到了 exports
关键字,意思是将 co.yiiu.core
包暴露出去,别的模块在引入core模块的时候就可以使用core模块里这个包下的类了
然后是main模块,也是一样,创建一个包 co.yiiu.main
,包下有个类 Main.java
内容如下
package co.yiiu.main;
import co.yiiu.core.User;
public class Main {
public static void main(String[] args) {
User user = new User();
user.setUsername("tomoya");
user.setAge(11);
System.out.println(user.toString());
}
}
如果你跟着操作,到这一步Main.java里会报错,说找不到User类,这时在main模块的src里新建一个 module-info.java
文件,引入core模块即可
module main {
requires core;
}
这里用到了 requires
关键字,意思是引入其它模块的意思,再看看Main.java文件,就没有错误信息了,然后运行 Main.java 的main方法,可以看到输出内容 User{username='tomoya', age=11}
当然也可以引入jdk暴露的模块,比如 java.sql
在自己需要用到java.sql
模块的 module-info.java
里引入,比如我在 main模块下引入
module main {
requires core;
requires java.sql;
}
然后就可以在Main.java类中使用java.sql里的类了,比如获取数据库连接
package co.yiiu.main;
import co.yiiu.core.User;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
User user = new User();
user.setUsername("tomoya");
user.setAge(11);
System.out.println(user.toString());
// 获取数据库连接,这没写完,做个测试
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/pybbs");
}
}
这里只介绍了简单使用,如果想了解更多,可以查看这篇文章 https://www.baeldung.com/java-9-modularity
这个关键字是java10里增加的,类似于javascript里的var,什么变量都可以用它来定义,至于变量是什么类型的,java在编译的时候会根据变量的值来推断
其实在java7里就有了对泛型推断的功能,比如定义一个集合
List<Integer> list = new ArrayList<>();
第二个泛型没有写,程序在编译的时候会根据第一个泛型的类型来推断这个集合的泛型是啥,java10里引入的var定义变量也是一样的原理,只不过var声明的变量是根据值来推断变量的类型
注意
Consumer<String> consumer = (var t) -> System.out.println(t.toUpperCase());
这里的 var t
就是根据前面定义 consumer 的时候指定的泛型来推断的@Nonnull
,意思就是这个变量不能为空这个方法是在java9的时候加入的,我记得springboot2.x就有of方法了
看看用法吧
// 之前定义一个集合
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
System.out.println(list);
// 之前简写的方法
List<String> list = Arrays.asList("aa", "bb", "cc");
System.out.println(list);
// of写法
List<String> list = List.of("aa", "bb", "cc");
System.out.println(list);
其实跟 asList()
方法类似
注意
asList()
和 of()
两种方法创建的集合都是只读的集合,不可新增元素
Set也有of方法,跟List一样用法,只是如果用传统的方式 Set<Integer> set = new HashSet<>();
的话,当添加重复元素的时候,会覆盖之前的元素,但如果用of方法的创建set集合的话,出现重复元素会直接抛异常
原链文接:https://atjiu.github.io/2019/02/21/java11/
Stream在java8的时候就引入了
流的处理三个步骤
通过of()创建一个流
Stream<Integer> stream = Stream.of(1,2,4,5,6,7);
stream.forEach(System.out::println);
// -------------------
Stream<Integer> stream = Stream.of(); // 创建一个空流
stream.forEach(System.out::println);
stream.forEach(System.out::println);
// -------------------
// 这种创建会抛异常,因为传入一个null,会被解析成一个数组对象,然后进一步访问null的长度,自然就报错了
Stream<Integer> stream = Stream.of(null);
stream.forEach(System.out::println);
// -------------------
// 上一个例子中展示了不能通过 of() 创建一个空流,但可以通过 ofNullable() 函数创建一个空流,下面例子不会抛出异常
Stream<Integer> stream = Stream.ofNullable(null);
stream.forEach(System.out::println);
takeWhile() 和 dropWhile()
take是拿的意思,drop是丢弃的意思,这两个方法意思是判断方法参数里的条件,如果为true就拿/丢然后生成一个新流,看下面例子
Stream<Integer> stream = Stream.of(3,9,20,22,40,7);
Stream<Integer> stream1 = stream.takeWhile(t -> t % 2 != 0);
stream1.forEach(System.out::println); // 3,9
可以看到上面例子打印出来结果只有3,9,说明takeWhile是一直从流中获取到判定器判断为true的元素,只要碰到false了就终止了,不管后面还有没有符合条件的元素
dropWhile() 方法例子
Stream<Integer> stream = Stream.of(3,9,20,22,40,7);
Stream<Integer> stream1 = stream.dropWhile(t -> t % 2 != 0);
stream1.forEach(System.out::println); // 20,22,40,7
dropWhile() 方法跟takeWhile()方法正好相反,dropWhile()是判定器判断为true的舍弃,直到找到为false的时候,就不再判断了,直接把后面所有的元素全拿到然后生成一个新流,所以也就有了结果为 20,22,40,7
流的迭代,创建流
// java8里的方法 iterate(final T seed, final UnaryOperator<T> f)
// 创建一个无限流,起始值是1,后面每一项都在前一项的基础上 * 2 + 1
Stream<Integer> stream = Stream.iterate(1, t -> 2 * t + 1);
// 因为这是一个无限流,如果不加limit限制一下的话,它就会一直打印,所以这里限制一下只打印10个,结果如下
stream.limit(10).forEach(System.out::println);
//1,3,7,15,31,63,127,255,511,1023
// ------------------------------------------------------------
// java9 里新增的方法 iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
// 这个视频里说错了,这个方法是java9里就加入了,不是java11里新增的,新增了一个判定器,这样就不会出现无限流了,如下例子
Stream<Integer> stream = Stream.iterate(1, t -> t < 1000, t -> 2 * t + 1);
stream.forEach(System.out::println);
//1,3,7,15,31,63,127,255,511
判断字符器是否都是空白 isBlank()
注意不是判断是否为空,这个是java11里新增的方法
String str = "\t \r\n ";
System.out.println(str.isBlank()); // 结果 true
去掉字符串首尾空白 - since 11
String str = "\t \r\n ";
String str1 = str.strip(); // 去掉字符串首尾的空白(不是空格)
System.out.println(str1); //
System.out.println(str1.length()); // 0
strip() 跟 trim() 都是去掉首尾的空白字符,但trim()只能去掉特定范围内的空白字符,比如中文输入法下的空格,trim()就没办法去掉
但strip()就可以去掉英文以及其它所有语言的空白字符
相应的方法还有
InputStream 增加了一个非常有用的方法 transferTo()
可以将数据直接传输到OutputStream,这是在处理原始数据流时非常常见的一种用法
var classLoader = ClassLoader.getSystemClassLoader();
// 使用类加载器加载文件,将其读成一个InputStream
var is = classLoader.getResourceAsStream("javastack.txt");
try {
// 创建一个临时文件
var javastack = File.createTempFile("javatack2", "txt");
try (var os = new FileOutputStream(javastack)) {
is.transferTo(os);
}
is.close();
} catch (IOException e) {
e.printStackTrace();
}
简单使用案例(同步请求)
HttpClient client = HttpClient.newHttpClient();
// 构建请求体
HttpRequest request = HttpRequest.newBuilder(URI.create("https://atjiu.github.io/")).build();
// 构建响应处理器
HttpResponse.BodyHandler<String> bodyHandler = HttpResponse.BodyHandlers.ofString();
// 发送同步请求并获取响应,这一步会阻塞
HttpResponse<String> response = client.send(request, bodyHandler);
System.out.println(response.body());
异步请求
HttpClient client = HttpClient.newHttpClient();
// 构建请求体
HttpRequest request = HttpRequest.newBuilder(URI.create("https://atjiu.github.io/")).build();
// 构建响应处理器
HttpResponse.BodyHandler<String> bodyHandler = HttpResponse.BodyHandlers.ofString();
// 发送异步请求,这里会立即获取返回值
CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, bodyHandler);
// 从返回值里调用get()方法拿服务器响应内容,这一步会阻塞
HttpResponse<String> response = sendAsync.get();
System.out.println(response.body());
移除了
替代项
废弃项
在Java11里可以直接使用 java 命令来执行单个的java文件,执行命令 java Hello.java
执行过程中不会生成class文件
有个疑问,执行完后目录下确实没有.class
文件,但第一次执行会很慢,当第二次再执行的时候,就会快很多,我感觉它还是生成.class
文件了,只是没有在当前目录下,如果有知晓的,求告知
注意
所以这个功能还是很鸡肋的
Unicode 10 增加了8518个字符,总计达到了136690个字符,并且增加了4个类来处理,同时还有56个新的emoji表情符号
新增的四个类在 java.lang
包下,分别是
链原文接:https://atjiu.github.io/2019/02/21/java11/
在java11中移除了不太使用的JavaEE模块和CORBA技术。
CORBA来自于二十世纪九十年代,Oracle说,现在用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。
但是删除CORBA将使得那些依赖于JDK提供部分CORBAAPI的CORBA实现无法运行。目前还没有第三方CORBA版本,也不确定是否会有第三方愿意接手CORBA API的维护工作。
在java11中将java9标记废弃的Java EE及CORBA模块移除掉,具体如下:
xml 相关:
只剩下java.xml, java.xml.crypto.jdk.xml.dom 这几个模块
其它
将Javascript引擎标记为过时,后续版本会移除,有需要的可以考虑使用GraalVM
Java5中带了一个压缩工具:Pack2o0,这个工具能对普通的jar文件进行高效压缩。其实现原理是根据Java类特有的结构,合并常数池,去掉无用信息等来实现对java类的高效压缩。由于是专门对Java类进行压缩的,所以对普通文件的压缩和普通压缩软件没有什么两样,但是对于Jar文件却能轻 易达到10-40%的压缩率。这在Java应用部署中很有用,尤其对于移动Java计算,能够大大减小代码下载量。
Java5中还提供了这一技术的API接口,你可以将其嵌入到你的程序中使用。使用的方法很简单,下面的短短几行代码即可以实现jar的压缩和解压
压缩
Packer packer = Pack200.newPacker();
OutputStream output = new BufferedOutputStream(new FileOutputStream(outFile));
packer.pack(new JarFile(jarFile), output);
output.close();
解压
Unpacker unpacker = Pack200.newUnpacker();
OutputStream output = new JarOutputStream(new FileOutputStream(jarFile));
unpacker.unpack(pack200File, output);
output.close();
Pack200的压缩和解压缩速度是比较快的,而且压缩率也是很惊人的,在我是使用的包 4.46MB压缩后成了1.44MB (0.322%),而且随着包的越大压缩率会根据明显,据说如果jar包都是class类可以压缩到1/9的大小。 其实JavaWebStart 还有很多功能,例如可以按不同的jar包进行lazy下载和单独更新,设置可以根据jar中的类变动进行class粒度的下载
但是在java11中废除了pack200以及unpack200工具以及java.tiljar中的Pack200 API。因为Pack200主要是用来压缩jar包的工具,由于网络下载速度的提升以及java9引入模块化系统之后不再依赖Pack200,因此这个版本将其移除掉
Experimental(实验性质),生产环境不建议使用
A NoOp Garbage Collectors
启用方法:-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
JDK上对这个特性的描述是:开发一个处理内存分配但不实现任何实际内存回收机制的GC,一旦可用堆内存用完,JVM就会退出,如果有System.gc()
调用,实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC
效果一样), 因为没有内存回收,这个实现可能会警告用户尝试强制GC是徒劳
class Garbage {
int n = (int) (Math.random() * 100);
@Override
public void finalize() {
System.out.println(this + " : " + n + " is dying");
}
}
public class EpsilonTest {
public static void main(String[] args) {
boolean flag = true;
List<Garbage> list = new ArrayList<>();
long count = 0;
while(flag) {
list.add(new Garbage());
if(list.size() == 1000000 && count == 0) {
list.clear();
count++;
}
}
System.out.println("程序结束");
}
}
如果使用选项-XX: +UseEpsilonGC
程序很快就因为堆空间不足而退出,但如果不使用EpsilonGC,程序会在生成垃圾和清除垃圾之间犹豫,要等好一会才会因为堆空间不足而退出
使用这个选项的原因:
提供完全被动的GC实现,具有有限的分配限制和尽可能低的延迟开销,但代是内存占用和内存吞吐量, 众所周知,java 实现可广泛选择高度可配置的GC实现.各种可用的收集器最终满足不同的需求,即使它们的可配置性使它们的功能相交.有时更容易维护单独的实现,而不是在现有GC实现上堆积另一个配置选项.
主要用途如下:
使用-Xmx1g -XX:+UseEpsilonGC
如果程序有问题,则程序会崩溃
Experimental(实验性质),生产环境不建议使用
启用方法:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
这应该是java11最为瞩目的特性,没有之一,但是它也是实验性质,生产环境不建议使用
ZGC, A Scalable Low-Latency Garbage collector( Experimental) ZGC一个可伸缩的低延时的垃圾回收器
GC暂停时间不会超过10ms,既能处理几百兆的小堆,也能处理几介T的大堆(OMG)和G1相比,应用吞吐能力不会下降超过15%为未来的GC功能和利用colord指针以及Load barriers 优化奠定基础。初始只支持64位系统
ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。将来还可以扩 展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆
GC是java主要优势之一.然而,当GC停顿太长,就会开始影响应用的响应时间.消除或者减少GC停顿时长,java将对更广泛的应用场景是一个更有吸引力的平台.此外,现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存,并且无需长时间的GC暂停时间
ZGC是一个并发,基于region,压缩型的垃圾收集器,只有root扫描阶段会STW,因此GC停顿时间不会随着堆的增长和存活对象的增长而变长
垃圾回收机 | 等待平均值(ms) | 等待最大值(ms) |
---|---|---|
ZGC | 1.091 | 1.681 |
G1 | 156.806 | 543.846 |
许多运行在Java虚拟机中的应用程序(包括ApacheSpark和Kafka等数据服务以及传统的企业,应用程序)都可以在Docker容器中运行。但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java10的发布,这个问题总算得以解诀,JVM现在可以识别由容器控制组(cgroups) 设置的约束。可以在容器中使用内存和CPU约束来直接管理Java应用程序
其中包括:
容器的内存限制
在Java9之前,JVM无法识别容器使用标志设置的内存限制和CPU限制。而在Java10中,内存限制会自动被识别并强制执行
Java将服务器类机定义为具有2个CPU和2GB内存,以及默认堆大小为物理内存的1/4.例如,Docker企业版安装设置为2GB内存和4个CPU的环境,我们可以比较在这个Docker容器上运行Java8和Java10的区别
对于支持docker容器是jdk自动完成了,原来docker怎么配置,现在还怎么配置就行
对于G1 GC,相比于JDK8,升级到JDK 11即可免费享受到:并行的Full GC,快速的CardTable扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对G1的不断增强,其中串行FullGC等甚至是曾经被广泛诟病的短板,你会发现GC配置和调优在JDK11中越来越方便
免费的低耗能飞行记录仪和堆分析仪
通过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式提供一个低开销的,为了排错java应用问题,以及JVM问题的数据收集框架
希望达到的目标如下:
实现了RFC7539中指定的ChaCha20和Poly1305两种加密算法,代替RC4
RFC7748定义的密钥协商方案更高效,更安全,JDK增加了两个新的接口XECPublicKey
XECPrivateKey
Flight Recorder 源自飞机的黑盒子。 Flight Recorder 以前是商业版的特性,在javal1当中开源出来,它可以导出事件到文件中,之后可以用Java Mission Control 来分析。
两种启动方式
$ jcmd <pid> JFR.start # 启动记录仪
$ jcmd <pid> JFR.dump.filename=recording.jfr # 将记录内容保存到文件里
$ jcmd <pid> JFR.stop # 停止记录仪
查看jfr文件在java11里没有办法,不过在java12里已经加入了jfr命令,可以查看jfr文件
是Oracle刚刚开源的强大特性。我们知道在生产系统进行不同角度的Profiling,有各种工具、框架,但是能力范围、可靠性、开销等,大都差强人意,要么能力不全面,要么开销太大,甚至不可靠可能导致Java 应用进程宕机。
而JFR是一套集成进入JDK、JVM内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在SPECjbb2015等基准测试中,JFR的性能开销最大不超过1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启JFR进行特定诊断,也可以让系统长期运行JFR,用以在复杂环境中进行“After-the-fact”分析。还需要苦恼重现随机问题吗? JFR让问题简化了很多
在保证低开销的基础上, JFR提供的能力也令人眼前一亮,例如:我们无需BCI就可以进行Object Allocation Profiling, 终于不用担心BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行非常细粒度分析。甚至深入JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR提供了标准的Java.C++ 等扩展API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供All-in-One 解决方案。而这一切都是内建在JDK和JVM内部的,并不需要额外的依赖,开箱即用。
再次感谢尚硅谷录制的这么好的视频教程,还免费供用户下载查看
视频中还提到了对http1.3的支持,只是提了一下就过了,我这里也没有记录
注意:写博客很费时间的,转载请注明原文链接,谢谢