JDK各个版本新特性


 

jdk1.5的新特性:

1,泛型(它允许指定集合里元素的类型,这样你可以得到强类型在编译时刻进行类型检查的好处)
   ArrayList list=new ArrayList()------>ArrayList<Integer>list=new ArrayList<Integer>();

2,自动装箱/拆箱
  自动装包:基本类型自动转为包装类.(int >> Integer)
  自动拆包:包装类自动转为基本类型.(Integer >> int)
  nt i=list.get(0).parseInt();-------->int i=list.get(0);原始类型与对应的包装类不用显式转换

3,for-each(循环清晰许多,并且避免了强制类型转换)

 i=0;i<a.length;i++------------>for(int i:a){......}

4,static import ( 静态导入:要使用用静态成员(方法和变量)我们必须给出提供这个方法的类。使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名。)

  import static java.lang.Math.*;
  Math.sqrt();--------------->sqrt();

5 变长参数
  int sum(int ...intlist)有任意个参数,把他看作数组

6 枚举(Enums)
  然后可以这样来使用Color myColor = Color.Red.
  枚举类型还提供了两个有用的静态方法values()和valueOf(). 我们可以很方便地使用它们

 

jdk1.6新特性

1,Desktop类和SystemTray类
  在JDK1.6中,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的一部分,在JDK1.6中,SUN将其放到了Java SE中,这也是SUN的一贯做法.JDK1.6中自带的这个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)是JDK1.6.0中除了DOM和SAX之外的又一种处理XML文档的API.
  StAX 的来历:在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).
  JDK1.6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAXSun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版 本).JDK1.6里面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
  现在我 们可以用JDK1.6 的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
  JDK1.6 提供了一个简单的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开发控制台程序
  JDK1.6中提供了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 ANNOTATION_TYPE,CONSTRUCTOR,FIELD,LOCAL_VARIABLE,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来检验安全角色.

 

jdk1.7新特性 

1,对集合类的语言支持
  Java将包含对创建集合类的第一类语言支持。这意味着集合类的创建可以像Ruby和Perl那样了。
  原本需要这样:

 List<String> list = new ArrayList<String>(); 
  list.add("item"); 
  String item = list.get(0); 
  Set<String> set = new HashSet<String>(); 
  set.add("item"); 
  Map<String, Integer> map = new HashMap<String, Integer>(); 
  map.put("key", 1); 
  int value = map.get("key"); 

  现在你可以这样:

 List<String> list = ["item"]; 
  String item = list[0]; 
  Set<String> set = {"item"}; 
  Map<String, Integer> map = {"key" : 1}; 
  int value = map["key"]; 

  这些集合是不可变的。

2,自动资源管理
  Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。这个新的语言特性允许try语句本身申请更多的资源,
  这些资源作用于try代码块,并自动关闭。
  这个:

 BufferedReader br = new BufferedReader(new FileReader(path)); 
  try { 
    return br.readLine(); 
  } finally { 
    br.close(); 
  } 

  变成了这个:

try (BufferedReader br = new BufferedReader(new FileReader(path)) { 
    return br.readLine(); 
  } 

  你可以定义关闭多个资源:

 try ( 
    InputStream in = new FileInputStream(src); 
    OutputStream out = new FileOutputStream(dest)) 
  { 
    // code 
  } 

  为了支持这个行为,所有可关闭的类将被修改为可以实现一个Closable(可关闭的)接口。

3,增强的对通用实例创建(diamond)的类型推断
  类型推断是一个特殊的烦恼,下面的代码:

 Map<String, List<String>> anagrams = new HashMap<String, List<String>>(); 

  通过类型推断后变成:

 Map<String, List<String>> anagrams = new HashMap<>(); 

  这个<>被叫做diamond(钻石)运算符,这个运算符从引用的声明中推断类型。

4,数字字面量下划线支持
  很长的数字可读性不好,在Java 7中可以使用下划线分隔长int以及long了,如:
  int one_million = 1_000_000;
  运算时先去除下划线,如:1_1 * 10 = 110,120 – 1_0 = 110

5,switch中使用string
  以前你在switch中只能使用number或enum。现在你可以使用string了:

 String s = ... 
  switch(s) { 
    case "quux": 
      processQuux(s); 
      // fall-through 
    case "foo": 
    case "bar": 
      processFooOrBar(s); 
      break; 
    case "baz": 
      processBaz(s); 
      // fall-through 
    default: 
      processDefault(s); 
      break; 
  }

6,二进制字面量
  由于继承C语言,Java代码在传统上迫使程序员只能使用十进制,八进制或十六进制来表示数(numbers)。
  由于很少的域是以bit导向的,这种限制可能导致错误。你现在可以使用0b前缀创建二进制字面量:

int binary = 0b1001_1001; 

  现在,你可以使用二进制字面量这种表示方式,并且使用非常简短的代码,可将二进制字符转换为数据类型,如在byte或short。

byte aByte = (byte)0b001; 
short aShort = (short)0b010;

JDK1.8的新特性

1,hashMap等map集合的数据结构优化
在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化
原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode
方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容
如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表
无限下去,那么效率极低,碰撞是避免不了的
加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生
在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入
除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾 ,

2,Lambda表达式
Lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。
首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
  简单的来说就是,函数也是一等公民了,在java里面一等公民有变量,对象,那么函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,看一下下面这个例子。

//这是常规的Collections的排序的写法,需要对接口方法重写
public void test1(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
      return o2.compareTo(o1);
    }
  });
  for (String string : list) {
    System.out.println(string);
  }
}
//这是带参数类型的Lambda的写法
public void testLamda1(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
      return b.compareTo(a);
    }
  );
  for (String string : list) {
    System.out.println(string);
    }
}
//这是不带参数的lambda的写法
public void testLamda2(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, (a,b)->b.compareTo(a)
  );
  for (String string : list) {
    System.out.println(string);
  }
}

可以看到不带参数的写法一句话就搞定了排序的问题,所以引入lambda表达式的一个最直观的作用就是大大的简化了代码的开发,像其他一些编程语言Scala,Python等都是支持函数式的写法的。当然,不是所有的接口都可以通过这种方法来调用,只有函数式接口才行,jdk1.8里面定义了好多个函数式接口,我们也可以自己定义一个来调用,下面说一下什么是函数式接口。

3,函数式接口
定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。

@FunctionalInterface
public interface MyLamda {
  public void test1(String y);
  //这里如果继续加一个抽象方法便会报错
  // public void test1(); 
  //default方法可以任意定义
  default String test2(){
    return "123";
  }  
  default String test3(){
    return "123";
  }
//static方法也可以定义
  static void test4(){
    System.out.println("234");
  }
}

看一下这个接口的调用,符合lambda表达式的调用方法。

MyLamda m = y -> System.out.println("ss"+y);

4,方法引用和构造器调用
  jdk1.8提供了另外一种调用方式::,当你需要使用方法引用时 , 目标引用放在分隔符::前 ,方法的名称放在后面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()的快捷写法,如下示例。

//先定义一个函数式接口
@FunctionalInterface
public interface TestConverT<T, F> {
  F convert(T t);
}

测试如下,可以以::形式调用。

public void test(){
  TestConverT<String, Integer> t = Integer::valueOf;
  Integer i = t.convert("111");
  System.out.println(i);
}

此外,对于构造方法也可以这么调用。

//实体类User和它的构造方法
public class User { 
  private String name;
  private String sex;
  public User(String name, String sex) {
    super();
    this.name = name;
    this.sex = sex;
  }
}
//User工厂
public interface UserFactory {
  User get(String name, String sex);
}
//测试类
UserFactory uf = User::new;
User u = uf.get("ww", "man");

这里的User::new就是调用了User的构造方法,Java编译器会自动根据UserFactory.get方法的签名来选择合适的构造函数。

5,局部变量限制
  Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda。 Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final,或事实上是final。
  为什么局部变量有这些限制?
  (1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
  (2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。

final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2);

5,Stream API
定义:流是Java API的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

流的操作类型分为两种:

Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
  在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

  构造流的几种方式

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();

6,新时间日期API
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。
  6.1 LocalDate/LocalTime/LocalDateTime
  LocalDate为日期处理类、LocalTime为时间处理类、LocalDateTime为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。
  now相关的方法可以获取当前日期或时间,of方法可以创建对应的日期或时间,parse方法可以解析日期或时间,get方法可以获取日期或时间信息,with方法可以设置日期或时间信息,plus或minus方法可以增减日期或时间信息;
  6.2TemporalAdjusters
  这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
  6.3DateTimeFormatter
  以前日期格式化一般用SimpleDateFormat类,但是不怎么好用,现在1.8引入了DateTimeFormatter类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss的形式:

LocalDateTime dt = LocalDateTime.now(); 
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); 
System.out.println(dtf.format(dt));

7,default关键字
 在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现,如下。,

public interface NewCharacter {
  public void test1();
  public default void test2(){
    System.out.println("我是新特性1");
  }
}

那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?
其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,如下。

public class NewCharacterImpl implements NewCharacter{
  @Override
  public void test1() {}
  public static void main(String[] args) {
    NewCharacter nca = new NewCharacterImpl();
    nca.test2();
  }
}

所以说这个default方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk的集合List里面增加了一个sort方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList等都需要对其添加实现,那么现在用default定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

8,Objects方法新特性

//比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);

//深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的对应下标值是否相同)
public static boolean deepEquals(Object a, Object b);

//返回对象的hashCode,若传入的为null,返回0
public static int hashCode(Object o);

//返回传入可变参数的所有值的hashCode的总和(这里说总和有点牵强,具体参考Arrays.hashCode()方法)
public static int hash(Object... values);

//返回对象的String表示,若传入null,返回null字符串
public static String toString(Object o)

//返回对象的String表示,若传入null,返回默认值nullDefault
public static String toString(Object o, String nullDefault)

//使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
public static <T> int compare(T a, T b, Comparator<? super T> c)

//如果传入的obj为null抛出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj)

//如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)

-----------------------------以下是jdk8新增方法---------------------------

//判断传入的obj是否为null,是返回true,否者返回false
public static boolean isNull(Object obj)

//判断传入的obj是否不为null,不为空返回true,为空返回false (和isNull()方法相反)
public static boolean nonNull(Object obj)

//如果传入的obj为null抛出NullPointerException并且使用参数messageSupplier指定错误信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)

 

 JDK1.9 新特性

1,集合加强
jdk9为所有集合(List/Set/Map)都增加了of和copyOf方法,用来创建不可变集合,即一旦创建就无法再执行添加、删除、替换、排序等操作,否则将报java.lang.UnsupportedOperationException异常。一般在特定场景下用还是可以的,不过如果引用了guava库的话推荐还是使用guava把hhhh,例子如下:

    List strs = List.of("Hello", "World");
    List strsCopy = List. copyOf(strs);
    Set strs = Set.of("Hello", "World");
    Map maps = Map.of("Hello", 1, "World", 2);

2,私有接口方法
jdk8提供了接口的默认方法(default)和静态方法,打破了之前接口只能定义方法而不能存在行为。jdk9则是允许接口定义私有方法,私有方法可以作为通用方法放在默认方法中调用,不过实际中并无多大用处,至少对我来说。

3,垃圾收集机制
jdk9把G1作为默认的垃圾收集器实现,替换了jdk7和jdk8的默认垃圾收集器实现:Parallel Scavenge(新生代)+Parallel Old(老年代)。

4,I/O流加强
java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据:
readAllBytes:读取 InputStream 中的所有剩余字节
readNBytes: 从 InputStream 中读取指定数量的字节到数组中
transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中 

5,JShell工具
jdk9引入了jshell这个交互性工具,让Java也可以像脚本语言一样来运行,可以从控制台启动 jshell ,在 jshell 中直接输入表达式并查看其执行结果。当需要测试一个方法的运行效果,或是快速的对表达式进行求值时,jshell 都非常实用。举个例子:

JDK10新特性

1,局部变量类型推断
局部变量类型推断可以说是jdk10中最值得注意的特性,这是Java语言开发人员为了简化Java应用程序的编写而采取的又一步,举个例子:
原先我们需要这么定义一个list
List<String> list = new ArrayList<>();
使用局部类型推断var关键词定义
var list = new ArrayList<String>();
不过局部变量类型推断仅仅适用在:
有初始化值的局部变量
增强 for 循环中的索引
传统 for 循环中声明的局部变量
Oracle 的 Java 团队申明,以下不支持局部变量类型推断:
方法参数
构造函数参数
方法返回类型
字段
catch 代码块(或任何其他类型的变量声明)

2,线程本地握手
jdk10将引入一种在线程上执行回调的新方法,因此这将会很方便能停止单个线程而不是停止全部线程或者一个都不停。说实话并不是很懂是什么意思...

3,GC改进和内存管理
jdk10中有2个JEP专门用于改进当前的垃圾收集元素。
第一个垃圾收集器接口是(JEP 304),它将引入一个纯净的垃圾收集器接口,以帮助改进不同垃圾收集器的源代码隔离。
预定用于Java 10的第二个JEP是针对G1的并行完全GC(JEP 307),其重点在于通过完全GC并行来改善G1最坏情况的等待时间。G1是Java 9中的默认GC,并且此JEP的目标是使G1平行。

 
JDK11新特性

1,字符串加强

// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空格
" Javastack ".strip(); // "Javastack"
// 去除尾部空格 
" Javastack ".stripTrailing(); 
// 去除首部空格 
" Javastack ".stripLeading(); // "Javastack "
// 复制字符串
"Java".repeat(3); // "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3

 

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

3,用于 Lambda 参数的局部变量语法
用于 Lambda 参数的局部变量语法简单来说就是支持类型推导:

var x = new A();
for (var x : xs) { ... }
try (var x = ...) { ... } catch ...

 

4,ZGC
从JDK 9开始,JDK使用G1作为默认的垃圾回收器。G1可以说是GC的一个里程碑,G1之前的GC回收,还是基于固定的内存区域,而G1采用了一种“细粒度”的内存管理策略,不在固定的区分内存区域属于surviors、eden、old,而我们不需要再去对于年轻代使用一种回收策略,老年代使用一种回收策略,取而代之的是一种整体的内存回收策略。这种回收策略在我们当下cpu、内存、服务规模都越来越大的情况下提供了更好的表现,而这一代ZGC更是有了突破性的进步。
从原理上来理解,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化,在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行,一般而言,主要有如下几种方式优化"Stop the world":
使用多个线程同时回收(并行回收)
回收过程分为多次停顿(增量回收)
在程序运行期间回收,不需要停顿或只停顿很短时间(并发回收)
只回收内存而不整理内存
ZGC主要采用的是并发回收的策略,相较于G1 ZGC最主要的提升是使用Load Barrier技术实现,引用R大对于ZGC的评价:
与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

JDK12新特性

1,Switch Expressions
这是一个为开发者准备的特性,我们可以利用具体代码快速了解一下,下面是传统 statement 形式的 switch 语法:

switch (day) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
    System.out.println(6);
    break;
  case TUESDAY:
    System.out.println(7);
    break;
  case THURSDAY:
  case SATURDAY:
    System.out.println(8);
    break;
  case WEDNESDAY:
    System.out.println(9);
    break;
}

如果有编码经验,你一定知道,switch 语句如果漏写了一个 break,那么逻辑往往就跑偏了,这种方式既繁琐,又容易出错。如果换成 switch 表达式,Pattern Matching 机制能够自然地保证只有单一路径会被执行,请看下面的代码示例:

switch (day) {
  case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
  case TUESDAY -> System.out.println(7);
  case THURSDAY, SATURDAY -> System.out.println(8);
  case WEDNESDAY -> System.out.println(9);
}

 

更进一步,下面的表达式,为我们提供了优雅地表达特定场合计算逻辑的方式

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

Switch Expressions 或者说起相关的 Pattern Matching 特性,为我们提供了勾勒出了 Java 语法进化的一个趋势,将开发者从复杂繁琐的低层次抽象中逐渐解放出来,以更高层次更优雅的抽象,既降低代码量,又避免意外编程错误的出现,进而提高代码质量和开发效率。

2,Shenandoah GC
新增了一个名为 Shenandoah 的 GC 算法,通过与正在运行的 Java 线程同时进行 evacuation 工作来减少 GC 暂停时间。使用 Shenandoah 的暂停时间与堆大小无关,这意味着无论堆是 200 MB 还是 200 GB,都将具有相同的暂停时间。

JDK13新特性
JDK13于9月17日正式发布。目前该版本包含的特性已经全部固定,主要包含以下五个:
下面来逐一介绍下这五个重要的特性。
Dynamic CDS Archives
这一特性是在JEP310:Application Class-Data Sharing基础上扩展而来的,Dynamic CDS Archives中的CDS指的就是Class-Data Sharing。
那么,这个JEP310是个啥东西呢?
我们知道在同一个物理机/虚拟机上启动多个JVM时,如果每个虚拟机都单独装载自己需要的所有类,启动成本和内存占用是比较高的。所以Java团队引入了CDS的概念,通过把一些核心类在每个JVM间共享,每个JVM只需要装载自己的应用类,启动时间减少了,另外核心类是共享的,所以JVM的内存占用也减少了。
CDS 只能作用于 Boot Class Loader 加载的类,不能作用于 App Class Loader 或者自定义的 Class Loader 加载的类。
在 Java 10 中,则将 CDS 扩展为 AppCDS,顾名思义,AppCDS 不止能够作用于 Boot Class Loader了,App Class Loader 和自定义的 Class Loader 也都能够起作用,大大加大了 CDS 的适用范围。也就说开发自定义的类也可以装载给多个JVM共享了。
Java 10中包含的JEP310的通过跨不同Java进程共享公共类元数据来减少了内存占用和改进了启动时间。
但是,JEP310中,使用AppCDS的过程还是比较复杂的,需要有三个步骤:
这一次的JDK 13中的JEP 350 ,在JEP310的基础上,又做了一些扩展。允许在Java应用程序执行结束时动态归档类,归档类将包括默认的基础层 CDS(class data-sharing)存档中不存在的所有已加载的应用程序类和库类。
也就是说,在Java 13中再使用AppCDS的时候,就不在需要这么复杂了。
ZGC: Uncommit Unused Memory
在讨论这个问题之前,想先问一个问题,JVM的GC释放的内存会还给操作系统吗?
GC后的内存如何处置,其实是取决于不同的垃圾回收器的。因为把内存还给OS,意味着要调整JVM的堆大小,这个过程是比较耗费资源的。
在JDK 11中,Java引入了ZGC,这是一款可伸缩的低延迟垃圾收集器,但是当时只是实验性的。并且,ZGC释放的内存是不会还给操作系统的。
而在Java 13中,JEP 351再次对ZGC做了增强,本次 ZGC 可以将未使用的堆内存返回给操作系统。之所以引入这个特性,是因为如今有很多场景中内存是比较昂贵的资源,在以下情况中,将内存还给操作系统还是很有必要的:
1、那些需要根据使用量付费的容器
2、应用程序可能长时间处于空闲状态并与许多其他应用程序共享或竞争资源的环境。
3、应用程序在执行期间可能有非常不同的堆空间需求。例如,启动期间所需的堆可能大于稍后在稳定状态执行期间所需的堆。

Reimplement the Legacy Socket API
使用易于维护和调试的更简单、更现代的实现替换 java.net.Socket 和 java.net.ServerSocket API。
java.net.Socket和java.net.ServerSocket的实现非常古老,这个JEP为它们引入了一个现代的实现。现代实现是Java 13中的默认实现,但是旧的实现还没有删除,可以通过设置系统属性jdk.net.usePlainSocketImpl来使用它们。
运行一个实例化Socket和ServerSocket的类将显示这个调试输出。这是默认的(新的).
上面输出的sun.nio.ch.NioSocketImpl就是新提供的实现。
如果使用旧的实现也是可以的(指定参数jdk.net.usePlainSocketImpl):
上面的结果中,旧的实现java.net.PlainSocketImpl被用到了。
Switch Expressions (Preview)
在JDK 12中引入了Switch表达式作为预览特性。JEP 354修改了这个特性,它引入了yield语句,用于返回值。这意味着,switch表达式(返回值)应该使用yield, switch语句(不返回值)应该使用break。
在以前,我们想要在switch中返回内容,还是比较麻烦的,一般语法如下:
在JDK13中使用以下语法:
或者
在这之后,switch中就多了一个关键字用于跳出switch块了,那就是yield,他用于返回一个值。和return的区别在于:return会直接跳出当前循环或者方法,而yield只会跳出当前switch块。
Text Blocks (Preview)
在JDK 12中引入了Raw String Literals特性,但在发布之前就放弃了。这个JEP在引入多行字符串文字(text block)在意义上是类似的。
text block,文本块,是一个多行字符串文字,它避免了对大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。
我们以前从外部copy一段文本串到Java中,会被自动转义,如有一段以下字符串:
将其复制到Java的字符串中,会展示成以下内容:
使用“”“作为文本块的开始符合结束符,在其中就可以放置多行的字符串,不需要进行任何转义。看起来就十分清爽了。
如常见的SQL语句:
看起来就比较直观,清爽了。

JDK13中包含的5个特性,能够改变开发者的编码风格的主要有Text Blocks和Switch Expressions两个新特性,但是这两个特性还处于预览阶段。
而且,JDK13并不是LTS(长期支持)版本,如果你正在使用Java 8(LTS)或者Java 11(LTS),暂时可以不必升级到Java 13.

 

原地址:https://blog.csdn.net/papima/article/details/78219001

原地址:https://blog.csdn.net/wlanye/article/details/51954855

原地址:https://www.cnblogs.com/junrong624/p/11596191.html

原地址:https://blog.csdn.net/jlq_diligence/article/details/102775215


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM