概述
java8 在java语言方面为我们带来了许多新的特性:
速度更快,代码更少(增加了新的语法 Lambda表达式),强大的 Stream API,便于并行,最大化减少空指针异常Optional等
速度更快
java8 更新了底层的数据结构,其中最核心的就是HashMap。在java7中HashMap底层数据结构是数组加单项链表,而 java8则采用的是数组加链表加红黑树。
修改了垃圾回收机制!
代码更少
新的java8中新增加了 Lambda表达式,这是另人激动的语言改变,使用它设计的代码会更简洁,而且可读,最重要的是代码量也随之减少很多。
强大的Stream API
集合(Collections)的改进也是Java 8的一大亮点,而让集合越来越好的核心组件则是“Stream”。它与java.io包里的InputStream和OutputStream是完全不同的概念,它是一个全新的概念,大家不要混淆。
便于并行
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8中将并行流进行了优化,我们可以很容易的对数据进行并行操作。
最大化减少空指针异
Java8的类中引入了Optional类,来包装对象从而解决NullPointerException。
Lambda表达式
什么是Lambda表达式?
Lambda完全代替了匿名内部类。Lambda是由参数,箭头符号,方法调用,这三部分组成的一段函数表达式。Lambda表达式可以有参数和无参数,有返回值和无返回值。一个Lambda表达式即可代替整个匿名内部类。通常Lambda表达式和函数式接口配合使用。使用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
函数式接口
java中有四大核心的函数式接口
接口 | 类型 | 方法 |
---|---|---|
java.util.function.Consumer | 消费型 | void accept(T t) |
java.util.function.Supplier | 生产型 | T get() |
java.util.function.Function | 函数型 | R apply(T t) |
java.util.function.Predicate | 断言型 | boolean test(T t) |
在函数式接口内只能有有一个抽象方法,这中特殊的接口通常是为了和Lambda表达式配合使用。
示例:
@FunctionalInterface
public interface MyLambda {
//只能有一个抽象方法
String test(String name);
}
Lambda表达式的语法格式
前提:
在java8中引入的了的操作符:-> (箭头操作符)
-> 箭头操作符将 Lambda表达式分为了两部分
左侧:参数列表
右侧:对方法进行的实现(仅仅代表了方法的实现)
参数列表中无需指定数据类型 ,jvm会根据上下文做类型推断。
()-> 方法调用代码
Lambda表达式的使用
Lambda表达式子整体即代表了原先整个匿名函数。但是不能像匿名函数那样实现抽象类或接口中的众多抽象方法,它中只能在只有一个抽象方法的抽象类或者接口的情况下使用,针对多个抽象方法的情况请看下一个章节【函数式方法】。
Lambda 没有非常固定的写法。但是其万变不离其宗,即:左侧一定是参数,而右侧一定是方法执行。左侧的参数服务于右侧的方法。以下列出几种情况。
无参数,无返回值。
//无参数,无返回值。
public static void test() {
//实现了run方法
Runnable runnable = () -> System.out.println("Lambda");
runnable.run();//Lambda
}
无参数,有返回值。
// String test();被实现的抽象方法
MyLambda myLambda = () -> {
return "lambda";
};
System.out.println(myLambda.test());
有一个参数,无返回值
在只有一个参数时,小括号可以省略不写。但是通常为了方便阅读,我们还是应将小括号写上。
// System.out.println(x)为假设的方法实现,
Consumer consumer = (x) -> System.out.println(x);
consumer.accept("Lambda 表达式");
有一个参数,有返回值
// String test(String name);被实现的抽象方法
MyLambda myLambda = (a) -> {
return a;
};
System.out.println(myLambda.test("Lambda"));
}
有两个及以上的参数,并且有返回值。
Comparator comparator = (x,y) -> {return Integer.compare((int)x,(int)y);};
System.out.println(comparator.compare(1, 2));
方法引用
我们通常会使用Lambda的方法体对抽象方法的方法体进行实现,而这部分实现通常是由java类库中已经实现的方法组成,如果方法体中的内容已经有别的方法实现了,但是前提是方法体中所引用的方法必须与抽象方法的返回值和形式参数列表相同,在这种情况下我可以使用三种特殊的引用方式来表示,这么做可以减少代码量,方便阅读。
对象引用实例
当Lambda表达式体中有对象调用方法时。
示例:
Consumer consumer = (x) ->System.out.println(x);
/*以上可以使用方法引用的方式代替
System.out 得到的是一个对象
而println为所引用的方法,
*/
Consumer consumer1 = System.out::println;
consumer1.accept("lambda");//lambda
类引用静态方法
当Lambda表达式体中有类调用静态方法时。
示例:
Comparator comparator = (x,y) -> Integer.compare(x,y);
/*
其中Integer为类compare为静态方法
可以写作
*/
Comparator comparator1 = Integer::compare;
类引用示例方法
当Lambda表达式体中有类调用示例方法时。
前提:参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,
示例:
BiPredicate biPredicate = (x,y) -> x.equals(y);
/*
以上方法中 使用参数列表中的第一个参数调用方法将另一个参数传入,
x为对象 equals()为实例方法
*/
BiPredicate biPredicate1 = Object::equals;
构造器引用
我们通常会使用Lambda的方法体对抽象方法的方法体进行实现,而这部分实现包含new对象的操作时,我们可以使用构造器引用,当有多个构造器重载时,使用构造器引用时java会为为我们选取与抽象方法所匹配的构造器,java会根据变量类型的泛型进行推断。这么做可以减少代码量,方便阅读。
示例:
Supplier supplier = () -> new Employee();
/*
此时Employee可能有多种重载,泛型推断。
*/
Supplier<Employee> supplier1 = Employee::new;
supplier1.get();
Stream API
java.util.stream.Stream接口是java8 中提供的全新的API, Stream流主要是通过流的方式,在流中使用Lambda表达式实现方法来为数据进行各种中间操作,
方便我们从原数据中取到想要的结果。
创建Stream流
- 可以通过Collection系列集合提供的 stream()或parallstream()。
List list = new ArrayList();
Stream stream = list.stream();
Stream stream1 = list.parallelStream();
- 通过 Arrays中的静态方法 stream()获取数组流
User[] user = new User[10];
Arrays.stream(user);
- 通过 Stream类中的静态方法of()
Stream<String> stream = Stream of("aa","bb","cc");
中间操作
筛选与切片
方法 | 概述 |
---|---|
Stream filter(Predicate<? super T> predicate) | 返回由与此给定谓词匹配的此流的元素组成的流。 |
Stream limit(long maxSize) | 返回由此流的元素组成的流,截短长度不能超过 maxSize 。 |
Stream skip(long n) | 在丢弃流的第一个 n元素后,返回由该流的 n元素组成的流。 |
Stream distinct() | 返回由该流的不同元素(根据 Object.equals(Object) )组成的流。 |
示例:
List<User> list = Arrays.asList(
new User("李四", 41,false),
new User("王五", 60,false),
new User("马奇", 22,true),
new User("张三", 53,false),
new User("孙一", 61,true),
new User("葛八", 85,false)
);
Stream<User> stream = list.stream();
Stream<User>stream1 = stream
.filter( (user) -> { return user.getAge()>20 ;})//筛选,排除流中符合条件的某些元素
.skip(2)//抛弃若干数据,
.limit(3)//截断 使过滤后的结果不超过几个
.distinct();//去重,参加去重的元素需要实现hashCode和equals非法
stream1.forEach(System.out::println);
映射
方法 | 概述 |
---|---|
Stream map(Function<? super T,? extends R> mapper) | 返回由给定函数应用于此流的元素的结果组成的流。,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) | 返回由通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
示例:
//此示例不完整
List l = Arrays.asList("a","b","c","d","e");
Stream stream = l.stream()
.map((str) -> str.toUpperCase());
Stream stream1 = l.stream()
.map(filterMap);
排序
方法 | 概述 |
---|---|
Stream sorted() | 返回由此流的元素组成的流,根据自然顺序排序。 |
Stream sorted(Comparator<? super T> comparator) | 返回由该流的元素组成的流,根据提供的 Comparator进行排序。 |
示例:
List l = Arrays.asList("a","d","b","c","e");
l.stream()
.sorted()// 自然排序
.forEach(System.out::println);
//自定义排序............
终止
查找与匹配
方法 | 概述 |
---|---|
boolean allMatch(Predicate<? super T> predicate) | 检查是否匹配这些所有的元素 |
boolean anyMatch(Predicate<? super T> predicate) | 检查是否至少匹配一个元素 |
boolean noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配所有元素 |
Optional findFirst() | 返回第一个元素 |
Optional findAny() | 返回当前流中的任意元素 |
long count() | 返回流中元素的总个数 |
Optional max(Comparator<? super T> comparator) | 返回流中最大值 |
Optional min(Comparator<? super T> comparator) | 返回流中最小值 |
归约与收集
可以将流中元素反复结合起来,得到一个值。例如将一个数组中的数字元素全部相加
方法 | 概述 |
---|---|
Optional reduce(BinaryOperator accumulator) | 使用 associative累积函数对此流的元素执行 reduction ,并返回描述减小值的 Optional (如果有)。 |
T reduce(T identity, BinaryOperator accumulator) | 使用提供的身份值和 associative累积功能对此流的元素执行 reduction ,并返回减小的值。 |
U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator combiner) | 执行 reduction在此流中的元素,使用所提供的身份,积累和组合功能。 |
<R,A> R collect(Collector<? super T,A,R> collector) |
归约示例:
List l = Arrays.asList(1,2,3,4,5);
l.stream().reduce(0,(x,y) -> x+y);//将0作为起始值,在和起始值相加后将得到的值为x再与其他值相加
收集示例:
list.stream()
.map(User::getName)//取出所有名字
.collect(Collectors.toList());//将收集到的元素放入List中
list.stream()
.map(User::getName)//取出所有名字
.collect(Collectors.toCollection(HashSet::new));//将收集到的元素放入指定的容器中
list.stream()
.collect(Collectors.groupingBy(User::getSxe));//分组
list.stream()
.collect(Collectors.groupingBy(User::getSxe),
Collectors.groupingBy(再次分组的规则));//将分组后则组再次进行分组
list.stream()
.collect(Collectors.groupingBy(满足条件的和不满足的));//将元素进行分区,真和假 两个区
并行流与串行流
并行流是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API可以声明性地通过 parallel()与sequential()在并行流与顺序流之间进行切换。列如将一个复杂的任务按照临界值(不在进行拆分的规则)进行拆分,压入线程队列,最后将拆分的任务们所得到的结果进行 join合并,
Optional
Java.util.Optional类是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在 Optional可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
方法 | 概述 |
---|---|
static Optional of(T value) | 返回一个不为null的Optional,如果为空则抛出空指针异常 |
static Optional empty() | 返回一个不包含任何元素的空的Optional容器 |
static Optional ofNullable(T value) | 如果传入的参数为 null 则返回一个空的Optional容器,否则返回一个包此值的Optional容器 |
boolean isPresent() | 判断此optional容器内是否有值 |
T orElse(T other) | 如果此optional容器内含有值,则返回容器内的值,否则返回指定的值 |
T orElseGet(Supplier<? extends T> other) | 如果此optional容器内含有值,则返回容器内的值,否则返回此值的get操作的结果 |
Optional map(Function<? super T,? extends U> mapper) | 如果此函数的结果不为 null 并且不包含值,则返回一个不包含任何元素的空的Optional容器, 否则返回一个包含此函数的处理结果的 Optional容器 |
Optional flatMap(Function<? super T,Optional> mapper) | 如果此函数的结果不为 null 并且不包含值,则返回一个不包含任何元素的空的Optional容器,否则返回一个包含此函数的处理结果的 Object 类型的值 |
示例:
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args) {
/*返回一个不包含任何元素的空的Optional容器*/
Optional optional1 = Optional.empty();
/*如果传入的参数为 null 则返回一个空的Optional容器,否则返回一个包此值的Optional容器*/
Optional.ofNullable(null);
/*返回一个不为null的Optional,如果为空则抛出空指针异常*/
Optional optional = Optional.of(null);
/*判断此optional容器内是否有值*/
optional.isPresent();
/*如果此optional容器内含有值,则返回容器内的值,否则返回指定的值*/
optional.orElse(new User());
/*如果此optional容器内含有值,则返回容器内的值,否则返回此值的get操作的结果*/
optional.orElseGet(() -> new User());
/*如果此函数的结果不为 null 并且不包含值,则返回一个不包含任何元素的空的Optional容器,
否则返回一个包含此函数的处理结果的optional容器*/
optional.map((e) -> e.toString());
/*如果此函数的结果不为 null 并且不包含值,则返回一个不包含任何元素的空的Optional容器,
否则返回一个包含此函数的处理结果的Object类型的值*/
optional.flatMap((e) -> e.toString());
}
}
接口
在java8中为接口提供了新的功能:拥有实现的默认方法和静态方法。
。
默认方法
接口默认方法的”类优先”原则
当一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时,会选择父类中的方法。
如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。
public interface myInterface {
public default String a(){
return "斤斤计较";
}
}
静态方法
静态方法和默认方法关于同名时的处理规则相同。
public interface myInterface {
static void b(){
}
}
新时间日期API
Java 8 中为我们带了新的时间API与过去的时间API不同的是,新的时间API操作更便捷,代码更少,可读性高,可以进行数学运算的,并且不存在线程安全的问题。
主要的相关类:
类 | 概述 |
---|---|
java.time.LocalDate | 包含的 时 - 分 - 秒日期对象 |
java.time.LocalTime | 包含年 - 月 - 日的日期对象,其他日期字段,例如日期,星期几和星期 |
java.time.LocalDateTime | 包含年 - 月 - 日 - 时 - 分 - 秒的时间日期对象 |
java.time.Instant | 在时间线上的瞬间点(时间戳) |
java.time.Duration | 该类以秒和纳秒为单位建立数量或时间量,(时间) |
java.time. Period | 以年,月和日为单位建立数量或时间量, (日期) |
java.time.temporal | 调整器是修改时间物体的关键工具 |
java.time.DateTimeFormatter | 格式化器,用于打印和解析日期时间对象。 |
java.time. ZonedDate/ZonedTime/ZonedDateTime | 时区 |
java.time.ZoneId | 包含时区ID |
Localdate、 Localtime、Loca| Datetime 类的实例是不可变的对象,对对象的任何操作都会得到一个新的不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含数前的时间信息。也不包含与时区相关的信息。
LocalDateTime
以下只演示了LocalDateTime的常用方法,时间类与日期类的方法与LocalDateTime类的方法使用方式大致相同。
示例:
import java.time.*;
public class MyLocalDateTime {
public static void main(String[] args) {
//从默认时区的系统时钟获取当前日期。
LocalDateTime defaultLocalDateTime = LocalDateTime.now();
System.out.println(defaultLocalDateTime);//21-06-25T23:49:41.512
//创建一个日期时间对象
LocalDateTime localDateTime = LocalDateTime.of(2016, 1, 2,15,52,6,555);
System.out.println(localDateTime);//2016-01-02T15:52:06.000000555
//对时间对象进行运算
System.out.println(localDateTime.plusYears(5));//加五年
System.out.println(localDateTime.minusMonths(5));//减5月
//获取时间日期对象的值
System.out.println(localDateTime.getMonth());//获取对象的月份
System.out.println(localDateTime.getYear());//获取对象的年份
//获取时间戳
Instant instant = Instant.now();//此时间为UTC时间,通过偏移量获取中国时间
System.out.println(instant);
//偏移时间
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));//偏移8小时
System.out.println(offsetDateTime);
//获取两个时间相差的值
Duration duration = Duration.between(Instant.now(),instant);
//转换为秒
long duration1 = duration.toMillis();//毫秒
System.out.println(duration1);
}
}
时间矫正器
将时间调整到某个时候
示例:
LocalDateTime localDateTime =LocalDateTime.now();
//将日期指定到某一天
System.out.println(localDateTime.withDayOfMonth(10));
//获取下一个周日
localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
//获取下一个指定的某个日期
localDateTime.with(
(l) -> {
LocalDateTime localDateTime1 = (LocalDateTime) l;
DayOfWeek dayOfWeek = localDateTime1.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {//如果是周五
return localDateTime1;
}
return l;
}
);
格式化日期或时间
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME;//格式化的标准
LocalDateTime.now().format(dateTimeFormatter);//按照此标准格式化
//使用自己的格式创建一个时间日期对象
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy MM dd HH mm ss");
时区
Java 8中加入了对时区的支持,带时区的时间为分别为ZonedDate、 ZonedTime、 ZonedDateTime.其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式。Zoned:该类中包含了所有的时区信息。
示例:
//获取所有时区。
Set set =ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
//构建指定时区的时间对象
LocalDateTime DateTime = LocalDateTime.now(ZoneId.of("US/Pacific"));
//或
LocalDateTime dateTime1 = LocalDateTime.now();
dateTime1.atZone(ZoneId.of("US/Pacific"));
注解
Java 8 提供了改进了注解,提供了:可重复的注解,可用于类型的注解
可重复的注解
要使注解可服用需要创建一个注解容器。
步骤: 创建重复注解容器—>创建注解—>重复使用注解
创建重复注解容器示例:
@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationContainer {
MyAnnotation[] value();
}
创建注解:
@Repeatable(value = MyAnnotationContainer.class)
@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
重复使用注解:
public class Test {
@MyAnnotation
@MyAnnotation
public static void main(String[] args) {
}
}
可用于类型的注解
Java 8 为Annotation的元注解@Retention提供两个新的新的值
Retention值 | 概述 |
---|---|
TYPE_PARAMETER | 表示该注解能写在类型参数的声明语句中,可以对方法的参数进行类型限制声明参数类型 |
TYPE_USE | 表示注解可以在任何用到类型的地方使用 |
TYPE_PARAMETER:
首先:
//需要在@Target中加入
@Target({METHOD,TYPE, PARAMETER,TYPE_PARAMETER})
public void test(@MyAnnotation("qwe") String name){
}
TYPE_USE:
可以在任何声明类型处使用包括注解和枚举。