zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java 8 特性 – 终极手册(一)

JAVA 特性 手册 终极
2023-09-11 14:16:10 时间

1.简介

毫无疑问,Java 8是自Java  5(2004年)发布以来Java语言最大的一次版本升级,Java 8带来了很多的新特性,比如编译器、类库、开发工具和JVM(Java虚拟机)。在这篇教程中我们将会学习这些新特性,并通过真实例子演示说明它们适用的场景。

本教程由下面几部分组成,它们分别涉及到Java平台某一特定方面的内容:

运行时(Java虚拟机)

2.Java的新特性

总体来说,Java 8是一个大的版本升级。有人可能会说,Java 8的新特性非常令人期待,但是也要花费大量的时间去学习。这一节我们会讲到这些新特性。

2.1 Lambda表达式和函数式接口

Lambda表达式(也叫做闭包)是Java 8中最大的也是期待已久的变化。它允许我们将一个函数当作方法的参数(传递函数),或者说把代码当作数据,这是每个函数式编程者熟悉的概念。很多基于JVM平台的语言一开始就支持Lambda表达式,但是Java程序员没有选择,只能使用匿名内部类来替代Lambda表达式。

Lambda表达式的设计被讨论了很久,而且花费了很多的功夫来交流。不过最后取得了一个折中的办法,得到了一个新的简明并且紧凑的Lambda表达式结构。最简单的Lambda表达式可以用逗号分隔的参数列表、- 符号和功能语句块来表示。示例如下:

Arrays.asList( "a", "b", "d" ).forEach( e - System.out.println( e ) );

请注意到编译器会根据上下文来推测参数的类型,或者你也可以显示地指定参数类型,只需要将类型包在括号里。举个例子:

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) - System.out.println( e ) );

如果Lambda的功能语句块太复杂,我们可以用大括号包起来,跟普通的Java方法一样,如下:


String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

 ( String e ) - System.out.print( e + separator ) );

Lambda表达式可能会引用类的成员或者局部变量(会被隐式地转变成final类型),下面两种写法的效果是一样的:

String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

 ( String e ) - System.out.print( e + separator ) );

final String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

 ( String e ) - System.out.print( e + separator ) );

Lambda表达式可能会有返回值,编译器会根据上下文推断返回值的类型。如果lambda的语句块只有一行,不需要return关键字。下面两个写法是等价的:

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) - e1.compareTo( e2 ) );

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) - {

 int result = e1.compareTo( e2 );

 return result;

} );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) - {

 int result = e1.compareTo( e2 );

 return result;

} );

语言的设计者们思考了很多如何让现有的功能和lambda表达式友好兼容。于是就有了函数接口这个概念。函数接口是一种只有一个方法的接口,像这样地,函数接口可以隐式地转换成lambda表达式。

java.lang.Runnable 和java.util.concurrent.Callable是函数接口两个最好的例子。但是在实践中,函数接口是非常脆弱的,只要有人在接口里添加多一个方法,那么这个接口就不是函数接口了,就会导致编译失败。Java 8提供了一个特殊的注解@FunctionalInterface来克服上面提到的脆弱性并且显示地表明函数接口的目的(java里所有现存的接口都已经加上了@FunctionalInterface)。让我们看看一个简单的函数接口定义:

@FunctionalInterface

public interface Functional {

 void method();

我们要记住默认的方法和静态方法(下一节会具体解释)不会违反函数接口的约定,例子如下:

@FunctionalInterface

public interface FunctionalDefaultMethods {

 void method();

 default void defaultMethod() {

支持Lambda是Java 8最大的卖点,他有巨大的潜力吸引越来越多的开发人员转到这个开发平台来,并且在纯Java里提供最新的函数式编程的概念。对于更多的细节,请参考官方文档

2.2 接口的默认方法和静态方法

Java 8增加了两个新的概念在接口声明的时候:默认和静态方法。默认方法和Trait有些类似,但是目标不一样。默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法。

默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的实现类都会通过继承得倒这个方法(如果有需要也可以重写这个方法),让我们来看看下面的例子:

private interface Defaulable {

 // Interfaces now allow default methods, the implementer may or

 // may not implement (override) them.

 default String notRequired() {

 return "Default implementation";

private static class DefaultableImpl implements Defaulable {

private static class OverridableImpl implements Defaulable {

 @Override

 public String notRequired() {

 return "Overridden implementation";

接口Defaulable使用default关键字声明了一个默认方法notRequired(),类DefaultableImpl实现了Defaulable接口,没有对默认方法做任何修改。另外一个类OverridableImpl重写类默认实现,提供了自己的实现方法。

Java 8 的另外一个有意思的新特性是接口里可以声明静态方法,并且可以实现。例子如下:

private interface DefaulableFactory {

 // Interfaces now allow static methods

 static Defaulable create( Supplier Defaulable supplier ) {

 return supplier.get();

下面是把接口的静态方法和默认方法放在一起的示例(::new 是构造方法引用,后面会有详细描述):

public static void main( String[] args ) {

 Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );

 System.out.println( defaulable.notRequired() );

 defaulable = DefaulableFactory.create( OverridableImpl::new );

 System.out.println( defaulable.notRequired() );

控制台的输出如下:

Default implementation
Overridden implementation

JVM平台的接口的默认方法实现是很高效的,并且方法调用的字节码指令支持默认方法。默认方法使已经存在的接口可以修改而不会影响编译的过程。java.util.Collection中添加的额外方法就是最好的例子:stream(), parallelStream(), forEach(), removeIf()

虽然默认方法很强大,但是使用之前一定要仔细考虑是不是真的需要使用默认方法,因为在层级很复杂的情况下很容易引起模糊不清甚至变异错误。更多的详细信息请参考官方文档

2.3   方法引用

方法引用提供了一个很有用的语义来直接访问类或者实例的已经存在的方法或者构造方法。结合Lambda表达式,方法引用使语法结构紧凑简明。不需要复杂的引用。

下面我们用Car 这个类来做示例,Car这个类有不同的方法定义。让我们来看看java 8支持的4种方法引用。

public static class Car {

 public static Car create( final Supplier Car supplier ) {

 return supplier.get();

 public static void collide( final Car car ) {

 System.out.println( "Collided " + car.toString() );

 public void follow( final Car another ) {

 System.out.println( "Following the " + another.toString() );

 public void repair() {

 System.out.println( "Repaired " + this.toString() );

第一种方法引用是构造方法引用,语法是:Class::new ,对于泛型来说语法是:Class T ::new,请注意构造方法没有参数:

final Car car = Car.create( Car::new );

final List Car cars = Arrays.asList( car );

第二种方法引用是静态方法引用,语法是:Class::static_method请注意这个静态方法只支持一个类型为Car的参数。

cars.forEach( Car::collide );

第三种方法引用是类实例的方法引用,语法是:Class::method请注意方法没有参数。

cars.forEach( Car::repair );

最后一种方法引用是引用特殊类的方法,语法是:instance::method,请注意只接受Car类型的一个参数。

final Car police = Car.create( Car::new );

cars.forEach( police::follow );

运行这些例子我们将会在控制台得到如下信息(Car的实例可能会不一样): 

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

关于方法引用更多的示例和详细信息,请参考官方文档

2.4   重复注释

自从Java 5支持注释以来,注释变得特别受欢迎因而被广泛使用。但是有一个限制,同一个地方的不能使用同一个注释超过一次。 Java 8打破了这个规则,引入了重复注释,允许相同注释在声明使用的时候重复使用超过一次。 

重复注释本身需要被@Repeatable注释。实际上,他不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。让我们来看看例子:

package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;

import java.lang.annotation.Repeatable;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

public class RepeatingAnnotations {

 @Target( ElementType.TYPE )

 @Retention( RetentionPolicy.RUNTIME )

 public @interface Filters {

 Filter[] value();

 @Target( ElementType.TYPE )

 @Retention( RetentionPolicy.RUNTIME )

 @Repeatable( Filters.class )

 public @interface Filter {

 String value();

 @Filter( "filter1" )

 @Filter( "filter2" )

 public interface Filterable {

 public static void main(String[] args) {

 for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {

 System.out.println( filter.value() );

我们可以看到,注释Filter被@Repeatable( Filters.class )注释。Filters 只是一个容器,它持有Filter, 编译器尽力向程序员隐藏它的存在。通过这样的方式,Filterable接口可以被Filter注释两次。

另外,反射的API提供一个新方法getAnnotationsByType() 来返回重复注释的类型(请注意Filterable.class.getAnnotation( Filters.class )将会返回编译器注入的Filters实例)。

程序的输出将会是这样:

filter1
filter2

更多详细信息请参考官方文档

2.5   更好的类型推断

Java 8在类型推断方面改进了很多,在很多情况下,编译器可以推断参数的类型,从而保持代码的整洁。让我们看看例子:

package com.javacodegeeks.java8.type.inference;

package com.javacodegeeks.java8.type.inference;

public class Value T {

 public static T T defaultValue() {

 return null;

 public T getOrDefault( T value, T defaultValue ) {

 return ( value != null ) ? value : defaultValue;

这里是Value String 的用法

package com.javacodegeeks.java8.type.inference;

public class TypeInference {

 public static void main(String[] args) {

 final Value String value = new Value ();

 value.getOrDefault( "22", Value.defaultValue() );

参数Value.defaultValue()的类型被编译器推断出来,不需要显式地提供类型。在java 7, 相同的代码不会被编译,需要写成:Value. String defaultValue()

2.6   注解的扩展

Java 8扩展了注解可以使用的范围,现在我们几乎可以在所有的地方:局部变量、泛型、超类和接口实现、甚至是方法的Exception声明。一些例子如下:

package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

import java.util.ArrayList;

import java.util.Collection;

public class Annotations {

 @Retention( RetentionPolicy.RUNTIME )

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

 public @interface NonEmpty {

 public static class Holder @NonEmpty T extends @NonEmpty Object {

 public void method() throws @NonEmpty Exception {

 @SuppressWarnings( "unused" )

 public static void main(String[] args) {

 final Holder String holder = new @NonEmpty Holder String 

 @NonEmpty Collection @NonEmpty String strings = new ArrayList ();

Java 8 新增加了两个注解的程序元素类型ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER ,这两个新类型描述了可以使用注解的新场合。注解处理API(Annotation Processing API)也做了一些细微的改动,来识别这些新添加的注解类型。

3.Java编译器的新特性

3.1 参数名字

很长时间以来,Java程序员想尽办法把参数名字保存在java字节码里,并且让这些参数名字在运行时可用。Java 8 终于把这个需求加入到了Java语言(使用反射API和Parameter.getName() 方法)和字节码里(使用java编译命令javac的–parameters参数)。

package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;

import java.lang.reflect.Parameter;

public class ParameterNames {

public static void main(String[] args) throws Exception {

Method method = ParameterNames.class.getMethod( "main", String[].class );

for( final Parameter parameter: method.getParameters() ) {

System.out.println( "Parameter: " + parameter.getName() );

如果你编译这个class的时候没有添加参数–parameters,运行的时候你会得到这个结果:

Parameter: arg0

编译的时候添加了–parameters参数的话,运行结果会不一样:

Parameter: args

对于有经验的Maven使用者,–parameters参数可以添加到maven-compiler-plugin的配置部分:

plugin
groupId org.apache.maven.plugins /groupId
artifactId maven-compiler-plugin /artifactId
version 3.1 /version
configuration
compilerArgument -parameters /compilerArgument
source 1.8 /source
target 1.8 /target
/configuration
/plugin
最新版的Eclipse Kepler SR2 提供了编译设置项,如下图所示:

01.ECLIPSE-JAVA-COMPILER

Picture 1. Configuring Eclipse projects to support new Java 8 compiler –parameters argument.

额外的,有一个方便的方法Parameter.isNamePresent() 来验证参数名是不是可用。

 

4.Java  库的新特性

Java 8 新添加了很多类,并且扩展了很多现有的类来更好地支持现代并发、函数式编程、日期\时间等等。

4.1 Optional

著名的NullPointerException 是引起系统失败最常见的原因。很久以前Google Guava项目引入了Optional作为解决空指针异常的一种方式,不赞成代码被null检查的代码污染,期望程序员写整洁的代码。受Google Guava的鼓励,Optional 现在是Java 8库的一部分。

Optional 只是一个容器,它可以保存一些类型的值或者null。它提供很多有用的方法,所以没有理由不显式地检查null。请参照java 8的文档查看详细信息。

让我们看看两个Optional 用法的小例子:一个是允许为空的值,另外一个是不允许为空的值。

Optional String fullName = Optional.ofNullable( null );

System.out.println( "Full Name is set? " + fullName.isPresent() ); 

System.out.println( "Full Name: " + fullName.orElseGet( () - "[none]" ) ); 

System.out.println( fullName.map( s - "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

如果Optional实例有非空的值,方法 isPresent() 返回true否则返回false。方法orElseGet提供了回退机制,当Optional的值为空时接受一个方法返回默认值。map()方法转化Optional当前的值并且返回一个新的Optional实例。orElse方法和orElseGet类似,但是它不接受一个方法,而是接受一个默认值。上面代码运行结果如下:

Full Name is set? false
Full Name: [none]
Hey Stranger!

让我们大概看看另外一个例子。

Optional String firstName = Optional.of( "Tom" );

System.out.println( "First Name is set? " + firstName.isPresent() ); 

System.out.println( "First Name: " + firstName.orElseGet( () - "[none]" ) ); 

System.out.println( firstName.map( s - "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

System.out.println();

输出如下:

First Name is set? true
First Name: Tom
Hey Tom!

更多详细信息请参考官方文档

转载自 并发编程网 - ifeve.com
阿里P8熬了一个月肝出这份32W字Java面试手册,在Github标星31K+ 互联网行业竞争越来越严峻,面试也是越来越难,一直以来我都想整理一套完美的面试宝典,奈何难抽出时间,这套1000+道的Java面试手册我整理了整整1个月,上传到Git上目前star数达到了30K+ 这套互联网Java工程师面试题包括了:MyBatis、ZK、Dubbo、EL、Redis、MySQL、并发编程、Java面试、Spring、微服务、Linux、Springboot、SpringCloud、MQ、Kafka面试专题(共485页,32W字)
阿里藏经阁天花板:高性能Java架构核心原理手册,一定要偷偷看 市面上讲Java框架的书很多,包括Sping Boot、Spring Cloud、Kafka等,但这些书通常只会让你技术的“量”增长,而“质”仍处于SSM的阶段。而且互联网上并没有体系化、结构化的提升技术的“质”的教材,于是这份阿里的高性能java架构核心手册就出来了!
GitHub上这份阿里的Java高并发核心手册,即使再过20年依然“NB” 什么是高并发? 高并发(High Concurrency)通常是指通过设计保证系统能够同时并行处理很多请求。 通俗来讲,高并发是指在同一个时间点,有很多用户同时访问同一 API 接口或者 Url 地址。它经常会发生在有大活跃用户量,用户高聚集的业务场景中。 今天给大家分享一份由一位阿里大牛亲自操刀写出来的一份:Java高并发核心编程手册,号称即使再过20年这份资料依然不会被淘汰!
京东三面(后端)凭借这份Java面试复盘手册,已斩获60K*15offer 京东提前批一面: 简单的自我介绍 看我的项目取名叫“高级”,问我项目中怎么体现高级的(直接被人蒙了,瞎说了一通,把面试都逗笑了)? 怎么理解springboot、mybatis等框架的 如果没有mybatis,该怎么办(回答了用JDBC)那么mybatis和JDBC的区别是什么,为什么优先用mybatis mybatis是怎么操作数据库的,有哪些参数 你项目里用了kafka,怎么用的?只会用Kafka吗,用过其他的消息队列吗(我说用过blockqeue) ? 你项目里用到redis了,运用到了哪些场景?两大模块三大改进
《Java工程师必读手册》电子版地址 工匠追求“术”到极致,其实就是在寻“道”,且离悟“道”也就不远了,亦或是已经得道,这就是“工匠精神”——一种追求“以术得道”的精神。 如果一个工匠只满足于“术”,不能追求“术”到极致去悟“道”,那只是一个靠“术”养家糊口的工匠而已。作者根据多年来的实践探索,总结了大量的Java编码之“术”,试图阐述出心中的Java编码之“道”。
终于有清华大佬深入计算机底层总结出这份图解Java底层/网络手册 计算机(computer)俗称电脑,是现代一种用于高速计算的电子计算机器,可以进行数值计算,又可以进行逻辑计算,还具有存储记忆功能。是能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。 由硬件系统和软件系统所组成,没有安装任何软件的计算机称为裸机。可分为超级计算机、工业控制计算机、网络计算机、个人计算机、嵌入式计算机五类,较先进的计算机有生物计算机、光子计算机、量子计算机等。 从最初由约翰·冯·诺依曼发明之后经过代代的不断改良和迭代增加到现在已经发展了几十年之久,计算机可以说是20世纪最先进的科学发展之一
让GitHub低头认错的这份阿里内部绝密Java面试八股文手册有多强? 临近年关,相信很多小伙伴都无心工作了吧!今天给大家分享出一份让大家上班摸鱼也可以随意看的阿里巴巴内部特供Java面试八股文手册,这份手册据说曾经也是让GitHub都为之低头的存在!