写点什么

JAVA8 之后的版本履历,少儿编程 java 面试题目

作者:MySQL神话
  • 2021 年 11 月 27 日
  • 本文字数:8766 字

    阅读完需:约 29 分钟

  • Oracle JDK 基于 OpenJDK 构建,技术上基本没有差异。


1.3 Android 和 JDK




说起 Android 和 OpenJDK 的历史渊源,还是略微复杂。


简单来说,Android 最开始使用的 Java 是基于 Apache 协议发布的 Harmony,后来由于 Harmony 本身的限制和 Oracle 公司的起诉,从 Android N 以后, Google 开始使用 OpenJDK。


然后我们再稍微展开聊聊。


1.4 JVM 和 TCK




Sun 公司最初开发了 Java 语言,同时也开发了 JVM,并且定义了 JVM 规范。这个我们比较清楚,只要基于 JVM 规范开发自己的语言,就可以运行在 JVM 上。但是依照规范开发了语言之后,需要通过 Sun 的 TCK(Technology Compatibility Kit)测试,之后才能成为官方认可的 JVM 语言。


1.5 Harmony 和 OpenJDK




基于 JVM 规范,Apache 开发了一个开源免费的 Java 实现 Harmony,并且根据 Apache License v2 发布。但是 Sun 公司并没有给 Harmony TCK 许可。


在 2009.4.15 Sun 公司发布了 OpenJDK,基于 GNU GPL 发布。同时 Sun 公司规定只有衍生自 OpenJDK 采用的 GPL 协议的开源实现才能运行 OpenJDK 的 TCK。之后 Oracle 收购 Sun 公司以后接管了 OpenJDK。


由于 Apache 的 Harmony 是 Apache 协议,与 OpenJDK 的 GPL 协议不兼容,所以 Harmony 一直没有得到 TCK 授权。


Android 最开始是采用了 Harmony 作为自己的 Java 类库,因为 Harmony 使用的 Apache 协议更自由。而由于 Harmony 没有通过 TCK 认证,也为后来 Oracle 起诉 Google 埋下伏笔。


1.6 Oracle 和 Google 关于 JDK 纠纷




后来 Oracle 起诉 Google 主要集中在两点,一是 Oracle 认为 Google 代码中使用了 Java 的 37 个 API,二是 Sun 公司前员工在跳槽后为 Android 项目开发时,直接复制了 OpenJDK 中的九行代码,而 Android 项目并没有按照 GPL 协议授权,所以复制 OpenJDK 代码是没有通过 GPL 授权的。


所以到后来为了解决专利的问题,Android N 以后,Android 开始使用 OpenJDK 替换 Harmony。


以上 Android 和 JDK 参考资料:


https://juejin.im/entry/5abc516b518825556f557f90


https://gudong.name/2019/04/05/android-why-java-harmony.html


聊了一些关于 Java 的历史,下面我们看看各个 Java 版本有那些新特性。这里只列出了对开发者影响比较大的一些特性~(如果需要 Java 面试知识点,可以在 Java 知音公众号回复”Java 面试题聚合“)


二、JAVA 版本发布


==========


2.1 JAVA8




Java8 参考资料


http://ifeve.com/java-8-features-tutorial/


https://juejin.im/post/5ae6bfb66fb9a07a9b35bac1


https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html


https://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html

2.1.1??Lambda 和 函数式接口

Lambda 表达式相信不用再过多的介绍,终于在 Java 8 引入了,可以极大的减少代码量,代码看起来更清爽。


函数式接口就是有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。可以隐式转化为 Lambda 表达式。


我们定义一个函数式接口如下:


@FunctionalInterface


interface?Operation?{


int?operation(int?a,?int?b);


}


再定义一个 Class 用来操作 Operation 接口。


class?Test?{


private?int?operate(int?a,?int?b,?Operation?operation)?{


return?operation.operation(a,?b);


}


}


Test?test?=?new?Test();


在 Java 8 之前,我们想要实现 Operation 接口并传给 Test.operate() 方法使用,我们要定义一个匿名类,实现 Operation 方法。


test.operate(1,?2,?new?Operation()?{


@Override


public?int?operation(int?a,?int?b)?{


return?a?+?b;


}


});


而使用 Lambda 表达式,我们就可以这样写了:


test.operate(1,?2,?(a,?b)?->?a?+?b);

2.1.2. 方法引用

通过方法引用,可以使用方法的名字来指向一个方法。使用一对冒号来引 "::" 用方法。还是以上面的例子来看,我们再添加几个方法:


@FunctionalInterface


interface?Operation?{


int?operation(int?a,?int?b);


}


interface?Creater<T>?{


T?get();


}


interface?TestInt?{


int?cp(Test?test1,?Test?test2);


}


class?Test?{


public?static?Test?create(Creater<Test>?creater)?{


return?creater.get();


}


private?int?operate(int?a,?int?b,?Operation?operation)?{


return?operation.operation(a,?b);


}


private?static?int?add(int?a,?int?b)?{


return?a?+?b;


}


private?int?sub(int?a,?int?b)?{


return?a?-?b;


}


public?int?testM(Test?test)?{


return?0;


}


public?void?test(TestInt?testInt)?{


Test?t1?=?Test.create(Test::new);?


Test?t2?=?Test.create(Test::new);


testInt.cp(t1,?t2);


}


}


那么对应的方法引用有四种:


构造方法引用


使用方式:Class::new


Test?test?=?Test.create(Test::new);


静态方法引用


使用方式:Class::staticMethod


test.operate(1,?2,?Test::add);


对象的实例方法引用


使用方式:instance::method


test.operate(1,?2,?test::sub);


类的实例方法引用


使用方式:Class::method


test.test(Test::testM);


其实上面三种方法引用都好理解,最后类的实例方法引用,有两个条件:


  • 首先要满足实例方法,而不是静态方法

  • Lambda 表达式的第一个参数会成为调用实例方法的对象


根据这两点我们看上面的例子,test 方法接受一个 TestInt 实例,用 Lambda 表达式表示就是 (Test t1, Test t2) -> res,而我们调用 test 方法时传入的方法引用是 Test::testM,其参数也是一个 Test 实例,最终 test.test(Test::testM) 的调用效果就是 t1.testM(t2)

2.1.3?接口默认方法和静态方法

Java 8 新增了接口的默认实现,通过 default 关键字表示。同时也可以提供静态默认方法。


public?interface?TestInterface?{


String?test();


//?接口默认方法


default?String?defaultTest()?{


return?"default";


}


static?String?staticTest()?{


return?"static";


}


}

2.1.4?重复注解

Java 8 支持了重复注解。在 Java 8 之前想实现重复注解,需要用一些方法来绕过限制。比如下面的代码。


@interface?Author?{


String?name();


}


@interface?Authors?{


Author[]?value();


}


@Authors({@Author(name="a"),?@Author(name?=?"b")})


class?Article?{


}


而在 Java 8 中,可以直接用下面的方式。


@Repeatable(Authors.class)


@interface?Author?{


String?name();


}


@interface?Authors?{


Author[]?value();


}


@Author(name?=?"a")


@Author(name?=?"b")


class?Article?{


}


在解析注解的时候,Java 8 也提供了新的 API。


AnnotatedElement.getAnnotationsByType(Class<T>)

2.1.5. 类型注解

Java 8 之前注解只能用在声明中,在 Java 8 中,注解可以使用在 任何地方。


@Author(name="a")


private?Object?name?=?"";


private?String?author?=?(@Author(name="a")String)?name;

2.1.6. 更好的类型推断

Java 8 对于类型推断做了改进。


比如在 Java 7 中下面的写法:


List<String>?stringList?=?new?ArrayList<>();


stringList.add("A");


stringList.addAll(Arrays.<String>asList());


在 Java 8 中改进后的写法,可以自动做类型推断。


List<String>?stringList?=?new?ArrayList<>();


stringList.add("A");


stringList.addAll(Arrays.asList());

2.1.7. Optional

Java 8 中新增了 Optional 类用来解决空指针异常。Optional 是一个可以保存 null 的容器对象。通过 isPresent() 方法检测值是否存在,通过 get() 方法返回对象。


除此之外,Optional 还提供了很多其他有用的方法,具体可以查看文档。下面是一些示例代码。


//?创建一个?String?类型的容器


Optional<String>?str?=?Optional.of("str");


//?值是否存在


boolean?pre?=?str.isPresent();


//?值如果存在就调用?println?方法,这里传入的是?println?的方法引用


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


//?获取值


String?res?=?str.get();


//?传入空值


str?=?Optional.ofNullable(null);


//?如果值存在,返回值,否则返回传入的参数


res?=?str.orElse("aa");


str?=?Optional.of("str");


//?如果有值,对其调用映射函数得到返回值,对返回值进行?Optional?包装并返回


res?=?str.map(s?->?"aa"?+?s).get();


//?返回一个带有映射函数的?Optional?对象


res?=?str.flatMap(s?->?Optional.of(s?+?"bb")).flatMap(s?->?Optional.of(s?+?"cc")).get();

2.1.8. Stream

Java 8 中新增的 Stream 类提供了一种新的数据处理方式。这种方式将元素集合看做一种流,在管道中传输,经过一系列处理节点,最终输出结果。


关于 Stream 提供的具体方法,可以参照 API。下面是一些示例代码。


List<String>?list?=?Arrays.asList("maa",?"a",?"ab",?"c");


list.stream()


.filter(s?->?s.contains("a"))


.map(s?->?s?+?"aa")


.sorted()


.forEach(System.out::println);


System.out.println("####");


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


List<Integer>?numbers?=?Arrays.asList(1,?2,?3,?4,?5,?6,?7,?8);


int?res?=?numbers.stream().map(i?->?i?+?1).mapToInt(i?->?i).summaryStatistics().getMax();


System.out.println(res);

2.1.9. 日期时间 API

Java 8 中新增了日期时间 API 用来加强对日期时间的处理,其中包括了 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等等,关于 API 可以参照官方文档以及这篇博客,写的很详细。


https://www.cnblogs.com/muscleape/p/9956754.html


下面是示例代码。


LocalDate?now?=?LocalDate.now();


System.out.println(now);


System.out.println(now.getYear());


System.out.println(now.getMonth());


System.out.println(now.getDayOfMonth());


LocalTime?localTime?=?LocalTime.now();


System.out.println(localTime);


LocalDateTime?localDateTime?=?now.atTime(localTime);


System.out.println(localDateTime);

2.1.10. Base64 支持

Java 8 标准库中提供了对 Base 64 编码的支持。具体 API 见可参照文档。下面是示例代码。


String?base64?=?Base64.getEncoder().encodeToString("aaa".getBytes());


System.out.println(base64);


byte[]?bytes?=?Base64.getDecoder().decode(base64);


System.out.println(new?String(bytes));

2.1.11. 并行数组 ParallelSort

Java 8 中提供了对数组的并行操作,包括 parallelSort 等等,具体可参照 API。


Arrays.parallelSort(new?int[]?{1,?2,?3,?4,?5});

2.1.12. 其他新特性

  • 对并发的增强

  • 在 java.util.concurrent.atomic 包中还增加了下面这些类:


DoubleAccumulator


DoubleAdder


LongAccumulator


LongAdder


  • 提供了新的 Nashorn javascript 引擎

  • 提供了 jjs,是一个给予 Nashorn 的命令行工具,可以用来执行 JavaScript 源码

  • 提供了新的类依赖分析工具 jdeps

  • JVM 的新特性


JVM 内存永久区已经被 metaspace 替换(JEP 122)。JVM 参数 -XX:PermSize 和 –XX:MaxPermSize 被 XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize 代替。


可以看到,Java 8 整体上的改进是很大的,最重要的是引入 Lambda 表达式,简化代码。


其他一些改进可参照:


https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html


2.2 Java 9




参考资料


https://www.runoob.com/java/java9-new-features.html


https://www.twle.cn/c/yufei/java9/java9-basic-process-manage-api.html


https://docs.oracle.com/javase/9/whatsnew/toc.htm

2.2.1?Jigsaw 模块系统

在 Java 9 以前,打包和依赖都是基于 JAR 包进行的。JRE 中包含了 rt.jar,将近 63M,也就是说要运行一个简单的 Hello World,也需要依赖这么大的 jar 包。在 Java 9 中提出的模块化系统,对这点进行了改善。


关于模块化系统具体可以看看这篇文章。


https://zhuanlan.zhihu.com/p/24800180

2.2.2?JShell REPL

Java 9 提供了交互式解释器。有了 JShell 以后,Java 终于可以像 Python,Node.js 一样在 Shell 中运行一些代码并直接得出结果了。

2.2.3. 私有接口方法,接口中使用私有方法

Java 9 中可以在接口中定义私有方法。示例代码如下:


public?interface?TestInterface?{


String?test();


//?接口默认方法


default?String?defaultTest()?{


pmethod();


return?"default";


}


private?String?pmethod()?{


System.out.println("private?method?in?interface");


return?"private";


}


}

2.2.4. 集合不可变实例工厂方法

在以前,我们想要创建一个不可变的集合,需要先创建一个可变集合,然后使用 unmodifiableSet 创建不可变集合。代码如下:


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


set.add("A");


set.add("B");


set.add("C");


set?=?Collections.unmodifiableSet(set);


System.out.println(set);


Java 9 中提供了新的 API 用来创建不可变集合。


List<String>?list?=?List.of("A",?"B",?"C");


Set<String>?set?=?Set.of("A",?"B",?"C");


Map<String,?String>?map?=?Map.of("KA",?"VA",?"KB",?"VB");

2.2.5. 改进 try-with-resources

Java 9 中不需要在 try 中额外定义一个变量。Java 9 之前需要这样使用 try-with-resources:


InputStream?inputStream?=?new?StringBufferInputStream("a");


try?(InputStream?in?=?inputStream)?{


in.read();


}?catch?(IOException?e)?{


e.printStackTrace();


}


在 Java 9 中可以直接使用 inputStream 变量,不需要再额外定义新的变量了。


InputStream?inputStream?=?new?StringBufferInputStream("a");


try?(inputStream)?{


inputStream.read();


}?catch?(IOException?e)?{


e.printStackTrace();


}

2.2.6. 多版本兼容 jar 包

Java 9 中支持在同一个 JAR 中维护不同版本的 Java 类和资源。

2.2.7. 增强了 Stream,Optional,Process API

2.2.8. 新增 HTTP2 Client

2.2.9. 增强 Javadoc,增加了 HTML 5 文档的输出,并且增加了搜索功能

2.2.10. 增强 @Deprecated

对 Deprecated 新增了 since 和 forRemoval 属性

2.2.11. 增强了钻石操作符 "<>",可以在 匿名内部类中使用了。

在 Java 9 之前,内部匿名类需要指定泛型类型,如下:


Handler<??extends?Number>?intHandler1?=?new?Handler<Number>(2)?{


}


而在 Java 9 中,可以自动做类型推导,如下:


Handler<??extends?Number>?intHandler1?=?new?Handler<>(2)?{


}

2.2.12. 多分辨率图像 API:定义多分辨率图像 API,开发者可以很容易的操作和展示不同分辨率的图像了。

2.2.13. 改进的 CompletableFuture API

CompletableFuture 类的异步机制可以在 ProcessHandle.onExit 方法退出时执行操作。


其他一些改进可参照:


https://docs.oracle.com/javase/9/whatsnew/toc.htm


2.3 Java 10




参考资料


https://baijiahao.baidu.com/s?id=1594437679552808575&wfr=spider&for=pc


https://blog.csdn.net/visant/article/details/79778967


https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html

2.3.1. 局部变量类型推断

局部变量类型推断可以说是 Java 10 中最值得注意的特性,这是 Java 语言开发人员为了简化 Java 应用程序的编写而采取的又一步,如下图所示。



这个新功能将为 Java 增加一些语法糖 - 简化它并改善开发者体验。新的语法将减少与编写 Java 相关的冗长度,同时保持对静态类型安全性的承诺。


局部变量类型推断将引入"var"关键字,也就是你可以随意定义变量而不必指定变量的类型,如:


将被下面这个新语法所取代:


看完是不是有点 JS 的即视感???越来越像 JS 了吗?!虽然类型推断在 Java 中不是一个新概念,但在局部变量中确是很大的一个改进。


说到类型推断,从 JDK 5 引进泛型,到 JDK 7 的"<>"操作符允许不绑定类型而初始化 List,再到 JDK 8 的 Lambda 表达式,再到现在 JDK 10 的局部变量类型推断,Java 类型推断正大刀阔斧的向前发展。


局部变量类型推荐仅限于如下使用场景:


  • 局部变量初始化

  • for 循环内部索引变量

  • 传统的 for 循环声明变量


Java 官方表示,它不能用于以下几个地方:


  • 方法参数

  • 构造函数参数

  • 方法返回类型

  • 字段捕获表达式(或任何其他类型的变量声明)

2.3.2. GC 改进和内存管理

JDK 10 中有 2 个 JEP 专门用于改进当前的垃圾收集元素。


第一个垃圾收集器接口是(JEP 304),它将引入一个纯净的垃圾收集器接口,以帮助改进不同垃圾收集器的源代码隔离。


预定用于 Java 10 的第二个 JEP 是针对 G1 的并行完全 GC(JEP 307),其重点在于通过完全 GC 并行来改善 G1 最坏情况的等待时间。G1 是 Java 9 中的默认 GC,并且此 JEP 的目标是使 G1 平行。

**2.3.**3. 线程本地握手(JEP 312)

JDK 10 将引入一种在线程上执行回调的新方法,因此这将会很方便能停止单个线程而不是停止全部线程或者一个都不停。

**2.3.**4. 备用内存设备上的堆分配(JEP 316)

允许 HotSpot VM 在备用内存设备上分配 Java 对象堆内存,该内存设备将由用户指定。

**2.3.**5. 其他 Unicode 语言 - 标记扩展(JEP 314)

目标是增强 java.util.Locale 及其相关的 API,以便实现语言标记语法的其他 Unicode 扩展(BCP 47)。

**2.3.**6. 基于 Java 的实验性 JIT 编译器

Oracle 希望将其 Java JIT 编译器 Graal 用作 Linux / x64 平台上的实验性 JIT 编译器。

**2.3.**7. 根证书(JEP 319)

这个的目标是在 Oracle 的 Java SE 中开源根证书。

**2.3.**8. 根证书颁发认证

这将使 OpenJDK 对开发人员更具吸引力,它还旨在减少 OpenJDK 和 Oracle JDK 构建之间的差异。

**2.3.**9. 将 JDK 生态整合单个存储库(JEP 296)

此 JEP 的主要目标是执行一些内存管理,并将 JDK 生态的众多存储库组合到一个存储库中。

**2.3.**10. 删除工具 javah(JEP 313)

从 JDK 中移除了 javah 工具,这个很简单并且很重要。


其他可参考:


https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html#NewFeature


2.4 Java 11




参考资料


https://blog.csdn.net/weixin_38055381/article/details/82865385


https://openjdk.java.net/projects/jdk/11/

2.4.1 本地变量类型推断

什么是局部变量类型推断?


var javastack = "javastack";


System.out.println(javastack);


大家看出来了,局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。


var javastack = "javastack";


就等于:


String javastack = "javastack";

2.4.2 字符串加强

Java 11 增加了一系列的字符串处理方法,如以下所


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享


示。


// 判断字符串是否为空白


" ".isBlank(); // true


// 去除首尾空格


" Javastack ".strip(); // "Javastack"


// 去除尾部空格


" Javastack ".stripTrailing(); // " Javastack"


// 去除首部空格


" Javastack ".stripLeading(); // "Javastack "


// 复制字符串


"Java".repeat(3);// "JavaJavaJava"


// 行数统计


"A\nB\nC".lines().count(); // 3


2.4.3 集合加强


自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。


示例 1:


var list = List.of("Java", "Python", "C");


var copy = List.copyOf(list);


System.out.println(list == copy); // true


示例 2:


var list = new ArrayList<String>();


var copy = List.copyOf(list);


System.out.println(list == copy); // false


示例 1 和 2 代码差不多,为什么一个为 true,一个为 false?


来看下它们的源码:


static <E> List<E> of(E... elements) {


switch (elements.length) { // implicit null check of elements


case 0:


return ImmutableCollections.emptyList();


case 1:


return new ImmutableCollections.List12<>(elements[0]);


case 2:


return new ImmutableCollections.List12<>(elements[0], elements[1]);


default:


return new ImmutableCollections.ListN<>(elements);


}


}


static <E> List<E> copyOf(Collection<? extends E> coll) {


return ImmutableCollections.listCopy(coll);


}


static <E> List<E> listCopy(Collection<? extends E> coll) {


if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {


return (List<E>)coll;


} else {


return (List<E>)List.of(coll.toArray());


}


}


可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。


示例 2 因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为 false.


注意:使用 of 和 copyOf 创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。


上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

2.4.4 Stream 加强

Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。


1) 增加单个参数构造方法,可为 null


Stream.ofNullable(null).count(); // 0


2) 增加 takeWhile 和 dropWhile 方法


Stream.of(1, 2, 3, 2, 1)


.takeWhile(n -> n < 3)


.collect(Collectors.toList()); // [1, 2]


从开始计算,当 n < 3 时就截止。


Stream.of(1, 2, 3, 2, 1)


.dropWhile(n -> n < 3)


.collect(Collectors.toList()); // [3, 2, 1]


这个和上面的相反,一旦 n < 3 不成立就开始计算。


3)iterate 重载


这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。


如果你对 JDK 8 中的 Stream 还不熟悉,可以看之前分享的这一系列教程。

2.4.5 Optional 加强

Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。


Optional.of("javastack").orElseThrow(); // javastack

最后

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到


又是一年求职季,在这里,我为各位准备了一套 Java 程序员精选高频面试笔试真题,来帮助大家攻下 BAT 的 offer,题目范围从初级的 Java 基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考


以下是部分内容截图



本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

用户头像

MySQL神话

关注

还未添加个人签名 2021.11.12 加入

还未添加个人简介

评论

发布
暂无评论
JAVA8之后的版本履历,少儿编程java面试题目