Java 7
- 二进制前缀
0b
或者0B
。整型(byte, short, int, long)可以直接用二进制表示。
int i = 0b010; /* 10进制值为2 */
int j = 0B010;
- 字面常量数字的下划线。用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾。
# 数字间的下划线不影响实际值
int k = 1_1; // 值为11
- 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;
}
- 泛型实例化类型自动推断。
Map<String, List<String>> myMap = new HashMap<String, List<String>>(); // 之前
Map<String, List<String>> myMap = new HashMap<>(); // 现在
- 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);
}
}
- 单个catch中捕获多个异常类型(用
|
分割)并通过改进的类型检查重新抛出异常。
try {
Integer num = null;
num.toString();
} catch(IOException|NullPointerException e) {
throw e;
}
Java 8
- 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();
- 方法引用(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();
}
}
}
- 默认方法(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();
}
}
- 重复注解(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里面做法更适合常规的思维,可读性强一点
- 类型注解(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
}
- 方法参数反射(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());
}
}
}
}
- Stream API 。新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。Stream API集成到了Collections API里。
forEach
,Stream
提供了新的方法 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);
- 统计,另外,一些产生统计结果的收集器也非常有用。它们主要用于
int
、double
、long
等基本类型上,它们可以用来产生类似如下的统计结果。
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());
HashMap
改进,在键值哈希冲突时能有更好表现。
- 在
Java 8
之前, 如果发生碰撞往往是将该value直接链接到该位置的其他所有value的末尾,即相互碰撞的所有value形成一个链表
,因此,在最坏情况下,HashMap的查找时间复杂度将退化到O(n)
- 但是在
Java 8
中,该碰撞后的处理进行了改进。当一个位置所在的冲突过多时,存储的value将形成一个排序二叉树,排序依据为key
的hashcode
,则,在最坏情况下,HashMap
的查找时间复杂度将从O(1)
退化到O(logn)
。
Date Time API
(LocalDate/Time/DateTime
日期时间API)。加强对日期和时间的处理。
# 创建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);
- 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
- java.util.concurrent 包下增加了新的类和方法。
java.util.concurrent.ConcurrentHashMap
类添加了新的方法以支持新的StreamApi和lambada表达式。
java.util.concurrent.atomic
包下新增了类以支持可伸缩可更新的变量。
java.util.concurrent.ForkJoinPool
类新增了方法以支持 common pool。
- 新增了
java.util.concurrent.locks.StampedLock
类,为控制读/写访问提供了一个基于性能的锁,且有三种模式可供选择。
- HotSpot
- 删除了 永久代(PermGen)
- 方法调用的字节码指令支持默认方法。
Java 9
- java模块系统 (
Java Platform Module System
)。
- 新的版本号格式。
$MAJOR.$MINOR.$SECURITY.$PATCH
3. java shell
,交互式命令行控制台。
- 在
private instance methods
方法上可以使用@SafeVarargs
注解。
diamond
语法与匿名内部类结合使用。
- 下划线
_
不能单独作为变量名使用。
- 支持私有接口方法(您可以使用
diamond
语法与匿名内部类结合使用)。
- Javadoc
- 简化Doclet API。
- 支持生成HTML5格式。
- 加入了搜索框,使用这个搜索框可以查询程序元素、标记的单词和文档中的短语。
- 支持新的模块系统。
- JVM
- 增强了
Garbage-First(G1)
并用它替代Parallel GC
成为默认的垃圾收集器。
- 统一了
JVM
日志,为所有组件引入了同一个日志系统。
- 删除了
JDK 8
中弃用的GC组合。(DefNew + CMS
,ParNew + SerialOld
,Incremental CMS
)。
- properties文件支持
UTF-8
编码,之前只支持ISO-8859-1
。
- 支持
Unicode 8.0
,在JDK8中是Unicode 6.2
。
Java 10
- 局部变量类型推断(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
的使用场景主要有以下四种:
-
- 本地变量初始化。
-
- 增强for循环中。
-
- 传统for循环中声明的索引变量。
-
- Try-with-resources 变量。
Optional
类添加了新的方法orElseThrow
。相比于已经存在的get方法,这个方法更推荐使用。
Java 11
- 支持
Unicode 10.0
,在jdk10中是8.0。
- 标准化
HTTP Client
- 编译器线程的延迟分配。添加了新的命令
-XX:+UseDynamicNumberOfCompilerThreads
动态控制编译器线程的数量。
- 新的垃圾收集器—
ZGC
。一种可伸缩的低延迟垃圾收集器(实验性)。
Epsilon
。一款新的实验性无操作垃圾收集器。Epsilon GC
只负责内存分配,不实现任何内存回收机制。这对于性能测试非常有用,可用于与其他GC
对比成本和收益。
Lambda
参数的局部变量语法。java10中引入的var字段得到了增强,现在可以用在lambda表达式的声明中。如果lambda表达式的其中一个形式参数使用了var,那所有的参数都必须使用var。
End...