JavaSE-新特性(JDK5-JDK12)持续维护

 

 

JDK5新特性

自动装箱与拆箱
枚举
静态导入
可变参数(Varargs
内省(introspector
泛型(Generic
For-Each
循环

JDK6新特性

Desktop类和SystemTray
使用JAXB2来实现对象与XML之间的映射
理解STAX
使用Compiler API
轻量级 Http Server API
插入式注解处理 API
使用Console开发控制台程序
对脚本语言的支持
Common Annotations
Java GUI
界面的显示
嵌入式数据库Derby
Web
服务元数据
Jtable
的排序和过滤
更简单更强大的JAX-WS

JDK7新特性

switch中可以使用字符串了
泛型实例化类型自动推断
自定义自动关闭类
新增一些读取环境信息的工具方法
Boolean
类型反转,空指针安全,参与位运算
两个char之间的equals
安全的加减乘除|sd
对Java集合( Collections)的增强支持
数值可加下划线
支持二进制数字
在try/catch异常扑捉中,一个catch可以写多个异常类型用|隔开
可以不必要写finally语句来关闭资源,只要你在try()的括号内部定义要使用的资源

JDK8新特性

接口的默认方法
Lambda表达式
函数式接口
方法与构造函数引用
扩展了集合类
新的Date API
Annotation多重注解
streams(流)
parallel streams(并行流)
Map数据结构改进

JDK9新特性

Jigsaw模块化项目
简化进程API
轻量级JSON API
钱和货币的API
改善锁竞争机制
代码分段缓存
智能java编译
http2.0
客户端
kulla
计划

JDK10新特性
局部变量的类型推断
GC改进和内存管理
线程本地握手
备用内存设备上的堆分配
其他Unicode语言 - 标记扩展
基于Java的实验性JIT编译器
开源根证书
根证书颁发认证(CA)
将JDK生态整合单个存储库
删除工具javah

JDK11新特性
1
、基于嵌套的访问控制
2
、动态类文件常量
3
、改进 Aarch64 内联函数
4
、Epsilon:No-Op 垃圾收集器
5
、删除 Java EE 和 CORBA 模块
6
、HTTP 客户端(标准)
7
、Lambda 参数的本地变量语法
8
、Curve25519 和 Curve448 密钥协议
9
、Unicode 10
10
、运行记录仪
11
、ChaCha20 和 Poly1305 密码算法
12
、启动单文件源代码程序
13
、低开销堆分析
14
、传输层安全性(TLS)1.3
15
、ZGC:可扩展的低延迟垃圾收集器
16
、弃用 Nashorn JavaScript 引擎
17
、弃用 Pack200 工具和 API

 

JEP(JDK Enhancement Proposals,JDK 增强提案)

JDK1.5新特性

1:自动装箱与拆箱:

自动装箱:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。

自动拆箱:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。

自动装箱,只需将该值赋给一个类型包装器引用,java会自动创建一个对象。

自动拆箱,只需将该对象值赋给一个基本类型即可。

java——类的包装器

类型包装器有:Double,Float,Long,Integer,Short,Character和Boolean

2:枚举

把集合里的对象元素一个一个提取出来。枚举类型使代码更具可读性,理解清晰,易于维护。枚举类型是强类型的,从而保证了系统安全性。而以类的静态字段实现的类似替代模型,不具有枚举的简单性和类型安全性。

简单用法:JavaEnum简单的用法一般用于代表一组常用常量,可用来代表一类相同类型的常量值。

复杂用法:Java为枚举类型提供了一些内置的方法,同事枚举常量还可以有自己的方法。可以很方便的遍历枚举对象。

3:静态导入

通过使用 import static,就可以不用指定 Constants 类名而直接使用静态成员,包括静态方法。

import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out。

4:可变参数(Varargs)

可变参数的简单语法格式为:

methodName([argumentList], dataType... argumentName);

5:内省(Introspector)

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那可以通过getName,setName来得到其值或者设置新 的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter /setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。

一 般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后就可以通过反射机制来 调用这些方法。

6:泛型(Generic)

C++ 通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功能。一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候也不得不对他们进行强制得类型转换。猛虎引入了泛型,它允许指定集合里元素的类型,这样你可以得到强类型在编译时刻进行类型检查的好处。

7:For-Each循环

For-Each循环得加入简化了集合的遍历。假设要遍历一个集合对其中的元素进行一些处理。

 

JDK 1.6新特性

有关JDK1.6的新特性reamerit的博客文章已经说的很详细了。

1:Desktop类和SystemTray类

在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray。

前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序.

2:使用JAXB2来实现对象与XML之间的映射

JAXB是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。

我 们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。

实 际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。除了JAXB之外,还可以通过XMLBeans和Castor等来实现同样的功能。

3:理解StAX

StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一种处理XML文档的API。

StAX 的来历 :在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).

由 于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX,所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版本). JDK6里面JAXP的版本就是1.4. 。

StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX通过提供一种基于事件迭代器(Iterator)的API让 程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件,然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;

SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM 采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。

4.使用Compiler API

现在可以用JDK6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。

这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要产生另一个进程去 做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。

5:轻量级Http Server API

JDK6 提供了一个简单的Http Server API,据此可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在 这里,把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.

6:插入式注解处理API(Pluggable Annotation Processing API)

插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)

实 际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 可以在javax.lang.model包下面可以看到这些类. 所以可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.

JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列.

JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试方法。

7:用Console开发控制台程序

JDK6 中提供了java.io.Console 类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.

8:对脚本语言的支持如: ruby, groovy, javascript

 

9:Common Annotations

Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.

随 着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.

下面列举出Common Annotations 1.0里面的10个Annotations Common Annotations

Annotation Retention Target Description

Generated Source ANNOTATIONTYPE, CONSTRUCTOR, FIELD, LOCALVARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用于标注生成的源代码

Resource Runtime TYPE, METHOD, FIELD 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式

Resources Runtime TYPE 同时标注多个外部依赖,容器会把所有这些外部依赖注入

PostConstruct Runtime METHOD 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct

PreDestroy Runtime METHOD 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的

PermitAll Runtime TYPE, METHOD 允许所有角色执行被标注的类或方法

DenyAll Runtime TYPE, METHOD 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行

DeclareRoles Runtime TYPE 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色

注意:

1.RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上

2.标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来

4.处理以上Annotations的工作是由Java EE容器来做, Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做

改动的地方最大的就是java GUI界面的显示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系统的Windows Aero视窗效果,而JDK1.5不支持!!!

你要在vista环境下编程的话最好装jdk6.0,否则它总是换到windows basic视窗效果.

 

 

JDK 1.7 新特性

  1. 二进制字面量
  2. 数字字面量可以出现下划线
  3. switch 语句可以用字符串
  4. 泛型简化
  5. 异常的多个catch合并
  6. try-with-resources 语句

 

二进制字面量

  1. JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。使用二进制字面量的好处是,可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B
  2. 举例:
    • int x = ob110110

 

数字字面量可以出现下划线

  1. 为了增强对数值的阅读性,如经常把数据用逗号分隔一样。JDK7提供了_对数据分隔。
  2. 举例:
    • int x = 100_1000;
  3. 注意事项:
    • 不能出现在进制标识和数值之间
    • 不能出现在数值开头和结尾
    • 不能出现在小数点旁边

 

int a = 0b100_100;

int b = 0b_100_100;

int c = 0b100_100_;

float d = 12.34_56f;

float e = 12._34_56f;

 

switch 语句可以用字符串(底层是使用hashCode方法,然后再调用equals方法):String变量不能为空,case子句中的字符串也不能为null。字符串为null会抛出空指针异常

 

泛型简化:也就是钻石操作符<>出现,泛型推断

                   ArrayList<String> array = new ArrayList<>();

异常的多个catch合并:

Try{

}catch(Exception1 e | Exception2 e){

}

 

try-with-resources 语句:自动释放(JDK1.9进一步优化)

  1. 格式:
    • try(必须是java.lang.AutoCloseable的子类对象){…}
    • 好处:
      • 资源自动释放,不需要close()了
      • 把需要关闭资源的部分都定义在这里就ok了
      • 主要是流体系的对象是这个接口的子类(看JDK7的API)

                   // 改进版的try和catch代码

                   try (FileReader fr = new FileReader("a.txt");

                                     FileWriter fw = new FileWriter("b.txt");) {

                            int ch = 0;

                            while ((ch = fr.read()) != -1) {

                                     fw.write(ch);

                            }

                   } catch (IOException e) {

                            e.printStackTrace();

                   }

 

 

 

1:switch中可以使用字串

String s = "test";

switch (s) {

 case "test" :

   System.out.println("test");

case "test1" :

   System.out.println("test1");

break ;

default :

  System.out.println("break");

break ;

}

2:"<>"这个玩意儿的运用

ListtempList = new ArrayList<>(); 即泛型实例化类型自动推断。

public class JDK7GenericTest {

 

   public static void main(String[] args) {

      // Pre-JDK 7

      List<String> lst1 = new ArrayList<String>();

 

      // JDK 7 supports limited type inference for generic instance creation

 

      List<String> lst2 = new ArrayList<>();

      lst1.add("Mon");

      lst1.add("Tue");

      lst2.add("Wed");

      lst2.add("Thu");

      for (String item: lst1) {

         System.out.println(item);

      }

 

      for (String item: lst2) {

         System.out.println(item);

      }

   }

}

3:自定义自动关闭类

以下是jdk7 api中的接口,(不过注释太长,删掉了close()方法的一部分注释)

/**

 * A resource that must be closed when it is no longer needed.

 *

 * @author Josh Bloch

 * @since 1.7

 */

public interface AutoCloseable {

    /**

     * Closes this resource, relinquishing any underlying resources.

     * This method is invoked automatically on objects managed by the

     * {@code try}-with-resources statement.

     *

     */

    void close() throws Exception;

}

只要实现该接口,在该类对象销毁时自动调用close方法,你可以在close方法关闭你想关闭的资源,例子如下

class TryClose implements AutoCloseable {

 

 @Override

 public void close() throw Exception {

  System.out.println(" Custom close method …

                                         close resources ");

 }

}

//请看jdk自带类BufferedReader如何实现close方法(当然还有很多类似类型的类)

 

  public void close() throws IOException {

        synchronized (lock) {

            if (in == null)

                return;

            in.close();

            in = null;

            cb = null;

        }

    }

4:新增一些取环境信息的工具方法

File System.getJavaIoTempDir() // IO临时文件夹

 

File System.getJavaHomeDir() // JRE的安装目录

 

File System.getUserHomeDir() // 当前用户目录

 

File System.getUserDir() // 启动java进程时所在的目录

 

.......

5:Boolean类型反转,空指针安全,参与位运算

Boolean Booleans.negate(Boolean booleanObj)

 

True => False , False => True, Null => Null

 

boolean Booleans.and(boolean[] array)

 

boolean Booleans.or(boolean[] array)

 

boolean Booleans.xor(boolean[] array)

 

boolean Booleans.and(Boolean[] array)

 

boolean Booleans.or(Boolean[] array)

 

boolean Booleans.xor(Boolean[] array)

6: 两个char间的equals

boolean Character.equalsIgnoreCase(char ch1, char ch2)

7:安全的加减乘除

int Math.safeToInt(long value)

 

int Math.safeNegate(int value)

 

long Math.safeSubtract(long value1, int value2)

 

long Math.safeSubtract(long value1, long value2)

 

int Math.safeMultiply(int value1, int value2)

 

long Math.safeMultiply(long value1, int value2)

 

long Math.safeMultiply(long value1, long value2)

 

long Math.safeNegate(long value)

 

int Math.safeAdd(int value1, int value2)

 

long Math.safeAdd(long value1, int value2)

 

long Math.safeAdd(long value1, long value2)

 

int Math.safeSubtract(int value1, int value2)

8:对Java集合(Collections)的增强支持

在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下:

以List、Set、Map集合容器为例:

    //创建List接口对象

    List<String> list=new ArrayList<String>();

    list.add("item"); //用add()方法获取对象

    String Item=list.get(0); //用get()方法获取对象

 

    //创建Set接口对象

    Set<String> set=new HashSet<String>();

    set.add("item"); //用add()方法添加对象

 

    //创建Map接口对象

    Map<String,Integer> map=new HashMap<String,Integer>();

    map.put("key",1); //用put()方法添加对象

    int value=map.get("key");

在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下:

List<String> list=["item"]; //向List集合中添加元素

      String item=list[0]; //从List集合中获取元素

 

      Set<String> set={"item"}; //向Set集合对象中添加元素

      Map<String,Integer> map={"key":1}; //向Map集合中添加对象

      int value=map["key"]; //从Map集合中获取对象

9:数值可加下划线

例如:int one_million = 1_000_000;

10:支持二进制文字

例如:int binary = 0b1001_1001;

11:简化了可变参数方法的调用

当程序员试图使用一个不可具体化的可变参数并调用一个varargs (可变)方法时,编辑器会生成一个“非安全操作”的警告。

12:在try catch异常扑捉中,一个catch可以写多个异常类型,用"|"隔开

jdk7之前:

try {

   ......

} catch(ClassNotFoundException ex) {

   ex.printStackTrace();

} catch(SQLException ex) {

   ex.printStackTrace();

}

jdk7例子如下

try {

   ......

} catch(ClassNotFoundException|SQLException ex) {

   ex.printStackTrace();

}

13:jdk7之前,你必须用try{}finally{}在try内使用资源,在finally中关闭资源,不管try中的代码是否正常退出或者异常退出。jdk7之后,你可以不必要写finally语句来关闭资源,只要你在try()的括号内部定义要使用的资源

jdk7之前:

import java.io.*;

// Copy from one file to another file character by character.

// Pre-JDK 7 requires you to close the resources using a finally block.

public class FileCopyPreJDK7 {

   public static void main(String[] args) {

      BufferedReader in = null;

      BufferedWriter out = null;

      try {

         in  = new BufferedReader(new FileReader("in.txt"));

         out = new BufferedWriter(new FileWriter("out.txt"));

         int charRead;

         while ((charRead = in.read()) != -1) {

            System.out.printf("%c ", (char)charRead);

            out.write(charRead);

         }

      } catch (IOException ex) {

         ex.printStackTrace();

      } finally {            // always close the streams

         try {

            if (in != null) in.close();

            if (out != null) out.close();

         } catch (IOException ex) {

            ex.printStackTrace();

         }

      }

 

      try {

         in.read();   // Trigger IOException: Stream closed

      } catch (IOException ex) {

         ex.printStackTrace();

      }

   }

}

jdk7之后

import java.io.*;

// Copy from one file to another file character by character.

// JDK 7 has a try-with-resources statement, which ensures that

// each resource opened in try() is closed at the end of the statement.

public class FileCopyJDK7 {

   public static void main(String[] args) {

      try (BufferedReader in  = new BufferedReader(new FileReader("in.txt"));

           BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"))) {

         int charRead;

         while ((charRead = in.read()) != -1) {

            System.out.printf("%c ", (char)charRead);

            out.write(charRead);

         }

      } catch (IOException ex) {

         ex.printStackTrace();

      }

   }

}

 

 

 

 

 

 

 

 

JDK8特性

Java8新特性

1.Lambda 表达式

2.函数式接口

3.方法引用与构造器引用

4.Stream API

5.接口中的默认方法与静态方法

6.新时间日期API

速度更快、代码更少(增加了lambda表达式)、强大的Stream API。便于并行、最大haul减少空指针异常(Optional)

最核心的是Lambda和Stream API

 

1:接口支持默认方法和静态方法

Java 8允许给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:

代码如下:

interface Formula {

    double calculate(int a);

    default double sqrt(int a) {

        return Math.sqrt(a);

    }

}

Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。

代码如下:

Formula formula = new Formula() {

    @Override

    public double calculate(int a) {

        return sqrt(a * 100);

    }

};

formula.calculate(100);     // 100.0

formula.sqrt(16);           // 4.0

文中的formula被实现为一个匿名类的实例,该代码非常容易理解,6行代码实现了计算 sqrt(a * 100)。在下一节中,将会看到实现单方法接口的更简单的做法。

译者注: 在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现,在C++中支持多继承,允许一个子类同时具有多个父类的接口与功能,在其他 语言中,让一个类同时具有其他的可复用代码的方法叫做mixin。新的Java 8 的这个特新在编译器实现的角度上来说更加接近Scala的trait。 在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,和Java 8的这个在语义上有差别。

 

 

接口中的默认方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。

 

//知识点1:接口中定义的静态方法,只能由接口自身调用

//知识点2:接口中定义的默认方法,可以被实现类的对象调用

//知识点3:如果父类和接口中定义了同名同参数的方法,对于子类来说,默认执行的是父类中的方法。---类优先原则

//知识点4:如果实现类实现了多个接口,而多个接口中定义了同名同参数的默认方法,则会出现接口冲突。

//要想解决冲突,实现类必须重写这个同名同参数的方法。调用时,执行的是自己重写的方法

 

public class MyClass {     

         public String getName(){

                   return "嘿嘿嘿";

         }

}

public interface MyFun {

         default String getName(){

                   return "哈哈哈";

         }

}

public interface MyInterface {

         default String getName(){

                   return "呵呵呵";

         }

         public static void show(){

                   System.out.println("接口中的静态方法");

         }

}

如果类和接口拥有同一方法的话,默认是类。如果两个接口,必须重写方法,否则编译爆错

public class SubClass /*extends MyClass*/ implements MyFun, MyInterface{

         @Override

         public String getName() {

                   return MyInterface.super.getName();

         }

}

 

 

接口中的默认方法

接口默认方法的”类优先”原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时

 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

 

接口默认方法的”类优先”原则

 

接口中的静态方法

Java8 中,接口中允许添加静态方法。

 

 

 

2:Lambda 表达式

首先看看在老版本的Java中是如何排列字符串的:

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {

    @Override

    public int compare(String a, String b) {

        return b.compareTo(a);

    }

});

只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

Collections.sort(names, (String a, String b) -> {

    return b.compareTo(a);

});

看到了吧,代码变得更段且更具有可读性,但是实际上还可以写得更短:

Collections.sort(names, (String a, String b) -> b.compareTo(a));

对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:

Collections.sort(names, (a, b) -> b.compareTo(a));

ava编译器可以自动推导出参数类型,所以你可以不用再写一次类型。接下来看看lambda表达式还能作出什么更方便的东西来

3:函数式接口

Lambda 表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的 接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。

示例如下:

@FunctionalInterface

interface Converter<F, T> {

    T convert(F from);

}

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);

Integer converted = converter.convert("123");

System.out.println(converted);    // 123

需要注意如果@FunctionalInterface如果没有指定,上面的代码也是对的。

译者注 将lambda表达式映射到一个单方法的接口上,这种做法在Java 8之前就有别的语言实现,比如Rhino JavaScript解释器,如果一个函数参数接收一个单方法的接口而你传递的是一个function,Rhino 解释器会自动做一个单接口的实例到function的适配器,典型的应用场景有 org.w3c.dom.events.EventTarget 的addEventListener 第二个参数 EventListener。

4:方法与构造器引用

前一节中的代码还可以通过静态方法引用来表示:

Converter<String, Integer> converter = Integer::valueOf;

Integer converted = converter.convert("123");

System.out.println(converted);   // 123

Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,也可以引用一个对象的方法:

converter = something::startsWith;

String converted = converter.convert("Java");

System.out.println(converted);    // "J"

接下来看看构造函数是如何使用::关键字来引用的,首先定义一个包含多个构造函数的简单类:

class Person {

    String firstName;

    String lastName;

    Person() {}

 

    Person(String firstName, String lastName) {

        this.firstName = firstName;

        this.lastName = lastName;

    }

}

接下来指定一个用来创建Person对象的对象工厂接口:

interface PersonFactory<P extends Person> {

    P create(String firstName, String lastName);

}

这里使用构造函数引用来将他们关联起来,而不是实现一个完整的工厂:

PersonFactory<Person> personFactory = Person::new;

Person person = personFactory.create("Peter", "Parker");

只需要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。

 

方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!

(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)

方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。

如下三种主要使用情况:

 对象::实例方法

 类::静态方法

 类::实例方法

方法引用

注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引

用方法的第二个参数(或无参数)时:ClassName::methodName

 

 

构造器引用

格式: ClassName::new

与函数式接口相结合,自动与函数式接口中方法兼容

可以把构造器引用赋值给定义的方法,与构造器参数

列表要与接口中抽象方法的参数列表一致!

 

数组引用

格式: type[] :: new

 

 

 

 

5:Lambda 作用域

在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。

6:访问局部变量

可以直接在lambda表达式中访问外层的局部变量:

final int num = 1;

Converter<Integer, String> stringConverter =

        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:

int num = 1;

Converter<Integer, String> stringConverter =

        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

不过这里的num必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:

int num = 1;

Converter<Integer, String> stringConverter =

        (from) -> String.valueOf(from + num);

num = 3;

在lambda表达式中试图修改num同样是不允许的。

7:访问对象字段与静态变量

和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

class Lambda4 {

    static int outerStaticNum;

    int outerNum;

    void testScopes() {

        Converter<Integer, String> stringConverter1 = (from) -> {

            outerNum = 23;

            return String.valueOf(from);

        };

 

        Converter<Integer, String> stringConverter2 = (from) -> {

            outerStaticNum = 72;

            return String.valueOf(from);

        };

    }

}

8:访问接口的默认方法

还记得第一节中的formula例子么,接口Formula定义了一个默认方法sqrt可以直接被formula的实例包括匿名对象访问到,但是在lambda表达式中这个是不行的。 Lambda表达式中是无法访问到默认方法的,以下代码将无法编译:

Formula formula = (a) -> sqrt( a * 100);

Built-in Functional Interfaces

JDK 1.8 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。 Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。 Predicate接口

Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true

predicate.negate().test("foo");     // false

 

Predicate<Boolean> nonNull = Objects::nonNull;

Predicate<Boolean> isNull = Objects::isNull;

 

Predicate<String> isEmpty = String::isEmpty;

Predicate<String> isNotEmpty = isEmpty.negate();

Function 接口

Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

Function<String, Integer> toInteger = Integer::valueOf;

Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Supplier 接口Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

Supplier<Person> personSupplier = Person::new;

personSupplier.get();   // new Person

Consumer 接口

Consumer 接口表示执行在单个参数上的操作。

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);

greeter.accept(new Person("Luke", "Skywalker"));

Comparator 接口

Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");

Person p2 = new Person("Alice", "Wonderland");

 

comparator.compare(p1, p2);             // > 0

comparator.reversed().compare(p1, p2);  // < 0

Optional 接口

Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:

Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true

optional.get();                 // "bam"

optional.orElse("fallback");    // "bam"

 

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"

Stream 接口

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。 Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

List<String> stringCollection = new ArrayList<>();

stringCollection.add("ddd2");

stringCollection.add("aaa2");

stringCollection.add("bbb1");

stringCollection.add("aaa1");

stringCollection.add("bbb3");

stringCollection.add("ccc");

stringCollection.add("bbb2");

stringCollection.add("ddd1");

Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:

Filter 过滤

过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以可以在过滤后的结果来应用其他Stream操作 (比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以不能在forEach之后来执行 其他Stream操作。

stringCollection

    .stream()

    .filter((s) -> s.startsWith("a"))

    .forEach(System.out::println);

// "aaa2", "aaa1"

Sort 排序

排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。

stringCollection

    .stream()

    .sorted()

    .filter((s) -> s.startsWith("a"))

    .forEach(System.out::println);

// "aaa1", "aaa2"

需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:

System.out.println(stringCollection);

// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Map 映射

中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

stringCollection

    .stream()

    .map(String::toUpperCase)

    .sorted((a, b) -> b.compareTo(a))

    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Match 匹配

Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

boolean anyStartsWithA =

    stringCollection

        .stream()

        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

 

boolean allStartsWithA =

    stringCollection

        .stream()

        .allMatch((s) -> s.startsWith("a"));

 

System.out.println(allStartsWithA);      // false

 

boolean noneStartsWithZ =

    stringCollection

        .stream()

        .noneMatch((s) -> s.startsWith("z"));

 

System.out.println(noneStartsWithZ);      // true

Count 计数

计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

long startsWithB =

    stringCollection

        .stream()

        .filter((s) -> s.startsWith("b"))

        .count();

System.out.println(startsWithB);    // 3

Reduce 规约

这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

Optional<String> reduced =

    stringCollection

        .stream()

        .sorted()

        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);

// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

并行Streams

前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

下面的例子展示了是如何通过并行Stream来提升性能:

首先创建一个没有重复元素的大表:

int max = 1000000;

List<String> values = new ArrayList<>(max);

for (int i = 0; i < max; i++) {

    UUID uuid = UUID.randomUUID();

    values.add(uuid.toString());

}

然后计算一下排序这个Stream要耗时多久,

串行排序:

long t0 = System.nanoTime();

long count = values.stream().sorted().count();

System.out.println(count);

 

long t1 = System.nanoTime();

 

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("sequential sort took: %d ms", millis));

// 串行耗时: 899 ms

并行排序:

long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();

System.out.println(count);

 

long t1 = System.nanoTime();

 

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("parallel sort took: %d ms", millis));

 

// 并行排序耗时: 472 ms

上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。

Map

前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

Map<Integer, String> map = new HashMap<>();

for (int i = 0; i < 10; i++) {

    map.putIfAbsent(i, "val" + i);

}

map.forEach((id, val) -> System.out.println(val));

以上代码很容易理解, putIfAbsent 不需要做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。

下面的例子展示了map上的其他有用的函数:

map.computeIfPresent(3, (num, val) -> val + num);

map.get(3);             // val33

map.computeIfPresent(9, (num, val) -> null);

map.containsKey(9);     // false

 

map.computeIfAbsent(23, num -> "val" + num);

map.containsKey(23);    // true

 

map.computeIfAbsent(3, num -> "bam");

map.get(3);             // val33

接下来展示如何在Map里删除一个键值全都匹配的项:

map.remove(3, "val3");

map.get(3);             // val33

map.remove(3, "val33");

map.get(3);             // null

另外一个有用的方法:

map.getOrDefault(42, "not found");  // not found

对Map的元素做合并也变得很容易了:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9concat

Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。

9:Date API

 

日期时间:

JDK1.8以前:

Calendar:实现日期和时间之间的转换,它的属性可变,线程不安全

DataFormat:格式化和分析日期字符串

Data:承载日期和时间信息,属性可变,线程不安全。

 

JDK1.8新增java.time包:包含了处理日期、时间、日期/时间、时区、时刻(Instnts)、时钟(Clock)等的操作

 

常用:

System.currentTimeMillis()

 

 

 

 

Java 8 在包java.time下包含了一组全新的时间日期API。新的日期API和开源的Joda-Time库差不多,但又不完全一样,下面的例子展示了这组新API里最重要的一些部分: Clock 时钟

Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。

Clock clock = Clock.systemDefaultZone();

long millis = clock.millis();

Instant instant = clock.instant();

Date legacyDate = Date.from(instant);   // legacy java.util.Date

Timezones 时区

在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。

System.out.println(ZoneId.getAvailableZoneIds());

// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");

ZoneId zone2 = ZoneId.of("Brazil/East");

System.out.println(zone1.getRules());

System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]

// ZoneRules[currentStandardOffset=-03:00]

LocalTime 本地时间

LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:

LocalTime now1 = LocalTime.now(zone1);

LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

 

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);

long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

 

System.out.println(hoursBetween);       // -3

System.out.println(minutesBetween);     // -239

LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串。

LocalTime late = LocalTime.of(23, 59, 59);

System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =

    DateTimeFormatter

        .ofLocalizedTime(FormatStyle.SHORT)

        .withLocale(Locale.GERMAN);

 

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);

System.out.println(leetTime);   // 13:37

LocalDate 本地日期

LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。

LocalDate today = LocalDate.now();

LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);

LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);

DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();

 

System.out.println(dayOfWeek);    // FRIDAY

从字符串解析一个LocalDate类型和解析LocalTime一样简单:

DateTimeFormatter germanFormatter =

    DateTimeFormatter

        .ofLocalizedDate(FormatStyle.MEDIUM)

        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);

System.out.println(xmas);   // 2014-12-24

LocalDateTime 本地日期时间

LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法。

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();

System.out.println(dayOfWeek);      // WEDNESDAY

 

Month month = sylvester.getMonth();

System.out.println(month);          // DECEMBER

 

long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);

System.out.println(minuteOfDay);    // 1439

只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的java.util.Date。

Instant instant = sylvester

        .atZone(ZoneId.systemDefault())

        .toInstant();

Date legacyDate = Date.from(instant);

System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,也可以自己定义格式:

DateTimeFormatter formatter =

    DateTimeFormatter

        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);

String string = formatter.format(parsed);

System.out.println(string);     // Nov 03, 2014 - 07:13

和java.text.NumberFormat不一样的是新版的DateTimeFormatter是不可变的,所以它是线程安全的。关于时间日期格式的详细信息:http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html

 

 

新时间日期API

使用LocalDate、LocalTime、LocalDateTime

 LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

 

背景:

因为原来的日期API不是很好用。

最开始的Date JDK1.0、如构造器中,传入的时间要通过计算后才准确,

Calander JDK1.1 每周第一天默认星期天

TimeZone:使用麻烦

。。以上的都线程不安全

SimpleDateFormat类,居然是在java.text下

新的API在java.time下

 

传统API的多线程不安全问题:

//     SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//
//    Callable<Date> task1 = new Callable<Date>() {
//       @Override
//       public Date call() throws Exception {
//          return sdf.parse("20180115");
//       }
//    };
//    //
使用多线程去访问。结果因为SimpleDateFormat线程不安全,报异常
//    ExecutorService pool = Executors.newFixedThreadPool(10);
//
//    List<Future<Date>> results = new ArrayList<>();
//
//    for (int i = 0; i < 10; i++) {
//       results.add(pool.submit(task1));
//    }
//
//    for (Future<Date> future : results) {
//       System.out.println(future.get());
//    }
//    pool.shutdown();

将发生异常。

传统解决方案:

public class DateFormatThreadLocal {
   
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
       
protected DateFormat initialValue(){
           
return new SimpleDateFormat("yyyyMMdd");
       
}
    }
;
    public static final
Date convert(String source) throws ParseException {
       
return df.get().parse(source);
   
}
}

测试:成功通过

Callable<Date> task = new Callable<Date>() {
  
@Override
  
public Date call() throws Exception {
     
return DateFormatThreadLocal.convert("20180115");
  
}
}
;

ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for
(int i = 0; i < 10; i++) {
   results.add(pool.submit(task))
;
}
for (Future<Date> future : results) {
   System.
out.println(future.get());
}
pool.shutdown()
;

JDK8的方式:

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
//DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

Callable<LocalDate> task2 = new Callable<LocalDate>() {
   
@Override
   
public LocalDate call() throws Exception {
        LocalDate ld = LocalDate.parse(
"20180115", dtf);
        return
ld;
   
}
}
;

ExecutorService pool2 = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results2 = new ArrayList<>();
for
(int i = 0; i < 10; i++) {
    results2.add(pool2.submit(task2))
;
}
for (Future<LocalDate> future : results2) {
    System.
out.println(future.get());
}
pool2.shutdown()
;

 

 

 

Instant 时间戳:用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算

 

Duration 和Period

Duration:用于计算两个“时间”间隔

Period:用于计算两个“日期”间隔

 

日期的操纵

 TemporalAdjuster : 时间校正器。有时可能需要获取例如:将日期调整到“下个周日”等操作。

 TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。

例如获取下个周日:

 

 

解析与格式化

java.time.format.DateTimeFormatter 类:该类提供了三种

格式化方法:

 预定义的标准格式

 语言环境相关的格式

 自定义的格式

 

时区的处理

Java8 中加入了对时区的支持,带时区的时间为分别为:

ZonedDate、ZonedTime、ZonedDateTime

其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式

例如:Asia/Shanghai 等

ZoneId:该类中包含了所有的时区信息

getAvailableZoneIds() : 可以获取所有时区时区信息

of(id) : 用指定的时区信息获取ZoneId 对象

 

 

与传统日期处理的转换

 

    //1. LocalDateLocalTimeLocalDateTime:三者使用方式一样,以一个为例
   
@Test
   
public void test1(){
       
// 获取当前系统时间
       
LocalDateTime ldt = LocalDateTime.now();
       
PrintUtil.print(ldt);

       
LocalDateTime ld2 = LocalDateTime.of(2018, 01, 15, 01, 15, 10);
       
PrintUtil.print(ld2);
       
// 增加20
       
LocalDateTime ldt3 = ld2.plusYears(20);
       
PrintUtil.print(ldt3);
       
// 2个月
       
LocalDateTime ldt4 = ld2.minusMonths(2);
       
PrintUtil.print(ldt4);

       
PrintUtil.print(ldt.getYear());
       
PrintUtil.print(ldt.getMonthValue());
       
PrintUtil.print(ldt.getDayOfMonth());
       
PrintUtil.print(ldt.getHour());
       
PrintUtil.print(ldt.getMinute());
       
PrintUtil.print(ldt.getSecond());
   
}
   
// JDK1.8
    //2. Instant :
时间戳。 (使用 Unix 元年  197011 00:00:00 所经历的毫秒值)
   
@Test
   
public void test2(){
        Instant ins = Instant.now()
//默认使用 UTC 时区(不是系统时间)
       
PrintUtil.print(ins);
       
PrintUtil.print(ins.toEpochMilli());
       
// 偏移8小时时差,就是中国的时间
       
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
       
PrintUtil.print(odt);
       
PrintUtil.print(ins.getNano());
       
// 加上5s
       
Instant ins2 = Instant.ofEpochSecond(5);
       
PrintUtil.print(ins2);
   
}
   
//3.
    //Duration :
用于计算两个时间间隔
   
//Period : 用于计算两个日期间隔
   
@Test
   
public void test3(){
        Instant ins1 = Instant.now()
;
        try
{
            Thread.sleep(
1000);
       
} catch (InterruptedException e) {
        }
        Instant ins2 = Instant.now()
;
       
PrintUtil.print("所耗费时间为:" + Duration.between(ins1, ins2));
       
PrintUtil.print("所耗费时间为:" + Duration.between(ins1, ins2).toMillis());
       
PrintUtil.print("----------------------------------");
       
LocalTime localTime1 = LocalTime.now();
        try
{
            Thread.sleep(
1000);
       
} catch (InterruptedException e) {
        }
        LocalTime localTime2 = LocalTime.now()
;
       
PrintUtil.print("所耗费时间为:" + Duration.between(localTime1,localTime2).toMillis());
       
PrintUtil.print("----------------------------------");
       
// 日期间隔
       
LocalDate ld1 = LocalDate.now();
       
LocalDate ld2 = LocalDate.of(2011, 1, 1);
       
Period pe = Period.between(ld2, ld1);
       
PrintUtil.print(pe.getYears());
       
PrintUtil.print(pe.getMonths());
       
PrintUtil.print(pe.getDays());
   
}
   
//4. TemporalAdjuster : 时间校正器
   
@Test
   
public void test4(){
        LocalDateTime ldt = LocalDateTime.now()
;
       
PrintUtil.print(ldt);
       
// 指定Day10
       
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
       
PrintUtil.print(ldt2);
       
// 下周日
       
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
       
PrintUtil.print(ldt3);
       
//自定义:下一个工作日
       
LocalDateTime ldt5 = ldt.with((ll) -> {
            LocalDateTime ldt4 = (LocalDateTime) ll
;
           
DayOfWeek dow = ldt4.getDayOfWeek();
            if
(dow.equals(DayOfWeek.FRIDAY)){
               
return ldt4.plusDays(3);
           
}else if(dow.equals(DayOfWeek.SATURDAY)){
               
return ldt4.plusDays(2);
           
}else{
               
return ldt4.plusDays(1);
           
}
        })
;
       
PrintUtil.print(ldt5);
   
}
   
//5. DateTimeFormatter : 解析和格式化日期或时间
   
@Test
   
public void test5(){
//    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
       
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss E");
       
LocalDateTime ldt = LocalDateTime.now();
       
String strDate = ldt.format(dtf);
       
System.out.println(strDate);
       
LocalDateTime newLdt = ldt.parse(strDate, dtf);
       
System.out.println(newLdt);
   
}
   
@Test
   
public void test6(){
       
// 查看支持的时区
       
Set<String> set = ZoneId.getAvailableZoneIds();
       
set.forEach(System.out::println);
   
}
   
//6.ZonedDateZonedTimeZonedDateTime : 带时区的时间或日期
   
@Test
   
public void test7(){
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of(
"Asia/Shanghai"));
       
System.out.println(ldt);
       
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
       
System.out.println(zdt);
   
}

 

 

 

 

10:Annotation 注解

在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。 首先定义一个包装类Hints注解用来放置一组具体的Hint注解:

@interface Hints {

    Hint[] value();

}

@Repeatable(Hints.class)

@interface Hint {

    String value();

}

Java 8允许把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。

例 1: 使用包装类当容器来存多个注解(老方法)

@Hints({@Hint("hint1"), @Hint("hint2")})

class Person {}

例 2:使用多重注解(新方法)

@Hint("hint1")

@Hint("hint2")

class Person {}

第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:

Hint hint = Person.class.getAnnotation(Hint.class);

System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);

System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);

System.out.println(hints2.length);          // 2

即便没有在Person类上定义@Hints注解,还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。 另外Java 8的注解还增加到两种新的target上了:

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})

@interface MyAnnotation {}

 

 

增加JS引擎:使得java可以调用js代码:javax.script

ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine javaScriptEngine = scriptEngineManager.getEngineByName("JavaScript");
PrintUtil.print(javaScriptEngine.getClass().getName());
PrintUtil.print(javaScriptEngine.eval("function f(){return 'Hello';};f()+'World'"));

 

加入Base64

java.util.Base64

String str = "HelloWorld";
String encodStr = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
PrintUtil.print(encodStr);
String decodStr = new String(Base64.getDecoder().decode(encodStr),StandardCharsets.UTF_8);
PrintUtil.print(decodStr);

SGVsbG9Xb3JsZA==

HelloWorld

 

 

增强泛型推断:

Class List<E>{

 Static <Z> List <Z> nul(){   …}

}

JDK1.7:

List<Integer> I = List.<Integer>nil();

JDK1.8

List<Integer> I = List.nil();

 

内部数据结构的优化:

HashMap:

如果不用hashcode的话,要和每一个进行equals,效率极低。

使用哈希表(数组,默认16个),先调用哈希算法。算出数组的索引值,索引值没有Object的话,直接插入,如果有的话,碰撞。先equals,如果内容一样,后者覆盖前者,如果不一样,形成链表。

JDK1.8优化:链表过长后,将导致效率降低。如果hash后都在一个地方,就退化成链表。JDK1.8优化后,变成当链表长度到达一定值(链表总大小大于64.长度大于8)的时候,链表升级为红黑树(Hash算法再好也避免不了碰撞)【红黑树,除了添加外,其他都比链表快】

扩容:当到达75%的时候,就进行扩容。然后每个值都需要重新运算。然后重新排

 

ConcurrentHahMap:

JDK1.7采用锁中断机制,隔离级别太大,

JDK1.8改变,采用CAS算法,无锁算法。

 

栈、堆、方法区的改变:略

 

重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

 

 

//@MyAnnotations({@MyAnnotation(value="hello"),@MyAnnotation(value="atguigu")})

 

 

 

 

 

11.其他

肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如Arrays.parallelSort, StampedLock和CompletableFuture等等。

 

Java9

JDK9新特性:参考  Open JDK :http://openjdk.java.net/projects/jdk9/

102: Process API Updates
110: HTTP 2 Client
143: Improve Contended Locking
158: Unified JVM Logging
165: Compiler Control
193: Variable Handles
197: Segmented Code Cache
199: Smart Java Compilation, Phase Two
200: The Modular JDK
201: Modular Source Code
211: Elide Deprecation Warnings on Import Statements
212: Resolve Lint and Doclint Warnings
213: Milling Project Coin
214: Remove GC Combinations Deprecated in JDK 8
215: Tiered Attribution for javac
216: Process Import Statements Correctly
217: Annotations Pipeline 2.0
219: Datagram Transport Layer Security (DTLS)
220: Modular Run-Time Images
221: Simplified Doclet API
222: jshell: The Java Shell (Read-Eval-Print Loop)
223: New Version-String Scheme
224: HTML5 Javadoc
225: Javadoc Search
226: UTF-8 Property Files
227: Unicode 7.0
228: Add More Diagnostic Commands
229: Create PKCS12 Keystores by Default
231: Remove Launch-Time JRE Version Selection
232: Improve Secure Application Performance
233: Generate Run-Time Compiler Tests Automatically
235: Test Class-File Attributes Generated by javac
236: Parser API for Nashorn
237: Linux/AArch64 Port
238: Multi-Release JAR Files
240: Remove the JVM TI hprof Agent
241: Remove the jhat Tool
243: Java-Level JVM Compiler Interface
244: TLS Application-Layer Protocol Negotiation Extension
245: Validate JVM Command-Line Flag Arguments
246: Leverage CPU Instructions for GHASH and RSA
247: Compile for Older Platform Versions
248: Make G1 the Default Garbage Collector
249: OCSP Stapling for TLS
250: Store Interned Strings in CDS Archives
251: Multi-Resolution Images
252: Use CLDR Locale Data by Default
253: Prepare JavaFX UI Controls & CSS APIs for Modularization
254: Compact Strings
255: Merge Selected Xerces 2.11.0 Updates into JAXP
256: BeanInfo Annotations
257: Update JavaFX/Media to Newer Version of GStreamer
258: HarfBuzz Font-Layout Engine
259: Stack-Walking API
260: Encapsulate Most Internal APIs
261: Module System
262: TIFF Image I/O
263: HiDPI Graphics on Windows and Linux
264: Platform Logging API and Service
265: Marlin Graphics Renderer
266: More Concurrency Updates
267: Unicode 8.0
268: XML Catalogs
269: Convenience Factory Methods for Collections
270: Reserved Stack Areas for Critical Sections
271: Unified GC Logging
272: Platform-Specific Desktop Features
273: DRBG-Based SecureRandom Implementations
274: Enhanced Method Handles
275: Modular Java Application Packaging
276: Dynamic Linking of Language-Defined Object Models
277: Enhanced Deprecation
278: Additional Tests for Humongous Objects in G1
279: Improve Test-Failure Troubleshooting
280: Indify String Concatenation
281: HotSpot C++ Unit-Test Framework
282: jlink: The Java Linker
283: Enable GTK 3 on Linux
284: New HotSpot Build System
285: Spin-Wait Hints
287: SHA-3 Hash Algorithms
288: Disable SHA-1 Certificates
289: Deprecate the Applet API
290: Filter Incoming Serialization Data
291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
292: Implement Selected ECMAScript 6 Features in Nashorn
294: Linux/s390x Port
295: Ahead-of-Time Compilation
297: Unified arm32/arm64 Port
298: Remove Demos and Samples
299: Reorganize Documentation

 

特性1:目录结构的变化

JDK8目录结构:

 

bin  目录

包含命令行开发和调试工具,如 javac, jar 和 javadoc

include 目录

包含在编译本地代码时使用的 C/C++头文件

lib  目录

 

包含 JDK 工具的几个 JAR 和其他类型的文件。  它有

一个 tools.jar 文件,其中包含 javac 编译器的 Java 类

 

jre/bin  目录

 

包含基本命令,如 java 命令。  在 Windows 平台上,

它包含系统的运行时动态链接库(DLL)。

jre/lib  目录

 

包含用户可编辑的配置文件,如.properties 和.policy

文件。包含几个 JAR。  rt.jar 文件包含运行时的 Java

类和资源文件。

 

JDK9目录结构:

没有名为 jre 的子目录

bin  目录

包含所有命令。  在 Windows 平台上,它继续包含系

统的运行时动态链接库。

 

conf  目录

包含用户可编辑的配置文件,例如以前位于 jre\lib 目

录中的.properties 和.policy 文件

include  目录

 

包含要在以前编译本地代码时使用的 C/C++头文件。

它只存在于 JDK 中

 

jmods  目录

 

包含 JMOD 格式的平台模块。  创建自定义运行时映像

时需要它。  它只存在于 JDK 中

 

legal  目录

包含法律声明

lib  目录

包含非 Windows 平台上的动态链接本地库。  其子目

录和文件不应由开发人员直接编辑或使用

 

 

附:JDK和JRE的关系

JDK : Java Development Kit (Java 开发工具包)

JRE : Java Runtime Environment (Java 运行环境)

JDK = JRE +  开发工具集(例如 Javac 编译工具等)

JRE = JVM + Java SE 标准类库

 

 

 

特性2:模块化Modularity

背景:Java越来越丰富的同时,带来了臃肿(包含为了兼容低版本,不同版本类库交叉。。java开发和运行效率降低)

Java运行环境的膨胀和臃肿。JVM每启动时,至少有30~60MB的内存加载,因为JVM需要加载rt.jar,不管其中的类是否被classloader加载,整个jar都会被JVM加载到内存当中去 (而模块化可以根据模块的需要加载程序运行需要的class)

 

 

很难真正地对代码进行封装,  而系统并没有对不同部分(即JAR文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到, 会导致无意中使用了并不想被公开访问的API。

类路径本身也存在问题:  需要的JAR是否都已经有了,  是否存在重复的项?

 

JSR 376 Java  平台模块化系统(JPMS, Java Platform Module System)作为  Jigsaw  项目的核心,  其主体部分被分解成 6 个 JEP(JDK Enhancement Proposals)。

 

 

本质上,模块(module)的概念,就是package外再裹一层,用模块来管理各个package,通过声明某个package暴露,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。

 

 

实现目标

主要目的在于减少内存的开销

只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开发和维护

改进 Java SE 平台,使其可以适应不同大小的计算设备

改进其安全性,可维护性,提高性能

 

模块将由通常的类和新的模块声明文件(module-info.java)组成。

该文件是位于 java 代码结构的顶层, 该模块描述符明确地定义了的模块需要什么依赖关系,以及哪些模块被外部使用。在 exports 子句中未提及的所有包默认情况下将封装在模块中,不能在外部使用

 

右键添加module-info.java

导出

module Java9Entity {
   
exports com.jinwen.www.entity;
}

导入

module Java9Test {

    requires Java9Entity;

    requires java.logging;

}

 

使用

public class ModuleTest {

    private static final Logger LOGGER = Logger.getLogger("jinwen");

    public static void main(String[] args) {

        User user = new User("jinern",2);

        LOGGER.info("Hello World");

    }

}

requires:指明对其它模块的依赖。

exports:控制着哪些包可以被其它模块访问到。所有不被导出的包默认都被封装在模块里面。

 

 

特性3:多版本兼容jar包

多版本兼容jar功能能让你创建仅在特定版本的Java环境中运行库程序选择使用的class版本。

举例1:

jar root

  - A.class

  - B.class

  - C.class

  - D.class

  - META-INF

     - versions

        - 9

           - A.class

           - B.class

说明:

在上述场景中,root.jar 可以在 Java 9 中使用,不过A或B类使用的不是顶层的root.A或root.B 这两个class,而是处在“META-INF/versions/9”下面的这两个。这是特别为 Java9 准备的 class 版本,可以运用 Java 9 所提供的特性和库。同时,在早期的 Java 诸版本中使用这个JAR 也是能运行的,因为较老版本的 Java只会看到顶层的A类或 B 类。

 

举例2:

jar root

  - A.class

  - B.class

  - C.class

  - D.class

  - META-INF

     - versions

        - 9

           -  A.class

           - B.class

        - 10

           -

  A.class

官方说明:

By this scheme, it is possiblefor versions of a class designed for a later Java platform release to overridethe version of that same class designed for an earlier Java platform release.

 

 

public class Generator {

    public Set<String> createStrings() {

        Set<String> strings = new HashSet<String>();

        strings.add("Java");

        strings.add("8");

        return strings;

    }

}

 

public class Application {

   public static void testMultiJar(){

      Generator gen = new Generator();

      System.out.println("Generated strings: " + gen.createStrings());

   }

}

Java9文件夹下

public class Generator {

    public Set<String> createStrings() {

        return Set.of("Java", "9");

    }

}

打包

指令如下:

javac -d build --release 8  src/main/java/com/atguigu/*.java

javac -d build9 --release 9  src/main/java-9/com/atguigu/*.java

jar --create --main-class=Application --file  multijar.jar -C build . --release 9 -C build9 .

 

特性3:接口再次增强

Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。

 

在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。

 

/**

 * 接口的增强

 * 抽象类和接口的异同:

 * ①二者的定义: a.声明的方式   b.内部的结构(jdk 7 ;jdk 8 ; jdk 9)

 * ②共同点:不能实例化;以多态的方式使用

 * ③不同点:单继承 ; 多实现.

 */

interface MyInterface{

    /**

     * jdk 7 : 只能声明全局常量(public static final)和抽象方法(public abstract)

     * */

    void method1();

    /**

     * jdk 8 : 声明静态方法 和 默认方法

     * */

    public static void method2(){

        System.out.println("method2");

    }

    default void method3(){

        System.out.println("method3");

        method4();

    }

    /**

     * jdk 9 : 支持声明私有方法

     * */

    private void method4(){

        System.out.println("私有方法");

    }

}

class MyInterfaceImpl implements MyInterface{

    @Override

    public void method1() {

        System.out.println("实现接口中的抽象方法method1()");

    }

}

public class InterfaceDemo {

    public static void main(String[] args) {

        MyInterface impl = new MyInterfaceImpl();

        impl.method3();

        impl.method1();

        //不可以调用

        //impl.method2();

        //impl.method4();

    }

}

 

 

特性:语法改进:钻石操作符(Diamond Operator)使用升级

将能够与匿名实现类共同使用钻石操作符(diamond operator)

 

在java8中如下的操作是会报错的:

编译报错信息:'<>' cannot be used with anonymous classes

 

public void diamondOperator(){

    //创建一个继承于HashSet的匿名子类的对象

    Set<String> set = new HashSet<>(){};//编译通过,JDK9以前只能new HashSet<>();

    set.add("MM");

    set.add("JJ");

    set.add("GG");

    set.add("DD");

    for(String s : set){

        System.out.println(s);

    }

}

 

 

特性:try语句增强

在java8 之前,习惯于这样处理资源的关闭:

InputStreamReader reader = null;

try {

    reader = new InputStreamReader(System.in);

    reader.read();

} catch (IOException e) {

    e.printStackTrace();

}finally {

    //资源关闭操作

    if (reader!=null){

        try {

            reader.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过

//举例2:在举例1基础上,升级的写法。不用显式的处理资源的关闭.会自动调用close

//java 8 中:要求资源对象的实例化,必须放在try的一对()内完成。

//java 9 中:可以在try()中调用已经实例化的资源对象
try(InputStreamReader reader = new InputStreamReader(System.in)){

    //读取数据的过程:略

    reader.read();

}catch(IOException e){

    e.printStackTrace();

}

 

Java 9 中,用资源语句编写try将更容易,可以在try子句中使用已经初始化过的资源,此时的资源是final的:

//如下的操作不可以在jdk 8 及之前的版本中使用
        InputStreamReader reader = new InputStreamReader(System.in);

        OutputStreamWriter writer = new OutputStreamWriter(System.out);

        try(reader;writer){

            //此时的readerfinal的,不可再被赋值

//            reader = null;

            //读取数据的过程:略

            reader.read();

        }catch(IOException e){

            e.printStackTrace();

        }

 

 

 

特性:语法改进:UnderScore(下划线)使用的限制

在java 8 中,标识符可以独立使用“_”来命名:

但是,在java 9 中规定“_”不再可以单独命名标识符了,如果使用,会报错:

 

 

特性:String存储结构变更【用户体验不到string的改变】

产生背景

Motivation

The current implementation of the String classstores characters in a char array,using two bytes (sixteen bits) for each character. Data gathered from manydifferent applications indicates that strings are a major component of heapusage and, moreover, that most String objectscontain only Latin-1 characters. Such characters require only one byte ofstorage, hence half of the space in the internal char arraysof such String objects is going unused.

以前底层是char型数组。占用2个字节。大部分是单字符。省空间,用byte数组。使用flag标识字符集,实现中文类用多个字节存储等的作用。UTF-16不管什么,都2个字节。UTF-8.可能1个,可能3个等等。都有可能

 

 

使用说明

Description

We propose to change the internal representation of the Stringclass from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.

 

�结论:String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约了一些空间。

public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {

    /**

     * The value is used for character storage.

     *

     * @implNote This field is trusted by the VM, and is a subject to

     * constant folding if String instance is constant. Overwriting this

     * field after construction will cause problems.

     *

     * Additionally, it is marked with {@link Stable} to trust the contents

     * of the array. No other facility in JDK provides this functionality (yet).

     * {@link Stable} is safe here, because value is never null.

     */

    @Stable

    private final byte[] value;

拓展:StringBuffer 与StringBuilder

那StringBuffer 和 StringBuilder 是否仍无动于衷呢?

�String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will

be updated to use the same representation, as will the HotSpot VM's intrinsic string operations.

/**

 * String内部优化

 * Stringjdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[] (encoding flag)

 * StringBuffer:jdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[]

 * StringBuilder:jdk 8 及之前:底层使用char[]存储;jdk 9 : 底层使用byte[]

 *

 * String:不可变的字符序列;

 * StringBuffer:可变的字符序列;线程安全的,效率低;

 * StringBuilder:可变的字符序列;线程不安全的,效率高(jdk 5.0)

 */

 

 

 

特性:集合工厂方法:快速创建只读集合

产生背景

要创建一个只读、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。

        List<String> list = new ArrayList<>();

        list.add("Tom");

        list.add("Jerry");

        list.add("Lilei");

        list.add("HanMeimei");

        //调用Collections中的方法,将list变为只读的

        List<String> newList = Collections.unmodifiableList(list);

//        newList.add("Tim");//不能执行,否则报异常

        //遍历:jdk 8

        newList.forEach(System.out::println);

缺点:一下写了五行。即:它不能表达为单个表达式。

当然,也可以稍微简单点处理:

使用说明

//jdk 8 以及之前:创建一个只读特点的集合
        //List:

        List<Integer> list = Collections.unmodifiableList(Arrays.asList(1, 2, 3));

//        list.add(4);

        //Set:

        Set<Integer> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));

//        set.add(4);

//        set.forEach(System.out::println);

        //Map:

        Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {

            {

                put("Tom", 78);

                put("Jerry", 88);

                put("Tim", 68);

            }

        });

        map.forEach((k,v) -> System.out.println(k + ":" + v));

 

Java9因此引入了方便的方法,这使得类似的事情更容易表达。

List firsnamesList= List.of(“Joe”,”Bob”,”Bill”);

 

调用集合中静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能可用于Set和List,也可用于Map的类似形式。此时得到的集合,是不可变的:在创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException” 。

 

由于Java 8中接口方法的实现,可以直接在List,Set和Map的接口内定义这些方法,便于调用。

    //jdk 9 中:创建一个只读特点的集合

        //List:

        List<Integer> list = List.of(1, 2, 3);

//        list.add(4);

        list.forEach(System.out::println);

        //Set:

        Set<Integer> set = Set.of(2, 3, 4);

//        set.add(6);

        //Map:

        //创建只读集合的方式一:

        Map<String, Integer> map = Map.of("Tom", 23, "Jerry", 22, "Lilei", 12, "HanMeimei", 18);

//        map.put("Tim",33);

        //创建只读集合的方式二

        Map<String, Integer> map1 = Map.ofEntries(Map.entry("Tom", 23), Map.entry("Jerry", 21));

//        map1.put("Tim",33);

        System.out.println(map1);

继续添加元素到这些集合会导致 “UnsupportedOperationException”

 

特性:全新的HTTP 客户端API

使用说明

 

HTTP,用于传输网页的协议,早在1997年就被采用在目前的1.1版本中。直到2015年,HTTP2才成为标准。

HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。HTTP/1.1依赖于请求/响应周期。 HTTP/2允许服务器“push”数据:它可以发送比客户端请求更多的数据。 这使得它可以优先处理并发送对于首先加载网页至关重要的数据。

 

Java 9中有新的方式来处理HTTP调用。它提供了一个新的HTTP客户端(HttpClient),它将替代仅适用于blocking模式的HttpURLConnection (HttpURLConnection是在HTTP1.0的时代创建的,并使用了协议无关的方法),并提供对WebSocket和 HTTP/2的支持。

 

此外,HTTP客户端还提供API来处理HTTP/2的特性,比如流和服务器推送等功能。

 

全新的HTTP客户端API可以从jdk.incubator.httpclient模块中获取。因为在默认情况下,这个模块是不能根据classpath获取的,需要使用add

 

modules命令选项配置这个模块,将这个模块添加到classpath中。

module Java9Demo {

    requires Java9Module;

    requires java.logging;

    requires junit;

    requires jdk.incubator.httpclient;

}

 

//jdk 9 中 使用 HttpClient替换原有的HttpURLConnection

try {

    HttpClient client = HttpClient.newHttpClient();

    HttpRequest req = HttpRequest.newBuilder(URI.create("http://www.atguigu.com")).GET().build();

    HttpResponse<String> response = null;

    response = client.send(req, HttpResponse.BodyHandler.asString());

    System.out.println(response.statusCode());

    System.out.println(response.version().name());

    System.out.println(response.body());

} catch (IOException e) {

    e.printStackTrace();

} catch (InterruptedException e) {

    e.printStackTrace();

}

 

 

特性:多分辨率图像 API

产生背景

在Mac上,JDK已经支持视网膜显示,但在Linux和Windows上,它并没有。在那里,Java程序在当前的高分辨率屏幕上可能看起来很小,不能使用它们。这是因为像素用于这些系统的大小计算(无论像素实际有多大)。毕竟,高分辨率显示器的有效部分是像素非常小。

JEP 263以这样的方式扩展了JDK,即Windows和Linux也考虑到像素的大小。为此,使用比现在更多的现代API:Direct2D for

Windows和GTK +,而不是Xlib for Linux。图形,窗口和文本由此自动缩放。

JEP 251还提供处理多分辨率图像的能力,即包含不同分辨率的相同图像的文件。根据相应屏幕的DPI度量,然后以适当的分辨率使用图像。

3.使用说明

新的API定义在java.awt.image包下

将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体

获取这个图像的所有变体

获取特定分辨率的图像变体-表示一张已知分辨率单位为DPI的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。

基于当前屏幕分辨率大小和运用的图像转换算法,java.awt.Graphics类可以从接口MultiResolutionImage获取所需的变体。

MultiResolutionImage的基础实现是java.awt.image.BaseMultiResolutionImage。

 

 

 

Deprecated的相关API

Java 9 废弃或者移除了几个不常用的功能。其中最主要的是 Applet API,现在是标记为废弃的。随着对安全要求的提高,主流浏览器已经取消对 Java 浏览器插件的支持。HTML5 的出现也进一步加速了它的消亡。开发者现在可以使用像 Java Web Start 这样的技术来代替 Applet,它可以实现从浏览器启动应用程序或者安装应用程序。

 

同时,appletviewer 工具也被标记为废弃。

 

 

 

特性:智能Java编译工具

智能java编译工具( sjavac)的第一个阶段始于JEP139这个项目,用于在多核处理器情况下提升JDK的编译速度。如今,这个项目已经进入第二阶段,即JEP199,其目的是改进Java编译工具,并取代目前JDK编译工具javac,继而成为Java环境默认的通用的智能编译工具。

 

JDK9 还更新了javac编译器以便能够将 java 9 代码编译运行在低版本Java 中。

 

 

特性:统一的JVM日志系统

日志是解决问题的唯一有效途径:曾经很难知道导致JVM性能问题和导致JVM崩溃的根本原因。不同的JVM日志的碎片化和日志选项(例如:JVM组件对于日志使用的是不同的机制和规则),这使得JVM难以进行调试。

解决该问题最佳方法:对所有的JVM组件引入一个单一的系统,这些JVM组件支持细粒度的和易配置的JVM日志。

 

 

javadoc的HTML 5 支持

jdk 8 :生成的java帮助文档是在HTML 4 中,而HTML 4 已经是很久的标准了。

jdk 9 :javadoc的输出,现在符合兼容HTML 5 标准。

 

Javascript引擎升级:Nashorn

Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。

 

JDK 9 包含一个用来解析Nashorn 的ECMAScript 语法树的API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类,就能够分析 ECMAScript 代码。

 

 

java的动态编译器

Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到 Java,很多 C/C++ 开发者还是会不屑地评价为启动慢,吃内存。

简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机可以理解的二进制代码,而不是真正的可执行的本地代码,需要 Java 虚拟机进行解释和编译,这带来了额外的开销。

使用说明:

JITJust-in-time)编译器可以在运行时将热点编译成本地代码,速度很快。但是 Java 项目现在变得很大很复杂,因此 JIT 编译器需要花费较长时间才能热身完,而且有些 Java 方法还没法编译,性能方面也会下降。AoT 编译就是为了解决这些问题而生的。

在 JDK 9 中,AOT(JEP 295: Ahead-of-Time Compilation)作为实验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换成类似类库一样的文件。虽然仍处于试验阶段,但这个功能使得 Java 应用在被虚拟机启动之前能够先将 Java 类编译为原生代码。此功能旨在改进小型和大型应用程序的启动时间,同时对峰值性能的影响很小。

但是 Java 技术供应商 Excelsior 的营销总监 Dmitry Leskov 担心AoT 编译技术不够成熟,希望 Oracle 能够等到 Java10 时有个更稳定版本才发布。

另外 JVMCI (JEP 243: Java-Level JVM Compiler Interface)等特性,对于整个编程语言的发展,可能都具有非常重要的意义,虽然未必引起了广泛关注。目前 Graal Core API 已经被集成进入 Java 9,虽然还只是初始一小步,但是完全用 Java 语言来实现的可靠的、高性能的动态编译器,似乎不再是遥不可及,这是 Java 虚拟机开发工程师的福音。

与此同时,随着 Truffle 框架和 Substrate VM 的发展,已经让个别信心满满的工程师高呼“One VM to Rule Them All!”, 也许就在不远的将来 Ploygot 以一种另类的方式成为现实。

 

总结

在 java 9  中看不到什么?

1.1 一个标准化和轻量级的JSON API

 

一个标准化和轻量级的JSON API被许多java开发人员所青睐。但是由于资金问题无法在Java 9中见到,但并不会削减掉。Java平台首席架构师Mark Reinhold在JDK 9邮件列中说:“这个JEP将是平台上的一个有用的补充,但是在计划中,它并不像Oracle资助的其他功能那么重要,可能会重新考虑JDK 10或更高版本中实现。 ”

 

1.2 新的货币API

对许多应用而言货币价值都是一个关键的特性,但JDK对此却几乎没有任何支持。严格来讲,现有的java.util.Currency类只是代表了当前ISO 4217货币的一个数据结构,但并没有关联的值或者自定义货币。JDK对货币的运算及转换也没有内建的支持,更别说有一个能够代表货币值的标准类型了。

 

此前,Oracle 公布的JSR 354定义了一套新的Java货币API:JavaMoney,计划会在Java 9中正式引入。但是目前没有出现在JDK 9 中。

 

不过,如果你用的是Maven的话,可以做如下的添加,即可使用相关的API处理货币:

<dependency>

   <groupId>org.javamoney</groupId>

   <artifactId>moneta</artifactId>

   <version>0.9</version>

</dependency>

随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java技术的发展方向。传统的大型企业或互联网应用,正在被云端、容器化应用、模块化的微服务甚至是函数(FaaS, Function-as-a-Service所替代。

Java虽然标榜面向对象编程,却毫不顾忌的加入面向接口编程思想,又扯出匿名对象之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有余。Java对标C/C++,以抛掉内存管理为卖点,却又陷入了JVM优化的噩梦。选择比努力更重要,选择Java的人更需要对它有更清晰的认识。

Java 需要在新的计算场景下,改进开发效率。这话说的有点笼统,我谈一些自己的体会,Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。

 

特性3:jShell命令:交互式编程环境

http://upload-images.jianshu.io/upload_images/9654439-3d3b8bff6a576b3a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/438

 

http://upload-images.jianshu.io/upload_images/9654439-1c4319dce00b127b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/346

查看包含的包

http://upload-images.jianshu.io/upload_images/9654439-f1007349ae4bc156.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/296

只需按下 Tab 键,就能自动补全代码

 

列出当前 session 里所有有效的代码片段

http://upload-images.jianshu.io/upload_images/9654439-a8ac006cba06c501.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/318

查看当前 session 下所有创建过的变量

http://upload-images.jianshu.io/upload_images/9654439-38ccaec12218b619.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/205

查看当前 session 下所有创建过的方法

http://upload-images.jianshu.io/upload_images/9654439-bd621dae2e8fd976.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/197

Tips:还可以重新定义相同方法名和参数列表的方法,即为对现有方法的修改(或覆盖)。

使用外部代码编辑器来编写 Java 代码

http://upload-images.jianshu.io/upload_images/9654439-b05fe1adb4a50629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/283

使用/open命令调用:

http://upload-images.jianshu.io/upload_images/9654439-b7adfa9cabac61b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/415

没有受检异常(编译时异常)

http://upload-images.jianshu.io/upload_images/9654439-18f4cdc4eeee55bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/430

退出jShell

http://upload-images.jianshu.io/upload_images/9654439-7ac36dcf406c0bb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/149

 

 

JDK10新特性(12个)

这次发布的 Java 10,新带来的特性并不多。

根据官网公开资料,共有 12 个 JEP(JDK Enhancement Proposal 特性加强提议),带来以下加强功能:

 

 

1.JEP286: Local-Variable Type Inference(var 局部变量类型推断)

 

 

2.JEP296: Consolidate the JDK Forest into a Single Repository()

将原来用 Mercurial 管理的众多 JDK 仓库代码,合并到一个仓库中,简化开发和管理过程。

 

3.JEP304: Garbage-Collector Interface(统一的垃圾回收接口)

 

 

4.JEP307: Parallel Full GC for G1()

G1 垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。

5.JEP310: Application Class-Data Sharing()

应用程序类数据 (AppCDS) 共享,通过跨进程共享通用类元数据来减少内存占用空间,和减少启动时间。

 

6.JEP312: Thread-Local Handshakes()

ThreadLocal 握手交互。在不进入到全局 JVM 安全点 (Safepoint) 的情况下,对线程执行回调。优化可以只停止单个线程,而不是停全部线程或一个都不停

7.JEP313: Remove the Native-Header Generation Tool (javah)()

移除 JDK 中附带的 javah 工具。可以使用 javac -h 代替

8.JEP314: Additional Unicode Language-Tag Extensions)()

使用附加的 Unicode 语言标记扩展

9.JEP316: Heap Allocation on Alternative Memory Devices)()

能将堆内存占用分配给用户指定的备用内存设备

10.JEP317: Experimental Java-Based JIT Compiler)()

用 Graal 基于 Java 的编译器,可以预先把 Java 代码编译成本地代码来提升效能。

11.JEP319: Root Certificates)()

在 OpenJDK 中提供一组默认的根证书颁发机构证书。开源目前 Oracle 提供的的 Java SE 的根证书,这样 OpenJDK 对开发人员使用起来更方便。

12.JEP322: Time-Based Release Versioning)()

基于时间定义的发布版本,即上述提到的发布周期。版本号为\$FEATURE.\$INTERIM.\$UPDATE.\$PATCH,分别是大版本,中间版本,升级包和补丁版本。

 

 

 

部分新特性解析

1,迄今为止,在官方放出了Java 10少数新特性里面,局部变量类型推断(local-variable type inference) 绝对是备受万众瞩目的。它将常常在JS里面使用的var 变量引入到语言特性中,把从那些冗长的变量声明中解放出来。

这个语言功能在其他一些语言 (C#、JavaScript) 和基于 JRE 的一些语言 (Scala 和 Kotlin) 中,早已被加入。

在 Java 语言很早就在考虑,早在 2016 年正式提交了 JEP286 提议。后来举行了一次公开的开发者调查,获得最多建议的是采用类似 Scala 的方案,“同时使用 val 和 var”,约占一半;第二多的是“只使用 var”,约占四分之一。后来 Oracle 公司经过慎重考虑,采用了只使用 var 关键字的方案。

有了这个功能,开发者在写这样的代码时:

List<String> myList = new ArrayList<String>()

可以省去前面的类型声明,而只需要

var list = new ArrayList<String>()

编译器会自动推断出 list 变量的类型。对于链式表达式来说,也会很方便:

var stream = blocks.stream();

使用范围:局部变量。更加确切的说法是:具有初始化器的局部类型变量声明。

2,应用程序类数据共享(AppCDS)

CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS(Application Class-Data Sharing) 支持。

其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升。

可以想像为类似于操作系统的休眠过程,合上电脑时把当前应用环境写入磁盘,再次使用时就可以快速恢复环境。

我在自己 PC 电脑上做以下应用启动实验。

首先部署 wildfly 12 应用服务器,采用 JDK10 预览版作为 Java 环境。另外需要用到一个工具 cl4cds[1],作用是把加载类的日志记录,转换为 AppCDS 可以识别的格式。

A、安装好 wildfly 并部署一个应用,具有 Angularjs, rest, jpa 完整应用技术栈,预热后启动三次,并记录完成部署时间

分别为 6716ms, 6702ms, 6613ms,平均时间为 6677ms。

B、加入环境变量并启动,导出启动类日志

export PREPEND_JAVA_OPTS="-Xlog:class+load=debug:file=/tmp/wildfly.classtrace"

C、使用 cl4cds 工具,生成 AppCDS 可以识别的 cls 格式

/jdk-10/bin/java -cp src/classes/ io.simonis.cl4cds /tmp/wildfly.classtrace /tmp/wildfly.cls

打开文件可以看到内容为:

java/lang/Object id: 0x0000000100000eb0

java/io/Serializable id: 0x0000000100001090

java/lang/Comparable id: 0x0000000100001268

java/lang/CharSequence id: 0x0000000100001440

......

org/hibernate/type/AssociationType id: 0x0000000100c61208 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AbstractType id: 0x0000000100c613e0 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AnyType id: 0x0000000100c61820 super: 0x0000000100c613e0 interfaces: 0x0000000100c61030 0x0000000100c61208 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

....

这个文件用于标记类的加载信息。

D、使用环境变量启动 wildfly,模拟启动过程并导出 jsa 文件,就是记录了启动时类的信息。

export PREPEND_JAVA_OPTS="-Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=/tmp/wildfly.cls -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"

查看产生的文件信息,jsa 文件有较大的体积。

/opt/work/cl4cds$ ls -l /tmp/wildfly.*

-rw-rw-r-- 1 shihang shihang  8413843 Mar 20 11:07 /tmp/wildfly.classtrace

-rw-rw-r-- 1 shihang shihang  4132654 Mar 20 11:11 /tmp/wildfly.cls

-r--r--r-- 1 shihang shihang 177659904 Mar 20 11:13 /tmp/wildfly.jsa

E、使用 jsa 文件启动应用服务器

export PREPEND_JAVA_OPTS="-Xshare:on -XX:+UseAppCDS -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"

启动完毕后记录时长,三次分别是 5535ms, 5333ms, 5225ms,平均为 5364ms,相比之前的 6677ms 可以算出启动时间提升了 20% 左右。

这个效率提升,对于云端应用部署很有价值。

 

 

JDK11 新特性(17个新特性)

 

 

JEP 181: Nest-Based Access Control(基于嵌套的访问控制)

 

 

JEP 309: Dynamic Class-File Constants(动态类文件常量)

类文件新添的一种结构

Java的类型文件格式将被拓展,支持一种新的常量池格式:CONSTANT_Dynamic,加载CONSTANT_Dynamic会将创建委托给bootstrap方法。

目标

其目标是降低开发新形式的可实现类文件约束带来的成本和干扰。

 

 

JEP 315: Improve Aarch64 Intrinsics(改进 Aarch64 函数)

 

 

JEP 318: Epsilon: A No-Op Garbage Collector(Epsilon一个无操作的垃圾收集器)

新的Epsilon垃圾收集器。

A NoOp Garbage Collector(不会用来回收的GC,什么都不做)

JDK上对这个特性的描述是: 开发一个处理内存分配但不实现任何实际内存回收机制的GC, 一旦可用堆内存用完, JVM就会退出.

如果有System.gc()调用, 实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC效果一样), 因为没有内存回收, 这个实现可能会警告用户尝试强制GC是徒劳.

 

用法 : -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

 

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, 程序很快就因为堆空间不足而退出

 

使用这个选项的原因 :

提供完全被动的GC实现, 具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量.

众所周知, java实现可广泛选择高度可配置的GC实现. 各种可用的收集器最终满足不同的需求, 即使它们的可配置性使它们的功能相交. 有时更容易维护单独的实现, 而不是在现有GC实现上堆积另一个配置选项.

 

主要用途如下 :

         性能测试(它可以帮助过滤掉GC引起的性能假象)

         内存压力测试(例如,知道测试用例 应该分配不超过1GB的内存, 我们可以使用-Xmx1g –XX:+UseEpsilonGC, 如果程序有问题, 则程序会崩溃)

         非常短的JOB任务(对象这种任务, 接受GC清理堆那都是浪费空间)

         VM接口测试

         Last-drop 延迟&吞吐改进

 

 

JEP 320: Remove the Java EE and CORBA Modules(删除 Java EE 和 CORBA 模块)

在java11中移除了不太使用的JavaEE模块和CORBA技术

CORBA来自于二十世纪九十年代,Oracle说,现在用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。

 

但是删除CORBA将使得那些依赖于JDK提供部分CORBA API的CORBA实现无法运行。目前还没有第三方CORBA版本,也不确定是否会有第三方愿意接手CORBA API的维护工作。

 

在java11中将java9标记废弃的Java EE及CORBA模块移除掉,具体如下:

  1. xml相关的,

java.xml.ws,

java.xml.bind,

java.xml.ws,

java.xml.ws.annotation,

jdk.xml.bind,

jdk.xml.ws被移除,

只剩下java.xml,java.xml.crypto,jdk.xml.dom这几个模块;

  1. java.corba,

java.se.ee,

java.activation,

java.transaction被移除,

但是java11新增一个java.transaction.xa模块

 

 

JEP 321: HTTP Client (Standard)(标准HTTP客户端)

HTTP客户端进一步升级

 

JDK 9 中就已对 HTTP Client API 进行标准化,然后通过JEP 110,在 JDK 10 中进行了更新。在本次的Java 11的更新列表中,由以JEP 321进行进一步升级。该API通过CompleteableFutures提供非阻塞请求和响应语义,可以联合使用以触发相应的动作。 JDK 11完全重写了该功能。现在,在用户层请求发布者和响应发布者与底层套接字之间追踪数据流更容易了,这降低了复杂性,并最大程度上提高了HTTP / 1和HTTP / 2之间的重用的可能性。

 

标准Java异步HTTP客户端。

这是 Java 9 开始引入的一个处理 HTTP 请求的的 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。

 

来看一下 HTTP Client 的用法:

 

var request = HttpRequest.newBuilder()

.uri(URI.create("https://javastack.cn"))

.GET()

.build();

var client = HttpClient.newHttpClient();

// 同步

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());

// 异步

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())

.thenApply(HttpResponse::body)

.thenAccept(System.out::println);

上面的 .GET() 可以省略,默认请求方式为 Get!

 

更多使用示例可以看这个 API,后续有机会再做演示。

 

现在 Java 自带了这个 HTTP Client API,我们以后还有必要用 Apache 的 HttpClient 工具包吗?

         @Test

         public void testName2() throws Exception {

                   HttpClient client = HttpClient.newHttpClient();

                   HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();

                   BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();

                   CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);

                   sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);

                   //HttpResponse<String> response = sendAsync.get();

                   //String body = response.body();

                   //System.out.println(body);

                  

         }

        

         @Test

         public void testName() throws Exception {

                   HttpClient client = HttpClient.newHttpClient();

                   HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();

                   BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();

                   HttpResponse<String> response = client.send(request, responseBodyHandler);

                   String body = response.body();

                   System.out.println(body);

         }

 

 

 

JEP 323: Local-Variable Syntax for Lambda Parameters(用于 Lambda 参数的局部变量语法)

增强var用法

 

Java 10中增加了本地变量类型推断的特性,可以使用var来定义局部变量。尽管这一特性被很多人诟病,但是并不影响Java继续增强他的用法,在Java 11中,var可以用来作为Lambda表达式的局部变量声明。

 

 

JEP 324: Key Agreement with Curve25519 and Curve448(Curve25519 和 Curve448 算法的密钥协议)

 

JEP 327: Unicode 10

Unicode 10 增加了8518个字符, 总计达到了136690个字符. 并且增加了4个脚本.同时还有56个新的emoji表情符号.

 

 

JEP 328: Flight Recorder(飞行记录器)

Flight Recorder源自飞机的黑盒子

 

Flight Recorder以前是商业版的特性,在java11当中开源出来,它可以导出事件到文件中,之后可以用Java Mission Control来分析。可以在应用启动时配置java -XX:StartFlightRecording,或者在应用启动之后,使用jcmd来录制,比如

$ jcmd <pid> JFR.start

$ jcmd <pid> JFR.dump filename=recording.jfr

$ jcmd <pid> JFR.stop

 

是 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 内部的,并不需要额外的依赖,开箱即用。

https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=423998638,3946386819&fm=173&app=25&f=JPEG?w=414&h=290&s=14C649B63A0024CC5CAF0EA80300702A

JFR 是一套集成进入 JDK、JVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。

在保证低开销的基础上,JFR 提供的能力可以应用在对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 Java、C++ 等扩展 API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。

而这一切都是内建在 JDK 和 JVM 内部的,并不需要额外的依赖,开箱即用。

 

 

JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms(ChaCha20 和 Poly1305 加密算法)

实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4

实现 RFC 7539的ChaCha20 and ChaCha20-Poly1305加密算法

 

RFC7748定义的秘钥协商方案更高效, 更安全. JDK增加两个新的接口

XECPublicKey 和 XECPrivateKey

KeyPairGenerator kpg = KeyPairGenerator.getInstance(“XDH”);

NamedParameterSpec paramSpec = new NamedParameterSpec(“X25519”);

kpg.initialize(paramSpec);

KeyPair kp = kgp.generateKeyPair();

 

KeyFactory kf = KeyFactory.getInstance(“XDH”);

BigInteger u = new BigInteger(“xxx”);

XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);

PublicKey pubKey = kf.generatePublic(pubSpec);

 

KeyAgreement ka = KeyAgreement.getInstance(“XDH”);

ka.init(kp.getPrivate());

ka.doPhase(pubKey, true);

byte[] secret = ka.generateSecret();

 

 

JEP 330: Launch Single-File Source-Code Programs(启动单一文件的源代码程序)

 

JEP 331: Low-Overhead Heap Profiling(低开销的 Heap Profiling)

免费的低耗能飞行记录仪和堆分析仪

通过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式

 

提供一个低开销的, 为了排错java应用问题, 以及JVM问题的数据收集框架, 希望达到的目标如下 :

         提供用于生产和消费数据作为事件的API

         提供缓存机制和二进制数据格式

         允许事件配置和事件过滤

         提供OS,JVM和JDK库的事件

它来源于 Google 等业界前沿厂商的一线实践,通过获取对象分配细节,为 JDK 补足了对象分配诊断方面的一些短板,工程师可以通过 JVMTI 使用这个能力增强自身的工具。

从 Java 类库发展的角度来看,JDK 11 最大的进步也是两个方面:

第一, HTTP/2 Client API,新的 HTTP API 提供了对 HTTP/2 等业界前沿标准的支持,精简而又友好的 API 接口,与主流开源 API(如,Apache HttpClient, Jetty, OkHttp 等)对等甚至更高的性能。与此同时它是 JDK 在 Reactive-Stream 方面的第一个生产实践,广泛使用了 Java Flow API 等,终于让 Java 标准 HTTP 类库在扩展能力等方面,满足了现代互联网的需求。

第二,就是安全类库、标准等方面的大范围升级,其中特别是 JEP 332: Transport Layer Security (TLS) 1.3,除了在安全领域的重要价值,它还是中国安全专家范学雷所领导的 JDK 项目,完全不同于以往的修修补补,是个非常大规模的工程。

 

JEP 332: Transport Layer Security (TLS) 1.3(支持 TLS 1.3)

最新的HTTPS安全协议TLS 1.3。

实现TLS协议1.3版本, TLS允许客户端和服务器端通过互联网以一种防止窃听, 篡改以及消息伪造的方式进行通信.

JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) (可伸缩低延迟垃圾收集器)

这应该是JDK11最为瞩目的特性, 没有之一. 但是后面带了Experimental, 说明这还不建议用到生产环境.

ZGC, A Scalable Low-Latency Garbage Collector(Experimental)  可伸缩,低延迟的GC

ZGC, 这应该是JDK11最为瞩目的特性, 没有之一. 但是后面带了Experimental, 说明这还不建议用到生产环境.

         GC暂停时间不会超过10ms

         既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)

         和G1相比, 应用吞吐能力不会下降超过15%

         为未来的GC功能和利用colord指针以及Load barriers优化奠定基础

         初始只支持64位系统

 

ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。

 

GC是java主要优势之一. 然而, 当GC停顿太长, 就会开始影响应用的响应时间.消除或者减少GC停顿时长, java将对更广泛的应用场景是一个更有吸引力的平台. 此外, 现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分利用这些内存, 并且无需长时间的GC暂停时间.

 

STW – stop the world

 

ZGC是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会STW, 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长.

 

 

ZGC : avg 1.091ms   max:1.681

G1    : avg 156.806  max:543.846

 

用法 : -XX:+UnlockExperimentalVMOptions –XX:+UseZGC, 因为ZGC还处于实验阶段, 所以需要通过JVM参数来解锁这个特性

 

 

 

JEP 335: Deprecate the Nashorn JavaScript Engine(弃用 Nashorn JavaScript 引擎)

废除Nashorn javascript引擎,在后续版本准备移除掉,有需要的可以考虑使用GraalVM

它进一步明确了 Graal 很有可能将成为 JVM 向前演进的核心选择,Java-on-Java 正在一步步的成为现实。

JEP 336: Deprecate the Pack200 Tools and API (弃用 Pack200 工具和 API)

Java5中带了一个压缩工具:Pack200,这个工具能对普通的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();

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.util.jar中的Pack200 API。因为Pack200主要是用来压缩jar包的工具,由于网络下载速度的提升以及java9引入模块化系统之后不再依赖Pack200,因此这个版本将其移除掉。

 

 

 

JDK12新特性:(持续更新)

189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)

230: Microbenchmark Suite

325: Switch Expressions (Preview)

334: JVM Constants API

340: One AArch64 Port, Not Two

341: Default CDS Archives

344: Abortable Mixed Collections for G1

346: Promptly Return Unused Committed Memory from G1

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章