JDK1.8新特性总结 Day37 2018-12-27
JDK1.8新特性总结
一、十大特性
- 1.
default
关键字 - 2.
Lambda
表达式 - 3.接口新增:默认方法与静态方法
- 4.方法引用,与
Lambda
表达式联合使用 - 5.引入重复注解
- 6.类型注解
- 7.
Stream
函数式操作流元素集合 - 8.最新的
Date/Time API (JSR 310)
- 9.新增
base64
加解密API
- 10.数组并行(
parallel
)操作
- 1.
JVM
的PermGen
空间被移除:取代它的是Metaspace(JEP 122)
元空间
二、新特性
1、default关键字
在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default
,通过使用default
修饰方法,可以让我们在接口里面定义具体的方法实现,如下。
public interface NewCharacter {
public void test1();
public default void test2(){
System.out.println("我是新特性1");
}
}
那这么定义一个方法的作用是什么呢?为什么不在接口的实现类里面再去实现方法呢?
其实这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default
修饰的方法,也可以直接调用,如下。
public class NewCharacterImpl implements NewCharacter{
@Override
public void test1() {
}
public static void main(String[] args) {
NewCharacter nca = new NewCharacterImpl();
nca.test2();
}
}
所以说这个default
方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk
的集合List
里面增加了一个sort
方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList
等都需要对其添加实现,那么现在用default
定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。
2、Lambda 表达式
2.1、 Lambda表达式的写法
Lambda表达式是jdk1.8里面的一个重要的更新,这意味着java也开始承认了函数式编程,并且尝试引入其中。
-
Lambda 表达式
junit
测试代码
public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
/**
* 1.Lambda表达式
*/
@Test
public void testLambda(){
list.forEach(System.out::println);
list.forEach(e -> System.out.println("方式二:"+e));
}
首先,什么是函数式编程,引用廖雪峰先生的教程里面的解释就是说:
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
简单的来说就是,函数式编程语言里面函数也可以跟变量,对象一样使用了,也就是说函数既可以作为参数,也可以作为返回值了,看一下下面这个例子。
//这是常规的Collections的排序的写法,需要对接口方法重写
public void test1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
for (String string : list) {
System.out.println(string);
}
}
//这是带参数类型的Lambda的写法
public void testLamda1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
return b.compareTo(a);
}
);
for (String string : list) {
System.out.println(string);
}
}
//这是不带参数的lambda的写法
public void testLamda2(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (a,b)->b.compareTo(a)
);
for (String string : list) {
System.out.println(string);
}
可以看到不带参数的写法一句话就搞定了排序的问题,所以引入lambda表达式的一个最直观的作用就是大大的简化了代码的开发,像其他一些编程语言Scala,Python等都是支持函数式的写法的。当然,不是所有的接口都可以通过这种方法来调用,只有函数式接口才行,jdk1.8里面定义了好多个函数式接口,我们也可以自己定义一个来调用,下面说一下什么是函数式接口。
2.2、函数式接口
定义:“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。jdk1.8提供了一个@FunctionalInterface
注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。
@FunctionalInterface
public interface MyLamda {
public void test1(String y);
//这里如果继续加一个抽象方法便会报错
// public void test1();
//default方法可以任意定义
default String test2(){
return "123";
}
default String test3(){
return "123";
}
//static方法也可以定义
static void test4(){
System.out.println("234");
}
}
看一下这个接口的调用,符合lambda表达式的调用方法。
MyLamda m = y -> System.out.println("ss"+y);
2.3、方法与构造函数引用
jdk1.8提供了另外一种调用方式::
,当 你 需 要使用 方 法 引用时 , 目 标引用 放 在 分隔符::
前 ,方法 的 名 称放在 后 面 ,即ClassName :: methodName
。例如 ,Apple::getWeight
就是引用了Apple类中定义的方法getWeight。请记住,不需要括号,因为你没有实际调用这个方法。方法引用就是Lambda表达式(Apple a) -> a.getWeight()
的快捷写法,如下示例。
//先定义一个函数式接口
@FunctionalInterface
public interface TestConverT<T, F> {
F convert(T t);
}
测试如下,可以以::
形式调用。
public void test(){
TestConverT<String, Integer> t = Integer::valueOf;
Integer i = t.convert("111");
System.out.println(i);
}
此外,对于构造方法也可以这么调用。
//实体类User和它的构造方法
public class User {
private String name;
private String sex;
public User(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
}
//User工厂
public interface UserFactory {
User get(String name, String sex);
}
//测试类
UserFactory uf = User::new;
User u = uf.get("ww", "man");
这里的User::new
就是调用了User
的构造方法,Java编译器会自动根据UserFactory.get
方法的签名来选择合适的构造函数。
-
方法引用,与Lambda表达式联合使用
junit
测试代码
/**
* 4.方法引用,与Lambda表达式联合使用
*/
@Test
public void testMethodReference(){
//构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
cars.forEach( Car::collide );
//任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
cars.forEach( Car::repair );
//特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
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( "静态方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意对象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定对象的方法引用 " + car.toString() );
}
}
2.4、局部变量限制
Lambda
表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获Lambda
。 Lambda
可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final
,或事实上是final
。
为什么局部变量有这些限制?
(1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda
可以直接访问局部变量,而且Lambda
是在一个线程中使用的,则使用Lambda
的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
(2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2);
3.接口新增:默认方法与静态方法
/**
* 3.接口新增:默认方法与静态方法
* default 接口默认实现方法是为了让集合类默认实现这些函数式处理,而不用修改现有代码
* (List继承于Iterable<T>,接口默认方法不必须实现default forEach方法)
*/
@Test
public void testDefaultFunctionInterface(){
//可以直接使用接口名.静态方法来访问接口中的静态方法
JDK8Interface1.staticMethod();
//接口中的默认方法必须通过它的实现类来调用
new JDK8InterfaceImpl1().defaultMethod();
//多实现类,默认方法重名时必须复写
new JDK8InterfaceImpl2().defaultMethod();
}
public class JDK8InterfaceImpl1 implements JDK8Interface1 {
//实现接口后,因为默认方法不是抽象方法,重写/不重写都成!
// @Override
// public void defaultMethod(){
// System.out.println("接口中的默认方法");
// }
}
public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
//实现接口后,默认方法名相同,必须复写默认方法
@Override
public void defaultMethod() {
//接口的
JDK8Interface1.super.defaultMethod();
System.out.println("实现类复写重名默认方法!!!!");
}
}
public interface JDK8Interface1 {
//1.接口中可以定义静态方法了
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
//2.使用default之后就可以定义普通方法的方法体了
public default void defaultMethod(){
System.out.println("接口中的默认方法");
}
}
public interface JDK8Interface2 {
//1.接口中可以定义静态方法了
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
//2.使用default之后就可以定义普通方法的方法体了
public default void defaultMethod(){
System.out.println("接口中的默认方法");
}
}
4.方法引用,与Lambda表达式联合使用
/**
* 4.方法引用,与Lambda表达式联合使用
*/
@Test
public void testMethodReference(){
//构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
cars.forEach( Car::collide );
//任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
cars.forEach( Car::repair );
//特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
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( "静态方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意对象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定对象的方法引用 " + car.toString() );
}
}
5.引入重复注解
/**
* 5.引入重复注解
* 1.@Repeatable
* 2.可以不用以前的“注解容器”写法,直接写2次相同注解即可
*
* Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
*/
@Test
public void RepeatingAnnotations(){
RepeatingAnnotations.main(null);
}
package com.zgldoing.base.jdk8Features;
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;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
/**
* @ClassName:RepeatingAnnotations
* @Description:重复注解@Repeatable
* @author zgldo
*
*/
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();
String value2();
};
@Filter( value="filter1",value2="111" )
@Filter( value="filter2", value2="222")
//@Filters({@Filter( value="filter1",value2="111" ),@Filter( value="filter2", value2="222")}).注意:JDK8之前:1.没有@Repeatable2.采用本行“注解容器”写法
public interface Filterable {
}
public static void main(String[] args) {
//获取注解后遍历打印值
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() +filter.value2());
}
}
}
6.类型注解
/**
* 6.类型注解
* 新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
*
*/
@Test
public void ElementType(){
Annotations.main(null);
}
package com.zgldoing.base.jdk8Features;
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;
import com.zgldoing.base.jdk8Features.Annotations.Holder;
import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
/**
* @ClassName:Annotations
* @Description:新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
* @author zgldo
*
*/
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 {
}
}
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}
}
7、Stream
函数式操作流元素
定义:流是Java API
的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。就现在来说,我们可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,也就是说我们不用写多线程代码了。
Stream
不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator
。原始版本的 Iterator
,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream
,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream
会隐式地在内部进行遍历,做出相应的数据转换。
Stream
就如同一个迭代器(Iterator
),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream
可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item
读完后再读下一个item
。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream
的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)
来拆分任务和加速处理过程。
流的操作类型分为两种:
-
Intermediate
:一个流可以后面跟随零个或多个intermediate
操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy
),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。 -
Terminal
:一个流只能有一个terminal
操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal
操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect
。
在对于一个Stream
进行多次转换操作 (Intermediate
操作),每次都对 Stream
的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
构造流的几种方式
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
- Stream函数式操作流元素集合
/**
* 2.Stream函数式操作流元素集合
*/
@Test
public void testStream(){
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("求和:"+nums
.stream()//转成Stream
.filter(team -> team!=null)//过滤
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳过前2个元素
.limit(4)//限制取前4个元素
.peek(System.out::println)//流式处理对象函数
.sum());//
}
8、Date API更新
1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang
包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time
包下。比原来实用了很多。
8.1 LocalDate/LocalTime/LocalDateTime
LocalDate
为日期处理类、LocalTime
为时间处理类、LocalDateTime
为日期时间处理类,方法都类似,具体可以看API文档或源码,选取几个代表性的方法做下介绍。
now
相关的方法可以获取当前日期或时间,of
方法可以创建对应的日期或时间,parse
方法可以解析日期或时间,get
方法可以获取日期或时间信息,with
方法可以设置日期或时间信息,plus
或minus
方法可以增减日期或时间信息;
8.2 TemporalAdjusters
这个类在日期调整时非常有用,比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
8.3 DateTimeFormatter
以前日期格式化一般用SimpleDateFormat
类,但是不怎么好用,现在1.8引入了DateTimeFormatter
类,默认定义了很多常量格式(ISO打头的),在使用的时候一般配合LocalDate/LocalTime/LocalDateTime
使用,比如想把当前日期格式化成yyyy-MM-dd hh:mm:ss
的形式:
LocalDateTime dt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(dtf.format(dt));
-
Date/Time API
junit
测试代码
/**
* 7.最新的Date/Time API (JSR 310)
* UTC即为世界标准时间,世界统一时间,国际协调时间,字母T是时间和日期的分隔符,T后面表示的即为时间,末尾的z表示UTC统一时间。
*/
@Test
public void DateTime(){
//1.Clock
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
System.out.println( clock.millis() );//1545917118662
//2. ISO-8601格式且无时区信息的日期部分
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );//2018-12-27
System.out.println( dateFromClock );//2018-12-27
// ISO-8601格式且无时区信息的时间部分
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time ); //21:25:18.692
System.out.println( timeFromClock );//13:25:18.692
// 3.ISO-8601格式无时区信息的日期与时间
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime ); //2018-12-27T21:25:18.692
System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
// 4.特定时区的日期/时间,
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime ); //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
//5.在秒与纳秒级别上的一段时间
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() ); //Duration in days: 365
System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
}
9.新增base64加解密API
/**
* 8.新增base64加解密API
*/
@Test
public void testBase64(){
final String text = "就是要测试加解密!!abjdkhdkuasu!!@@@@";
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 );
}
10.数组并行(parallel)操作
/**
* 9.数组并行(parallel)操作
*/
@Test
public void testParallel(){
long[] arrayOfLong = new long [ 20000 ];
//1.给数组随机赋值
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
//2.打印出前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
//3.数组排序
Arrays.parallelSort( arrayOfLong );
//4.打印排序后的前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
11.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
/**
* 10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
*/
@Test
public void testMetaspace(){
//-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
//-XX:MaxMetaspaceSize最大空间,默认是没有限制
//-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
//-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
}
三、总结
总的来说,jdk1.8的一些新特性主要还是简化了代码的写法,减少了部分开发量,但是需要一些时间来熟悉。很惭愧,1.8的新特性还不是很熟悉,所以还是要继续努力,还需要多看些源码。
四、附录 junit
测试代码
1、 JDK8_features.java
package com.zgldoing.base.jdk8Features;
import static org.junit.jupiter.api.Assertions.*;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import com.google.common.collect.Lists;
/**
*
* @ClassName:JDK8_features
* @Description:JDK8新特性
*
*/
public class JDK8_features {
public List<Integer> list = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
/**
* 1.Lambda表达式
*/
@Test
public void testLambda(){
list.forEach(System.out::println);
list.forEach(e -> System.out.println("方式二:"+e));
}
/**
* 2.Stream函数式操作流元素集合
*/
@Test
public void testStream(){
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("求和:"+nums
.stream()//转成Stream
.filter(team -> team!=null)//过滤
.distinct()//去重
.mapToInt(num->num*2)//map操作
.skip(2)//跳过前2个元素
.limit(4)//限制取前4个元素
.peek(System.out::println)//流式处理对象函数
.sum());//
}
/**
* 3.接口新增:默认方法与静态方法
* default 接口默认实现方法是为了让集合类默认实现这些函数式处理,而不用修改现有代码
* (List继承于Iterable<T>,接口默认方法不必须实现default forEach方法)
*/
@Test
public void testDefaultFunctionInterface(){
//可以直接使用接口名.静态方法来访问接口中的静态方法
JDK8Interface1.staticMethod();
//接口中的默认方法必须通过它的实现类来调用
new JDK8InterfaceImpl1().defaultMethod();
//多实现类,默认方法重名时必须复写
new JDK8InterfaceImpl2().defaultMethod();
}
public class JDK8InterfaceImpl1 implements JDK8Interface1 {
//实现接口后,因为默认方法不是抽象方法,重写/不重写都成!
// @Override
// public void defaultMethod(){
// System.out.println("接口中的默认方法");
// }
}
public class JDK8InterfaceImpl2 implements JDK8Interface1,JDK8Interface2 {
//实现接口后,默认方法名相同,必须复写默认方法
@Override
public void defaultMethod() {
//接口的
JDK8Interface1.super.defaultMethod();
System.out.println("实现类复写重名默认方法!!!!");
}
}
public interface JDK8Interface1 {
//1.接口中可以定义静态方法了
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
//2.使用default之后就可以定义普通方法的方法体了
public default void defaultMethod(){
System.out.println("接口中的默认方法");
}
}
public interface JDK8Interface2 {
//1.接口中可以定义静态方法了
public static void staticMethod(){
System.out.println("接口中的静态方法");
}
//2.使用default之后就可以定义普通方法的方法体了
public default void defaultMethod(){
System.out.println("接口中的默认方法");
}
}
/**
* 4.方法引用,与Lambda表达式联合使用
*/
@Test
public void testMethodReference(){
//构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
//静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
cars.forEach( Car::collide );
//任意对象的方法引用。它的语法是Class::method。无参,所有元素调用;
cars.forEach( Car::repair );
//特定对象的方法引用,它的语法是instance::method。有参,在某个对象上调用方法,将列表元素作为参数传入;
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
}
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( "静态方法引用 " + car.toString() );
}
public void repair() {
System.out.println( "任意对象的方法引用 " + this.toString() );
}
public void follow( final Car car ) {
System.out.println( "特定对象的方法引用 " + car.toString() );
}
}
/**
* 5.引入重复注解
* 1.@Repeatable
* 2.可以不用以前的“注解容器”写法,直接写2次相同注解即可
*
* Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
*/
@Test
public void RepeatingAnnotations(){
RepeatingAnnotations.main(null);
}
/**
* 6.类型注解
* 新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
*
*/
@Test
public void ElementType(){
Annotations.main(null);
}
/**
* 7.最新的Date/Time API (JSR 310)
* UTC即为世界标准时间,世界统一时间,国际协调时间,字母T是时间和日期的分隔符,T后面表示的即为时间,末尾的z表示UTC统一时间。
*/
@Test
public void DateTime(){
//1.Clock
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );//2018-12-27T13:25:18.636Z
System.out.println( clock.millis() );//1545917118662
//2. ISO-8601格式且无时区信息的日期部分
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );
System.out.println( date );//2018-12-27
System.out.println( dateFromClock );//2018-12-27
// ISO-8601格式且无时区信息的时间部分
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );
System.out.println( time ); //21:25:18.692
System.out.println( timeFromClock );//13:25:18.692
// 3.ISO-8601格式无时区信息的日期与时间
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );
System.out.println( datetime ); //2018-12-27T21:25:18.692
System.out.println( datetimeFromClock );//2018-12-27T13:25:18.692
// 4.特定时区的日期/时间,
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );
System.out.println( zonedDatetime ); //2018-12-27T21:25:18.694+08:00[Asia/Shanghai]
System.out.println( zonedDatetimeFromClock );//2018-12-27T13:25:18.694Z
System.out.println( zonedDatetimeFromZone ); //2018-12-27T05:25:18.706-08:00[America/Los_Angeles]
//5.在秒与纳秒级别上的一段时间
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );
final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() ); //Duration in days: 365
System.out.println( "Duration in hours: " + duration.toHours() );//Duration in hours: 8783
}
/**
* 8.新增base64加解密API
*/
@Test
public void testBase64(){
final String text = "就是要测试加解密!!abjdkhdkuasu!!@@@@";
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 );
}
/**
* 9.数组并行(parallel)操作
*/
@Test
public void testParallel(){
long[] arrayOfLong = new long [ 20000 ];
//1.给数组随机赋值
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
//2.打印出前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
//3.数组排序
Arrays.parallelSort( arrayOfLong );
//4.打印排序后的前10个元素
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
/**
* 10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
*/
@Test
public void testMetaspace(){
//-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整
//-XX:MaxMetaspaceSize最大空间,默认是没有限制
//-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
//-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
}
}
2、重复注解@Repeatable
package com.zgldoing.base.jdk8Features;
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;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filter;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filterable;
import com.zgldoing.base.jdk8Features.RepeatingAnnotations.Filters;
/**
* @ClassName:RepeatingAnnotations
* @Description:重复注解@Repeatable
* @author zgldo
*
*/
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();
String value2();
};
@Filter( value="filter1",value2="111" )
@Filter( value="filter2", value2="222")
//@Filters({@Filter( value="filter1",value2="111" ),@Filter( value="filter2", value2="222")}).注意:JDK8之前:1.没有@Repeatable2.采用本行“注解容器”写法
public interface Filterable {
}
public static void main(String[] args) {
//获取注解后遍历打印值
for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
System.out.println( filter.value() +filter.value2());
}
}
}
3、新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
package com.zgldoing.base.jdk8Features;
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;
import com.zgldoing.base.jdk8Features.Annotations.Holder;
import com.zgldoing.base.jdk8Features.Annotations.NonEmpty;
/**
* @ClassName:Annotations
* @Description:新增类型注解:ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)
* @author zgldo
*
*/
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 {
}
}
public static void main(String[] args) {
final Holder< String > holder = new @NonEmpty Holder< String >();
@NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();
}
}