java8新特性

http://www.importnew.com/19345.html
本文总结自这篇神作,用来供自己复习学习

java8的新特性

1、lambda表达式和函数接口

(1)lambda表达式允许一个函数作为方法的参数

Array.asList("a","b","c").forEach(e->System.out.println(e));

编译器会根据上下文来推断参数的类型

(2)或者我们可以指定参数的类型

Array.asList("a","b","c").forEach((String e)->System.out.println(e));

(3)lambda表达式可能会引用局部变量或者成员变量,这变量会被隐式转换成final类型

String s = ",";
Array.asList("a","b","c").forEach((String e)->System.out.println(e+s));

会变成

final String s = ",";
Array.asList("a","b","c").forEach((String e)->System.out.println(e+s));

(4)lambda表达式可能会有返回值,编译器会通过上下文判断返回值的类型,我们也可以显式地规定返回值

Array.asList("a","b","c").sort((e1,e2)->e1.compareTo(e2));
Array.asList("a","b","c").sort((e1,e2)->{
	int result = e1.compareTo(e2);
	return result;
});

函数接口是一种只有一个方法的接口,像这样地,函数接口可以隐式地转换成lambda表达式。

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

@FunctionalInterface
public interface Functional {
    void method();
}
2、接口多了默认方法和静态方法

接口可以写默认方法和静态方法,且都可以写实现
接口中只能有一个普通方法,但是可以有多个默认方法来为接口提供实现
注意,实现接口的类或者子接口不会继承接口中的静态方法。static不能和default同时使用。

3、方法引用

java8提供了新的方法引用方式
方法引用的标准形式是:类名::方法名

类型示例
引用静态方法ContainingClass::staticMethodName
引用某个对象的实例方法containingObject::instanceMethodName
引用某个类型的任意对象的实例方法ContainingType::methodName
引用构造方法ClassName::new
4、重复注解
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() );
        }
    }
}
5、更好的类型推断

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

ackage 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;
    }
}
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()

6、注解的扩展

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

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

7、参数名字

很长时间以来,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() );
		}
	}	
}
8、Optional

Optional 只是一个容器,它可以保存一些类型的值或者null。它提供很多有用的方法,所以没有理由不显式地检查null。

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类是如何应对null的
isPresent()方法如果存在值则返回true,否则返回false,在上面这个例子中是返回false的
因此第一句结果应该是:Full Name is set?false
orElseGet()提供了回退机制,如果接受一个值为空的时候返回默认值
因此第二句结果应该是:Full Name:[none]
orElse()其实跟orElseGet()差不多,但是他不是接受一个方法函数,而是接受一个值,第三句的意思是,如果fullName值存在输出"Hey {fullName}!",否则输出后面的"Hey Stranger!"
因此第三句结果应该是: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();

与上面不同的是,Optional容器中是有值的
因此结果如下:
First Name is set? true
First Name: Tom
Hey Tom!

9、Stream

新增加的Stream API (java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加,希望程序员通过编写有效、整洁和简明的代码,能够大大提高生产率。

Stream API让集合处理简化了很多(我们后面会看到不仅限于Java集合类)。让我们从一个简单的类Task开始来看看Stream的用法。

public class Streams {
	private enum Status {
		OPEN, CLOSED
	};
 
	private static final class Task {
		private final Status status;
		private final Integer points;
 
		Task( final Status status, final Integer points ) {
			this.status = status;
			this.points = points;
		}
 
		public Integer getPoints() {
			return points;
		}
 
		public Status getStatus() {
			return status;
		}
 
		@Override
		public String toString() {
			return String.format( "[%s, %d]", status, points );
		}
	}
}

假设有这么一个集合

final Collection< Task > tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 ) 
);

问题1,我想求所有开放的task的点数,可以这么做

final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();
 
System.out.println( "Total points: " + totalPointsOfOpenTasks );

stream操作被分为中间操作和终点操作。

中间操作返回一个新的Stream。这些中间操作是延迟的,执行一个中间操作比如filter实际上不会真的做过滤操作,而是创建一个新的Stream,当这个新的Stream被遍历的时候,它里头会包含有原来Stream里符合过滤条件的元素。

终点操作比如说forEach或者sum会遍历Stream从而产生最终结果或附带结果。终点操作执行完之后,Stream管道就被消费完了,不再可用。在几乎所有的情况下,终点操作都是即时完成对数据的遍历操作。

Stream的另外一个价值是Stream创造性地支持并行处理。例如我们需要把所有点数加起来

final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints ) 
   .reduce( 0, Integer::sum );
 
System.out.println( "Total points (all tasks): " + totalPoints );

经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求

final Map< Status, List< Task > > map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );

又比如,计算整个集合中每个task分数(或权重)的平均值

final Collection< String > result = tasks
    .stream()                                        // Stream< String >
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream< Double >
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream< String> 
    .collect( Collectors.toList() );                 // List< String > 
 
System.out.println( result );

最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
9、日期时间API

Java 8引入了新的日期时间API(JSR 310)改进了日期时间的管理。日期和时间管理一直是Java开发人员最痛苦的问题。java.util.Date和后来的java.util.Calendar一点也没有改变这个情况(甚至让人们更加迷茫)。

因为上面这些原因,产生了Joda-Time ,可以替换Java的日期时间API。Joda-Time深刻影响了 Java 8新的日期时间API,Java 8吸收了Joda-Time 的精华。新的java.time包包含了所有关于日期、时间、日期时间、时区、Instant(跟日期类似但精确到纳秒)、duration(持续时间)和时钟操作的类。设计这些API的时候很认真地考虑了这些类的不变性(从java.util.Calendar吸取的痛苦教训)。如果需要修改时间对象,会返回一个新的实例。

10、Base64

对Base64的支持最终成了Java 8标准库的一部分,非常简单易用

package com.javacodegeeks.java8.base64;
 
import java.nio.charset.StandardCharsets;
import java.util.Base64;
 
public class Base64s {
	public static void main(String[] args) {
		final String text = "Base64 finally in Java 8!";
 
		final String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8 ) );
		System.out.println( encoded );
 
		final String decoded = new String(
		Base64.getDecoder().decode( encoded ),
		StandardCharsets.UTF_8 );
		System.out.println( decoded );
	}
}
11、并行数组

java 8新增加了很多方法支持并行的数组处理。最重要的大概是parallelSort()这个方法显著地使排序在多核计算机上速度加快。

12、并发

在新增Stream机制与lambda的基础之上,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法来支持聚集操作。同时也在java.util.concurrent.ForkJoinPool类中加入了一些新方法来支持共有资源池(common pool)。

新增的java.util.concurrent.locks.StampedLock类提供一直基于容量的锁,这种锁有三个模型来控制读写操作(它被认为是不太有名的java.util.concurrent.locks.ReadWriteLock类的替代者)。

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

DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值