Java 7,8,9,10 各个版本的特性

Java 7

  1. 二进制前缀0b或者0B。整型(byte, short, int, long)可以直接用二进制表示。
int i = 0b010; /* 10进制值为2 */
int j = 0B010;
  1. 字面常量数字的下划线。用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾。
# 数字间的下划线不影响实际值
int k = 1_1; // 值为11
  1. switch 支持String类型。
String args = "A";
switch (A) {
   case "A":
	System.out.print("is A");
	break;
   case "B":
	System.out.print("is B");
	break;
   default:
	System.out.print("is default");
	break;
}
  1. 泛型实例化类型自动推断。
Map<String, List<String>> myMap = new HashMap<String, List<String>>(); // 之前  
Map<String, List<String>> myMap = new HashMap<>(); // 现在
  1. try-with-resources语句。
# 传统的资源关闭方式
public static void main(String[] args) {
    FileInputStream inputStream = null;
    try {
        inputStream = new FileInputStream(new File("test"));
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }
}

# try-with-resource语法
public static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}
  1. 单个catch中捕获多个异常类型(用| 分割)并通过改进的类型检查重新抛出异常。
try {
   Integer num = null;
   num.toString();
} catch(IOException|NullPointerException e) {
   throw e;
}

Java 8

  1. lambada表达式(Lambda Expressions)。Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
# `Java8`之前,我们在使用Runnale创建线程的时候,经常需要将Runable实例传入new Thread中。一般采用匿名内部类将函数作为参数的形式传入

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Before Java8,There is no Lambda expression!");
    }
}).start();

# 使用Java8的新特性Lambda这样写:
new Thread(() -> System.out.println("In Java8, There is Lambda expression!")).start();
  1. 方法引用(Method references)。方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,可以使语言的构造更紧凑简洁,减少冗余代码。
public class MethodReference {
    public static void main(String[] args) {
        // 方法引用::引用构造函数
        PersonFactory factory = new PersonFactory(Person::new);
        List<Person> personList = new ArrayList<Person>();

        Person p1 = factory.getPerson();
        p1.setName("Kobe");
        personList.add(p1);
        Person p2 = factory.getPerson();
        p2.setName("James");
        personList.add(p2);
        Person p3 = factory.getPerson();
        p3.setName("Paul");
        personList.add(p3);

        Person[] persons1 = personList.toArray(new Person[personList.size()]);
        System.out.print("排序前: ");
        printArray(persons1);

        // 方法引用::引用静态方法
        Arrays.sort(persons1, MethodReference::myCompare);
        System.out.print("排序后: ");
        printArray(persons1);
        System.out.println();

        Person[] persons2 = personList.toArray(new Person[personList.size()]);
        System.out.print("排序前: ");
        printArray(persons2);

        // 方法引用::用特定对象的实例方法
        Arrays.sort(persons2, p1::compare);
        System.out.print("排序后: ");
        printArray(persons2);
        System.out.println();

        Person[] persons3 = personList.toArray(new Person[personList.size()]);
        System.out.print("排序前: ");
        printArray(persons3);

        // 方法引用::引用特定类型的任意对象的实例方法
        Arrays.sort(persons3, Person::compareTo);
        System.out.print("排序后: ");
        printArray(persons3);
    }

    public static void printArray(Person[] persons) {
        for (Person p : persons) {
            System.out.print(p.name + "  ");
        }
        System.out.println();
    }

    public static int myCompare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }

    static class Person {
        private String name;

        public Person() {
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int compare(Person p1, Person p2) {
            return p1.getName().compareTo(p2.getName());
        }

        public int compareTo(Person p) {
            return this.getName().compareTo(p.getName());
        }
    }

    static class PersonFactory {
        private Supplier<Person> supplier;

        public PersonFactory(Supplier<Person> supplier) {
            this.supplier = supplier;
        }

        public Person getPerson() {
            return supplier.get();
        }
    }
}
  1. 默认方法(Default methods)。默认方法允许将新功能添加到库的接口中,并确保兼容实现老版本接口的旧有代码。
interface InterfaceA {
    default void foo() {
        System.out.println("InterfaceA foo");
    }
}

interface InterfaceB extends InterfaceA {
}

interface InterfaceC extends InterfaceA {
    @Override
    default void foo() {
        System.out.println("InterfaceC foo");
    }
}

interface InterfaceD extends InterfaceA {
    @Override
    void foo();
}

public class Test {
    public static void main(String[] args) {
        new InterfaceB() {}.foo(); // 打印:“InterfaceA foo”
        new InterfaceC() {}.foo(); // 打印:“InterfaceC foo”
        new InterfaceD() {
            @Override
            public void foo() {
                System.out.println("InterfaceD foo");
            }
        }.foo(); // 打印:“InterfaceD foo”
        
        // 或者使用 lambda 表达式
        ((InterfaceD) () -> System.out.println("InterfaceD foo")).foo();
    }
}
  1. 重复注解(Repeating Annotations)。重复注解提供了在同一声明或类型中多次应用相同注解类型的能力。
# java 8之前也有重复使用注解的解决方案,但可读性不是很好,比如下面的代码:

public @interface Authority {  
     String role();  
} 

public @interface Authorities {  
    Authority[] value();  
}

public class RepeatAnnotationUseOldVersion {  
    @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})  
    public void doSomeThing(){  
    }  
}

由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解,我们再来看看java 8里面的做法:

@Repeatable(Authorities.class)  
public @interface Authority {  
     String role();  
} 

public @interface Authorities {  
    Authority[] value();  
}

public class RepeatAnnotationUseNewVersion {  
    @Authority(role="Admin")  
    @Authority(role="Manager")  
    public void doSomeThing(){ }  
}

# 不同的地方是,创建重复注解@Authority时,加上@Repeatable,指向存储注解@Authorities,在使用时候,直接可以重复使用@Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点
  1. 类型注解(Type Annotation)。在任何地方都能使用注解,而不是在声明的地方。
# @Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:

public enum ElementType {
    /** 类,接口(包括注解类型)或枚举的声明 */
    TYPE,

    /** 属性的声明 */
    FIELD,

    /** 方法的声明 */
    METHOD,

    /** 方法形式参数声明 */
    PARAMETER,

    /** 构造方法的声明 */
    CONSTRUCTOR,

    /** 局部变量声明 */
    LOCAL_VARIABLE,

    /** 注解类型声明 */
    ANNOTATION_TYPE,

    /** 包的声明 */
    PACKAGE
}

# @CherryAnnotation 被限定只能使用在类、接口或方法上面
@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface CherryAnnotation {
    String name();
    int age() default 18;
    int[] array();
}

# @Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。  
# 注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。同样使用了RetentionPolicy枚举类型定义了三个阶段:

public enum RetentionPolicy {
    /**
     * (注解将被编译器忽略掉)
     */
    SOURCE,

    /**
     * (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
     */
    CLASS,

    /**
     * (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
     */
    RUNTIME
}
  1. 方法参数反射(Method Parameter Reflection)。
class Test {
  public void replace(String str, List<String> list){

  }
}

public class MethodParameterTest {
   public static void main(String[] args)throws Exception {
      // 获取String的类
      Class<Test> clazz = Test.class;
      // 获取String类的带两个参数的replace()方法
      Method replace = clazz.getMethod("replace" , String.class, List.class);
      // 获取指定方法的参数个数
      System.out.println("replace方法参数个数:" + replace.getParameterCount());
      // 获取replace的所有参数信息
      Parameter[] parameters = replace.getParameters();
      int index = 1;
      // 遍历所有参数
      for (Parameter p : parameters)
      {
         if (p.isNamePresent())
         {
            System.out.println("---第" + index++ + "个参数信息---");
            System.out.println("参数名:" + p.getName());
            System.out.println("形参类型:" + p.getType());
            System.out.println("泛型类型:" + p.getParameterizedType());
         }
      }
   }
}
  1. Stream API 。新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。Stream API集成到了Collections API里。
  • forEachStream 提供了新的方法 forEach 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:
Random  random = new  Random();
random.ints().limit(10).forEach(System.out::println);
  • map,方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); 
// 获取对应的平方数  
List<Integer> squaresList = numbers.stream().map(  i -> i*i).distinct().collect(Collectors.toList());
  • filter,方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
// 获取空字符串的数量
int  count = strings.stream().filter(string -> string.isEmpty()).count();
  • limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:
Random  random = new  Random();
random.ints().limit(10).forEach(System.out::println);
  • sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
Random  random = new  Random();
random.ints().limit(10).sorted().forEach(System.out::println);
  • 并行(parallel)程序,parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
// 获取空字符串的数量
int  count = strings.parallelStream().filter(string -> string.isEmpty()).count();
  • 并行(parallel)程序,parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); 
// 获取空字符串的数量
int  count = strings.parallelStream().filter(string -> string.isEmpty()).count();
  • Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String  mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
  • 统计,另外,一些产生统计结果的收集器也非常有用。它们主要用于intdoublelong等基本类型上,它们可以用来产生类似如下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics  stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
  1. HashMap改进,在键值哈希冲突时能有更好表现。
  • Java 8 之前, 如果发生碰撞往往是将该value直接链接到该位置的其他所有value的末尾,即相互碰撞的所有value形成一个链表,因此,在最坏情况下,HashMap的查找时间复杂度将退化到O(n)
  • 但是在Java 8中,该碰撞后的处理进行了改进。当一个位置所在的冲突过多时,存储的value将形成一个排序二叉树,排序依据为keyhashcode,则,在最坏情况下,HashMap的查找时间复杂度将从O(1)退化到O(logn)
  1. Date Time API (LocalDate/Time/DateTime 日期时间API)。加强对日期和时间的处理。
  • LocalDate
# 创建LocalDate的方式有很多种,这里介绍几种最常用的方式:**
LocalDate now = LocalDate.now(); // 无参当前时间  输出2019-01-15
LocalDate of = LocalDate.of(2019, 1, 15); // 分别传递 年 月 日创建   输出2019-01-15
LocalDate parse = LocalDate.parse("2019-01-15"); // 传递日期字符串  输出2019-01-15
LocalDate parse1 = LocalDate.parse("2019/01/15", DateTimeFormatter.ofPattern("yyyy/MM/dd")); // 按指定格式创建日期  输出2019-01-15

# 通用的 增加 日期的方法
LocalDate now = LocalDate.now(); // 2019-01-15
LocalDate plusDate = now.plus(4, ChronoUnit.YEARS);  //添加数量4 添加字段 年 
System.out.println(plusDate);

# 计算日期的间隔
LocalDate now = LocalDate.now(); // 2019-01-15
LocalDate of = LocalDate.of(1989, 07, 02);
long until = now.until(of, ChronoUnit.MONTHS);  // 间隔 月
System.out.println(until);
  1. java.util 包下的改进,提供了几个实用的工具类。
  • 并行数组排序。
private void test() {
   int[] arr = getNumbers();
   long start = System.currentTimeMillis();
   Arrays.sort(arr);
   System.out.println("串行排序时间:"+(System.currentTimeMillis() - start) + " ms" );
   arr = getNumbers();
   start = System.currentTimeMillis();
   Arrays.parallelSort(arr);
   System.out.println("并行排序时间:"+(System.currentTimeMillis() - start) + " ms" );
}
 
private int[] getNumbers() {
   int[] arr = new int[5000000];
   Random r = new Random();
   for (int i = 0; i < 5000000; ++i) {
      arr[i] = r.nextInt(1000) + 1;
   }
   return arr;
}

# 输出
串行排序时间:271 ms
并行排序时间:146 ms
  • 标准的Base64编解码。
  • 支持无符号运算。
  1. java.util.concurrent 包下增加了新的类和方法。
  • java.util.concurrent.ConcurrentHashMap 类添加了新的方法以支持新的StreamApi和lambada表达式。
  • java.util.concurrent.atomic 包下新增了类以支持可伸缩可更新的变量。
  • java.util.concurrent.ForkJoinPool类新增了方法以支持 common pool。
  • 新增了java.util.concurrent.locks.StampedLock类,为控制读/写访问提供了一个基于性能的锁,且有三种模式可供选择。
  1. HotSpot
  • 删除了 永久代(PermGen)
  • 方法调用的字节码指令支持默认方法。

Java 9

  1. java模块系统 (Java Platform Module System)。
  2. 新的版本号格式。$MAJOR.$MINOR.$SECURITY.$PATCH
    3. java shell,交互式命令行控制台。
  3. private instance methods方法上可以使用@SafeVarargs注解。
  4. diamond语法与匿名内部类结合使用。
  5. 下划线_不能单独作为变量名使用。
  6. 支持私有接口方法(您可以使用diamond语法与匿名内部类结合使用)。
  7. Javadoc
  • 简化Doclet API。
  • 支持生成HTML5格式。
  • 加入了搜索框,使用这个搜索框可以查询程序元素、标记的单词和文档中的短语。
  • 支持新的模块系统。
  1. JVM
  • 增强了Garbage-First(G1)并用它替代Parallel GC成为默认的垃圾收集器。
  • 统一了JVM 日志,为所有组件引入了同一个日志系统。
  • 删除了JDK 8中弃用的GC组合。(DefNew + CMSParNew + SerialOldIncremental CMS)。
  1. properties文件支持UTF-8编码,之前只支持ISO-8859-1
  2. 支持Unicode 8.0,在JDK8中是Unicode 6.2

Java 10

  1. 局部变量类型推断(Local-Variable Type Inference)
# 之前的代码格式
URL url = new URL("http://www.oracle.com/");
URLConnection conn = url.openConnection();
Reader reader = new BufferedReader(
    new InputStreamReader(conn.getInputStream()))

# java10中用var来声明变量
var url = new URL("http://www.oracle.com/");
var conn = url.openConnection();
var reader = new BufferedReader(
    new InputStreamReader(conn.getInputStream()));
  • var是一个保留类型名称,而不是关键字。所以之前使用var作为变量、方法名、包名的都没问题,但是如果作为类或接口名,那么这个类和接口就必须重命名了。
  • var的使用场景主要有以下四种:
    1. 本地变量初始化。
    1. 增强for循环中。
    1. 传统for循环中声明的索引变量。
    1. Try-with-resources 变量。
  1. Optional类添加了新的方法orElseThrow。相比于已经存在的get方法,这个方法更推荐使用。

Java 11

  1. 支持Unicode 10.0,在jdk10中是8.0。
  2. 标准化HTTP Client
  3. 编译器线程的延迟分配。添加了新的命令-XX:+UseDynamicNumberOfCompilerThreads动态控制编译器线程的数量。
  4. 新的垃圾收集器—ZGC。一种可伸缩的低延迟垃圾收集器(实验性)。
  5. Epsilon。一款新的实验性无操作垃圾收集器。Epsilon GC 只负责内存分配,不实现任何内存回收机制。这对于性能测试非常有用,可用于与其他GC对比成本和收益。
  6. Lambda参数的局部变量语法。java10中引入的var字段得到了增强,现在可以用在lambda表达式的声明中。如果lambda表达式的其中一个形式参数使用了var,那所有的参数都必须使用var。

End...

posted @ 2020-08-29 20:18  Kotaku  阅读(263)  评论(0)    收藏  举报