1.序
2020/3/17日JDK14正式发版,但是现在大部分公司还是在使用jdk 8。所以我们今天继续聊聊jdk8。
2.jdk8 详解
2.1编程语言
2.1.1Lambda 表达式
Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
举个例子
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8");
}
}).start();
// Java 8
new Thread( () -> System.out.println("In Java8, Lambda expression") ).start();
用() -> {}代码块替代了整个匿名类
2.1.2方法引用
方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。
下面,我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
class Car {
//Supplier是jdk1.8的接口,这里和lamda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
静态方法引用:它的语法是Class::static_method,实例如下:
cars.forEach( Car::collide );
特定类的任意对象的方法引用:它的语法是Class::method实例如下:
cars.forEach( Car::repair );
特定对象的方法引用:它的语法是instance::method实例如下:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
2.1.3默认方法
Java 8 新增了接口的默认方法。
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个 default 关键字即可实现默认方法。
为什么要有这个特性?
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
语法
2.1.3.1.默认方法语法格式如下:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
2.1.3.2.多个默认方法
一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
public interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
public class Car implements Vehicle, FourWheeler {
default void print(){
System.out.println("我是一辆四轮汽车!");
}
}
第二种解决方案可以使用 super 来调用指定接口的默认方法:
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
2.1.3.3静态默认方法
Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
2.1.4可重复注解
注解并不是什么新鲜东西了,比如spring中存在大量注解简化我们的配置。但是在JDK8之前,我们是不能使用重复注解的,即某个位置相同注解只能出现一次。
比如我们想编写一个定时任务的注解,使用者可以配置在每天哪一小时触发,而且允许用户配置多个时间。传统做法是:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraditionalAnnoSchedule {
int[] hour() default {
0};
}
@TraditionalAnnoSchedule(hour = {
0, 8, 12})
public class Target {
public static void main(String[] args) {
TraditionalAnnoSchedule[] annotations = Target.class.getAnnotationsByType(TraditionalAnnoSchedule.class);
for (TraditionalAnnoSchedule each : annotations) {
System.out.println(Arrays.toString(each.hour()));
}
}
}
上面是比较传统的做法,下面我们使用JDK8的重复注解特性改造下上面的注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
Schedule[] value();
}
// JDK8新增的@Repeatable
@Repeatable(Schedules.class)
public @interface Schedule {
int hour() default 0;
}
@Schedule(hour = 0)
@Schedule(hour = 8)
@Schedule(hour = 12)
public class Target {
public static void main(String[] args) {
// 推荐的方式
Schedule[] annotations = Target.class.getAnnotationsByType(Schedule.class);
for (Schedule each : annotations) {
System.out.println(each.hour());
}
// 老的方式
Schedule[] schedules = Target.class.getAnnotation(Schedules.class).value();
for (Schedule each : schedules) {
System.out.println(each.hour());
}
}
}
这里有个使用@Repeatable( Schedules.class )的注解类Schedule,Schedules仅仅是Schedule注解的数组,对使用者而言,Target就拥有了两个Schedule注解,而不是1个Schedules注解。同时,反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型。
2.1.5 类型注解
类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(例如:UnsupportedOperationException; NumberFormatException;NullPointerException异常等都是runtime error),以提高代码质量。这就是类型注解的作用。
注意:使用Checker Framework可以找到类型注解出现的地方并检查。
例如下面的代码。
import checkers.nullness.quals.*;
public class TestDemo{
void sample() {
@NonNull Object my = new Object();
}
}
@Encrypted String data
List<@NonNull String> strings
MyGraph = (@Immutable Graph) tmpGraph;
2.1.6方法参数注解
2.2安全
- 默认启用客户端 TLS 1.2
- AccessController.doPrivileged 的新变体支持代码断言其权限的子集,而不会阻止完全遍历堆栈来检查其他权限
- 更强大的基于密码的加密算法
- JSSE 服务器端支持 SSL/TLS 服务器名称指示 (SNI) 扩展
- 支持 AEAD 算法:SunJCE 提供程序得到了增强,支持 AES/GCM/NoPadding 密码实现以及 GCM 算法参数。而且 SunJSSE 提供程序也得到了增强,支持基于 AEAD 模式的密码套件。请参阅 Oracle 提供程序文档,JEP 115。
- 密钥库增强,包括新的域密钥库类型 java.security.DomainLoadStoreParameter, 和为 keytool 实用程序新增的命令选项-importpassword
- SHA-224 消息摘要
- 增强了对 NSA Suite B 加密的支持
- 更好地支持高熵随机数生成
- 新增了 java.security.cert.PKIXRevocationChecker 类,用于配置 X.509 证书的撤销检查
- 适用于 Windows 的 64 位 PKCS11
- Kerberos 5 重放缓存中新增了 rcache 类型
- 支持 Kerberos 5 协议转换和受限委派
- 默认禁用 Kerberos 5 弱加密类型
- 适用于 GSS-API/Kerberos 5 机制的未绑定 SASL
- 针对多个主机名称的 SASL 服务
- JNI 桥接至 Mac OS X 上的原生 JGSS
- SunJSSE 提供程序中支持更强大的临时 DH 密钥
- JSSE 中支持服务器端加密套件首选项自定义
2.3部署
现在可以使用 URLPermission 允许沙盒小程序和 Java Web Start 应用连接回启动它们的服务器。不再授予 SocketPermission 。
在所有安全级别,主 JAR 文件的 JAR 文件清单中都需要 Permissions 属性。
2.4脚本
Rhino Javascript 引擎已被替换为 Nashorn JavaScript 引擎
2.5 Compact profiles
2.6 lang 包/util 包
- 并行数组排序
- 标准编码和解码 Base64
- 无符号算术支持
/** * 8.新增base64加解密API */
@Test
public void testBase64(){
final String text = "就是要测试加解密!!abjdkhdkuasu!!@@@@";
String encoded = Base64.getEncoder()
.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
System.out.println("加密后="+ encoded );
final String decoded = new String(
Base64.getDecoder().decode( encoded ),
StandardCharsets.UTF_8 );
System.out.println( "解密后="+decoded );
}
2.7 Java DB
JDK 8 包含 Java DB 10.10。
2.8 并发
2.8.1 concurrent包
2.8.2 ConcurrentHashMap
2.8.3 atomic 包
2.8.4 ForkJoinPool
1 ForkJoinPool 不是为了替代 ExecutorService,而是它的补充,在某些应用场景下性能比 ExecutorService 更好。
2 ForkJoinPool 主要用于实现“分而治之”的算法,特别是分治之后递归调用的函数,例如 quick sort 等。
3 ForkJoinPool 最适合的是计算密集型的任务,如果存在 I/O,线程间同步,sleep() 等会造成线程长时间阻塞的情况时,最好配合使用 ManagedBlocker。
2.8.5 StampedLock
StampedLock类,在JDK1.8时引入,是对读写锁ReentrantReadWriteLock的增强,该类提供了一些功能,优化了读锁、写锁的访问, 同时使读写锁之间可以互相转换,更细粒度控制并发。
该类的设计初衷是作为一个内部工具类,用于辅助开发其它线程安全组件,用得好,该类可以提升系统性能,用不好, 容易产生死锁和其它莫名其妙的问题。
先来看下,为什么有了ReentrantReadWriteLock,还要引入StampedLock?
ReentrantReadWriteLock使得多个读线程同时持有读锁(只要写锁未被占用),而写锁是独占的。
但是,读写锁如果使用不当,很容易产生“饥饿”问题:
比如在读线程非常多,写线程很少的情况下,很容易导致写线程“饥饿”,虽然使用“公平”策略可以一定程度上缓解这个问题,但是“公 平”策略是以牺牲系统吞吐量为代价的。
StampedLock的主要特点概括一下,有以下几点:
1.所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为0表示获取失败,其余都表示成功;
2. 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;
3. StampedLock是不可重入的;(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
4. StampedLock有三种访问模式:
①Reading(读模式):功能和ReentrantReadWriteLock的读锁类似 请登录后复制
②Writing(写模式):功能和ReentrantReadWriteLock的写锁类似
③Optimistic reading(乐观读模式):这是一种优化的读模式。
5. StampedLock支持读锁和写锁的相互转换 我们知道RRW中,当线程获取到写锁后,可以降级为读锁,但是读锁是不能直接升级为写锁的。 StampedLock提供了读锁和写锁相互转换的功能,使得该类支持更多的应用场景。
6. 无论写锁还是读锁,都不支持Conditon等待
我们知道,在ReentrantReadWriteLock中,当读锁被使用时,如果有线程尝试获取写锁,该写线程会阻塞。 但是,在Optimistic reading中,即使读线程获取到了读锁,写线程尝试获取写锁也不会阻塞,这相当于对读模式的优化,但是可 能会导致数据不一致的问题。所以,当使用Optimistic reading获取到读锁时,必须对获取结果进行校验。
2.9 Java Mission Control
2.10 集合
新的java.util.stream包中的类提供了一个 Stream API,支持对元素流进行函数式操作。Stream API 集成在 Collections API 中,可以对集合进行批量操作,例如顺序或并行的 map-reduce 转换。
针对存在键冲突的 HashMap 的性能改进
//stream
@Test
public void testStream(){
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("求和:"+nums
.stream()//转成Stream
.filter(team -> team!=null)//过滤
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳过前2个元素
.limit(4)//限制取前4个元素
.peek(System.out::println)//流式处理对象函数
.sum());//
}
//数组并行(parallel)操作
@Test
public void testParallel(){
long[] arrayOfLong = new long [ 20000 ];
//1.给数组随机赋值
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
//2.打印出前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
//3.数组排序
Arrays.parallelSort( arrayOfLong );
//4.打印排序后的前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
2.11 JavaFX
- 本版本中实施了新的 Modena 主题。有关更多信息,请参阅 xexperience.com.
- 新的 SwingNode 类允许开发人员将 Swing 内容嵌入到 JavaFX 应用中。请参阅SwingNode javadoc 和 将 Swing 内容嵌入 JavaFX 应用中。.
- 新的 UI 控件包括 DatePicker 和 TreeTableView 控件。
- javafx.print 程序包为 JavaFX Printing API 提供了公共类。有关更多信息,请参阅 javadoc
- 3D 图形特性现在包括 3D 形状、摄像头、灯光、子场景、材料、挑选和抗锯齿。JavaFX 3D 图形库中新增了 Shape3D (Box, Cylinder, MeshView和 Sphere子类 ), SubScene, Material, PickResult, LightBase (AmbientLight 和 PointLight 子类) , 和 SceneAntialiasing API 类。此版本中的Camera API 类也已更新。请参阅 javafx.scene.shape.Shape3D, javafx.scene.SubScene, javafx.scene.paint.Material, javafx.scene.input.PickResult和, javafx.scene.SceneAntialiasing, 类的相关 javadoc 以及 JavaFX 3D 图形入门 文档。
- WebView WebView 类包含新特性和改进。有关其他 HTML5 特性(包括 Web 套接字、Web 辅助进程和 Web 字体)的更多信息,请参阅
- 增强了文本支持,包括双向文本、复杂文本脚本(如泰语和印地语控件)以及文本节点中的多行多样式文本。
- 此版本添加了对 Hi-DPI 显示的支持。
- CSS Styleable* 类已成为公共 API。有关更多信息,请参阅 javafx.css javadoc。
- 新的 ScheduledService 类允许自动重新启动服务。
- JavaFX 现在可用于 ARM 平台。适用于 ARM 的 JDK 包含 JavaFX 的基础组件、图形组件和控制组件。
2.12 国际化
- Unicode 增强,包括对 Unicode 6.2.0 的支持
- 采用 Unicode CLDR 数据和 java.locale.providers 系统属性
- 新增日历和区域设置 API
- 支持将自定义资源包作为扩展进行安装
2.13 IO/NIO
- 全新的基于 Solaris 事件端口机制的面向 Solaris 的 SelectorProvider 实现。要使用它,请将系统属性java.nio.channels.spi.Selector 的值设置为 sun.nio.ch.EventPortSelectorProvider.
- 减小 <JDK_HOME>/jre/lib/charsets.jar 文件的大小
- 提高了 java.lang.String(byte[], *) 构造函数和 java.lang.String.getBytes() 方法的性能。
2.14 工具
2.14.1 jjs
Java 8的Nashorn JavaScript引擎。Nashorn是于Java 8中用于取代Rhino(Java 6,Java 7)的JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。与先前的Rhino引擎相比,它有二到十倍的性能提升。
jjs是个基于Nashorn引擎的命令行工具。你可以通过该工具快速地在Java上运行JavaScript代码,就像是一个REPL。
2.14.2 jdeps
可通过 jdeps 命令行工具来分析类文件。
2.14.3 jarsigner
The jarsigner工具提供了一个选项用于请求获取时间戳机构 (TSA) 的签名时间戳。
2.14.4 javac
- javac 命令的 -parameters 选项可用于存储正式参数名称,并启用反射 API 来检索正式参数名称。
- 命令现已正确实施了 Java 语言规范 (JLS) 第 15.21 节中的相等运算符的类型规则。
- javac工具现在支持检查 javadoc 注释的内容,从而避免在运行javadoc 时生成的文件中产生各种问题,例如无效的 HTML 或可访问性问题。可通过新的-Xdoclint 选项来启用此特性。有关更多详细信息,请参阅运行“javac-X”时的输出。此特性也可以在javac -X". This feature is also available in the javadoc工具中使用,并且默认启用。
- javac 工具现在支持根据需要生成原生标头。这样便无需在构建管道中单独运行 javah 工具。可以使用新的 -h 选项在 javac 中启用此特性,该选项用于指定写入头文件的目录。将为任何具有原生方法或者使用 java.lang.annotation.Native类型的新批注的类进行批注的常量字段生成头文件。
2.14.5 javadoc
- javadoc 工具支持新的 DocTree API,让您可以将 Javadoc 注释作为抽象语法树来进行遍历。
- javadoc 工具支持新的 Javadoc Access API,让您可以直接从 Java 应用中调用 Javadoc 工具,而无需执行新的进程。有关更多信息,请参阅 [javadoc 新特性] (https://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/whatsnew-8.html)页面。
- javadoc工具现在支持检查javadoc 注释的内容,从而避免在运行 javadoc 时生成的文件中产生各种问题,例如无效的 HTML 或可访问性问题。此特性默认为启用状态,可以通过新的-Xdoclint 选项加以控制。有关更多详细信息,请参阅运行 “javadoc -X” 时的输出。. javac 工具也支持此特性,但默认情况下并未启用它。
2.15 日期时间包
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
2.15.1本地化日期时间 API
LocalDate/LocalTime 和 LocalDateTime 类可以在处理时区不是必须的情况。代码如下:
package geym.conc.fastjsonTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class Test10 {
public static void main(String args[]){
Test10 java8tester = new Test10();
java8tester.testLocalDateTime();
}
public void testLocalDateTime(){
// 获取当前的日期时间
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("当前时间: " + currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: " + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();
System.out.println("月: " + month +", 日: " + day +", 秒: " + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2);
// 12 december 2014
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3: " + date3);
// 22 小时 15 分钟
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4);
// 解析字符串
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
2.15.2使用时区的日期时间API
如果我们需要考虑到时区,就可以使用时区的日期时间API:
package geym.conc.fastjsonTest;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Test10 {
public static void main(String args[]){
Test10 java8tester = new Test10();
java8tester.testZonedDateTime();
}
public void testZonedDateTime(){
// 获取当前时间日期
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
System.out.println("date1: " + date1);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("当期时区: " + currentZone);
}
}
2.16 网络
已添加 java.net.URLPermission 类。
在 java.net.HttpURLConnection类中,如果安装了安全管理器,那么请求打开连接的调用需要权限。
2.17 HotSpot
新增的硬件内部函数以便使用高级加密标准 (AES)。 UseAES 和 UseAESIntrinsics 标志用于为 硬件启用基于硬件的 AES 内部函数。硬件必须是 2010 年或更新的 Westmere 硬件。例如,要启用硬件 AES,请使用以下标志:
-XX:+UseAES -XX:+UseAESIntrinsics
要禁用硬件 AES,请使用以下标志: -XX:-UseAES -XX:-UseAESIntrinsics
2.17.1 默认方法支持
2.17.2 永久代删除
JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
//-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
//-XX:MaxMetaspaceSize最大空间,默认是没有限制
//-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
//-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
2.18 Java XML
2.19 JDBC
- 删除了 JDBC-ODBC Bridge。
- JDBC 4.2 引入了新特性。
2.20 Pack200
- Pack200 支持 JSR 292 引入的常量池条目和新字节码
- JDK8 支持 JSR-292、JSR-308 和 JSR-335 指定的类文件更改
维基百科
Pack200是Sun为更快地在网络上传输JAR文件而在JSR 200中提出的一种HTTP压缩方式。Pack200也可以指代从Sun JDK 1.50版就开始提供的Pack200压缩工具以及Pack200压缩文件。
在HTTP压缩方式中,Pack200可以和GZIP压缩格式协同使用(这种情况下被称为“pack200-gzip”)以提供比只使用GZIP压缩格式更高的压缩率。Pack200专为压缩JAR文件(尤其是其中的字节码部分)而优化。此技术也应用于Java Web Start的Java程序部署中。
3.总结
4.个人推广
博客地址
https://blog.csdn.net/weixin_41563161