lambda表达式
需求分析
创建一个新线程
package com.xqm.jdk8.lambda;
public class Demo1Lambda {
public static void main(String[] args) {
// 创建一个新线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"启动");
}
},"thread1").start();
System.out.println("主线程"+Thread.currentThread().getName());
}
}
代码分析:
1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
2. 为了指定run方法体,不得不需要Runnable的实现类
3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
5. 而实际上,我们只在乎方法体中的代码
lambda表达式
1.lambda表达式是一个匿名函数,可以理解为一段可以传递的代码
2.lambda表达式的优点:简化了匿名内部类的使用,语法更加简单
3.简化代码
package com.xqm.jdk8.lambda;
public class Demo2Lambda {
public static void main(String[] args) {
// 创建一个新线程
new Thread(()->{
System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName());
}).start();
}
}
lambda表达式语法规则
由三个部分组成:
(参数类型 参数名称) -> {
代码体;
}
格式说明:
- (参数类型 参数名称):参数列表
- {代码体;} :方法体
- -> : 箭头,分割参数列表和方法体
练习
无参返回
// 接口
package com.xqm.jdk8.lambda.service;
import java.util.function.Supplier;
public interface UserService {
/**
* 普通方法必须实现
*/
void show();
/**
* 默认方法可以实现也可以不实现
*/
default void show1(){};
}
// 测试类
package com.xqm.jdk8.lambda;
import com.xqm.jdk8.lambda.service.UserService;
/**
* 无参
*/
public class Demo3Lambda {
public static void main(String[] args) {
// 必须new才行
goShow(new UserService() {
@Override
public void show() {
System.out.println("show 方法执行了");
}
});
// 使用lambda表达式
goShow(()->{
System.out.println("show 方法又执行了");
});
}
private static void goShow(UserService userService){
userService.show();
}
}
有参有返回值
// 实体类
package com.xqm.jdk8.lambda.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Integer height;
}
// 测试类
package com.xqm.jdk8.lambda;
import com.xqm.jdk8.lambda.model.Person;
import com.xqm.jdk8.lambda.service.UserService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 无参
*/
public class Demo4Lambda {
public static void main(String[] args) {
List<Person> list=new ArrayList<>();
list.add(new Person("周杰伦",44,170));
list.add(new Person("刘德华",43,171));
list.add(new Person("周星驰",46,172));
list.add(new Person("郭富城",48,173));
// 进行普通排序
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
// 使用lambda表达式改造排序规则
Collections.sort(list,(Person o1,Person o2)->{
return o1.getAge()- o2.getAge();
});
// 简化后排序
list.sort(Comparator.comparingInt(Person::getAge));
for (Person person : list) {
System.out.println(person.toString());
}
}
}
lambda表达式局限
只能实现一个方法(如果实现接口,那么接口只能有一个抽象方法)
lambda表达式原理
1.匿名内部类的本质是在编译的时候生成xxxxx$1.class文件
2.
javap -c -p xxx.class
-c 表示反汇编
-p 表示显示所有类和接口
3.lambda会生成一个方法A,方法A中就是lambda的方法体,实现接口重写方法B,方法B会调用方法A
lambda表达式省略写法
1.小括号中参数类型可以省略
2.如果小括号内只有一个参数,则括号可以省略
3.如果大括号内有且仅有一条语句,则大括号、return、分号都可以省略
lambda表达式使用前提
1.方法的参数或者局部变量类型必须是接口才能使用lambda
2.接口有且仅有一个抽象方法
lambda表达式和匿名内部类的区别
1.所需类型
匿名内部类的类型可以是类、抽象类、接口
lambda只能是接口
2.抽象方法的数量不一样
匿名内部类所需接口的抽象方法数量随意
lambda所需接口的方法只能有一个抽象类
3.实现原理不一样
匿名内部类是在编译后形成一个class
lambda是在程序运行的时候动态生成class文件
接口
JDK1.8之前
interface 接口名{
静态常量
抽象方法
}
如果接口新增一个抽象方法,实现的子类必须重写全部抽象方法,不利于接口扩展
JDK1.8之后
interface 接口名{
静态常量
抽象方法
默认方法
静态方法
}
默认方法
interface 接口名 {
修饰符 default 返回值类型 方法名{
方法体
}
}
1.实现类实例直接调用接口的默认方法
2.实现类重写接口的默认方法
静态方法
interface 接口名{
修饰符 static 返回值类型 方法名{
方法体
}
}
1.不能被重写
2.只能通过 接口名.方法名 调用
函数式接口
Supplier:无参有返回值
格式
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
使用
package com.xqm.jdk8.fun;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* supplier函数式接口,其中get()方法是无参有返回值
*/
public class SupplierTest {
public static void main(String[] args) {
sum(()->{
int[] arr={11,44,77,22,10,2};
// 计算出数组最大值
Arrays.sort(arr);
return arr[arr.length-1];
});
}
private static void sum(Supplier<Integer> supplier){
Integer max = supplier.get();
System.out.println("max = " + max);
}
}
Consumer:有参无返回值
supplier是用来生产数据的,consumer是用来消费数据的,消费的时候需要指定一个泛型来定义参数类型
格式
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
使用
package com.xqm.jdk8.fun;
import java.util.function.Consumer;
/**
* consumer
*/
public class Consumer1Test {
public static void main(String[] args) {
test(msg->{
System.out.println(msg.toLowerCase());
});
}
public static void test(Consumer<String> consumer){
consumer.accept("HELLO WORLD!");
consumer.accept("HELLO KETTY!");
}
}
package com.xqm.jdk8.fun;
import java.util.function.Consumer;
/**
* consumer
*/
public class Consumer2Test {
public static void main(String[] args) {
test(msg1->{
System.out.println(msg1.toLowerCase());
},msg2->{
System.out.println(msg2.toUpperCase());
});
}
public static void test(Consumer<String> consumer1,Consumer<String> consumer2){
String str="Hello World";
// 转小写
consumer1.accept(str);
// 转大写
consumer2.accept(str);
}
}
package com.xqm.jdk8.fun;
import java.util.function.Consumer;
/**
* consumer
*/
public class Consumer3Test {
public static void main(String[] args) {
test(msg1->{
System.out.println(msg1.toLowerCase());
},msg2->{
System.out.println(msg2.toUpperCase());
});
}
public static void test(Consumer<String> consumer1,Consumer<String> consumer2){
String str="Hello World";
// 转小写-->转大写
consumer1.andThen(consumer2).accept(str);
}
}
Function:有参有返回值
根据一个类型的数据获取另一个类型的数据。前者称为前置条件,后者称为后置条件。
格式
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
测试
传递一个字符串,返回一个int
package com.xqm.jdk8.fun;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
test(msg->{
Integer i = Integer.parseInt(msg);
System.out.println(i.getClass());
return i;
});
}
public static void test(Function<String,Integer> function){
Integer apply = function.apply("666");
System.out.println("apply:"+apply);
}
}
andThen
package com.xqm.jdk8.fun;
import java.util.function.Function;
public class Function2Test {
public static void main(String[] args) {
test(msg->{
Integer i = Integer.parseInt(msg);
System.out.println(i.getClass());
return i;
},msg2->{
System.out.println(msg2);
return msg2;
});
}
public static void test(Function<String,Integer> function1,Function<Integer,Integer> function2){
// Integer a = function1.apply("666");
// Integer b = function2.apply(a);
Integer apply = function1.andThen(function2).apply("123");
}
}
compose
package com.xqm.jdk8.fun;
import java.util.function.Function;
public class Function3Test {
public static void main(String[] args) {
test(msg->{
Integer i = Integer.parseInt(msg);
System.out.println(i.getClass());
return i;
},msg2->{
System.out.println(msg2);
return msg2;
});
}
public static void test(Function<String,Integer> function1,Function<Integer,Integer> function2){
// 这里就是先执行compose中的,然后再执行外层
function2.compose(function1).apply("123");
}
}
Predicate:有参且返回值为Boolean
结构
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
测试
package com.xqm.jdk8.fun;
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
test(msg->{
// 如果长度大于三就返回true
return msg.length()>3;
},"hello world");
}
public static void test(Predicate<String> predicate,String msg){
boolean test = predicate.test(msg);
System.out.println(test);
}
}
在Predicate中默认方法提供了and(与)、or(或)、negate(非)、isEqual
package com.xqm.jdk8.fun;
import java.util.function.Predicate;
/**
* 包含H和W
*/
public class Predicate2Test {
public static void main(String[] args) {
test(msg->msg.contains("H")
,msg2->
msg2.contains("W")
);
}
public static void test(Predicate<String> predicate,Predicate<String> predicate2){
boolean hello = predicate.and(predicate2).test("Hello World");
System.out.println(hello);
}
}
方法引用
lambda表达式冗余
在使用lambda表达式的时候,也会出现表达式冗余的情况。比如求数组之和的时候。
package com.xqm.jdk8.funcRef;
import java.util.function.Consumer;
public class FunRef1 {
public static void main(String[] args) {
printSum(a->{
int sum=0;
for (int i : a) {
sum+=i;
}
System.out.println(sum);
});
}
public static void printSum(Consumer<int[]> consumer){
int a[]={1,2,3,4,5,6};
consumer.accept(a);
}
}
解决方案
在lambda表达式中要执行的代码和另一个方法中的代码是一样的,这时没有必要重写一份逻辑
,这时候就可以引用重复的代码。使用双冒号进行方法引用::。
package com.xqm.jdk8.funcRef;
import java.util.function.Consumer;
public class FunRef2 {
public static void main(String[] args) {
// 引用方法,双冒号::就是方法引用
printSum(FunRef2::getTotla);
}
/**
* 求数组之和
* @param a
*/
private static void getTotla(int[] a){
int sum=0;
for (int i : a) {
sum+=i;
}
System.out.println(sum);
}
private static void printSum(Consumer<int[]> consumer){
int a[]={1,2,3,4,5,6};
consumer.accept(a);
}
}
方法引用的格式
符号表示:::
符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用
应用场景:如果lambda表达式所要实现的方案,已经有其他的方法存在相同的方案,那么则可以使用方法引用
常见引用方式:
-
InstanceName::methodName 对象::方法名
-
ClassName::staticMethodName 类名::静态方法名
-
ClassName::methodName 类名::普通方法
-
ClassName::new 类名::new调用的构造器
-
TypeName[]::new String[]::new调用的数组构造器
对象::方法名
package com.xqm.jdk8.funcRef;
import java.util.Date;
import java.util.function.Supplier;
public class FunRef3 {
public static void main(String[] args) {
Date now =new Date();
// 对象::方法名
Supplier<Long> supplier= now::getTime;
System.out.println(supplier.get());
}
}
类名::静态方法名
package com.xqm.jdk8.funcRef;
import java.util.function.Supplier;
public class FunRef4 {
public static void main(String[] args) {
// 未使用方法引用
Supplier<Long> supplier= ()->{
return System.currentTimeMillis();
};
System.out.println(supplier.get());
// 使用方法引用 方法名::静态方法
Supplier<Long> supplier1= System::currentTimeMillis;
}
}
类名::实例方法
java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用者
package com.xqm.jdk8.funcRef;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class FunRef5 {
public static void main(String[] args) {
Function<String,Integer> function=s-> s.length();
System.out.println(function.apply("hello"));
// 通过方法引用来实现
Function<String,Integer> function1=String::length;
System.out.println(function1.apply("world"));
BiFunction<String,Integer,String> function2=String::substring;
String msg = function2.apply("Hello world", 3);
System.out.println(msg);
}
}
类名::构造器
package com.xqm.jdk8.lambda.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
}
package com.xqm.jdk8.funcRef;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class FunRef6 {
public static void main(String[] args) {
// 无参构造方法
Supplier<Person> supplier=Person::new;
System.out.println(supplier.get());
BiFunction<String,Integer,Person> function=Person::new;
System.out.println(function.apply("张三",18));
}
}
数组::构造器
package com.xqm.jdk8.funcRef;
import java.util.Arrays;
import java.util.function.Function;
public class FunRef7 {
public static void main(String[] args) {
// 未使用方法引用
Function<Integer,String[]> function=(len)->{
return new String[len];
};
// 这里的方法引用,Integer代表的是创建String数组的长度
Function<Integer,String[]> function1=String[]::new;
// 输出为 [null, null, null, null, null]
System.out.println(Arrays.toString(function1.apply(5)));
}
}
Stream流
集合处理数据的弊端
对集合进行操作的时候,除了必须的增删改查,典型的操作就是遍历集合。只要对集合操作,就得进行循环。
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test01Stream {
public static void main(String[] args) {
List<String> list=Arrays.asList("张三","张三丰","张大炮","思密达","斯密斯");
List<String> list2=new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")){
list2.add(s);
}
}
System.out.println(list2.toString());
}
}
使用stream流来处理
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Test02Stream {
public static void main(String[] args) {
List<String> list=Arrays.asList("张三","张三丰","张大炮","思密达","斯密斯");
List<String> list2 = list.stream().filter(s -> s.startsWith("张")).collect(Collectors.toList());
System.out.println(list2.toString());
}
}
Stream流概述
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。
Stream流的获取方式
1.通过Collection获取
java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实现都可以通过steam方法来获取Stream流。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.stream();
Set<String> set = new HashSet<>();
set.stream();
Vector vector = new Vector();
vector.stream();
}
但是Map接口别没有实现Collection接口,那这时怎么办呢?这时我们可以根据Map获取对应的key value的集合。
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
Stream<String> stream = map.keySet().stream(); // key
Stream<Object> stream1 = map.values().stream(); // value
Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry
}
2.通过Stream的of方法获取
在实际开发中不可避免会操作数组中的数据,由于数组对象不可能添加默认方法,所以Stream中提供了静态方法
Stream不能处理基本类型的数据
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test04Stream {
public static void main(String[] args) {
Stream<String> a=Stream.of("a1","a2","a3");
String[] a1={"aa","bb","cc"};
Stream<String> a2=Stream.of(a1);
// aa bb cc
a2.forEach(System.out::print);
Integer[] a3={1,2,3,4};
// 1 2 3 4
Stream.of(a3).forEach(System.out::print);
// 将{1,2,3,4}当作一个对象来处理,因此使用Stream不能处理基本类型
int[] a4={1,2,3,4};
// [I@533ddba
Stream.of(a4).forEach(System.out::print);
}
}
Stream常用方法
常用方法
方法名 | 方法作用 | 返回值类型 | 方法种类 |
---|---|---|---|
count | 统计个数 | long | 终结 |
forEach | 逐个处理 | void | 终结 |
collect | 生成集合 | collect | 终结 |
filter | 过滤 | Stream | 函数拼接 |
limit | 取用前几个 | Stream | 函数拼接 |
skip | 跳过前几个 | Stream | 函数拼接 |
map | 映射 | Stream | 函数拼接 |
concat | 拼接 | Stream | 函数拼接 |
终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和
forEach 方法。
非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结
方法。)
Stream注意事项(重要)
- Stream只能操作一次
- Stream方法返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
forEach
用来遍历stream流
void forEach(Consumer<? super T> action);
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test05Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
// 1 2 3 4
Stream.of(a3).forEach(System.out::print);
}
}
count
用来统计流中数据个数
long count();
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test06Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
long count = Stream.of(a3).count();
// 4
System.out.println(count);
}
}
filter
用来过滤语句
Stream<T> filter(Predicate<? super T> predicate);
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test07Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
// 3 4
Stream.of(a3).filter(a->a>2).forEach(System.out::print);
}
}
limit
取用前几个数据,如果集合当前长度大于参数则截取,否则不操作
Stream<T> limit(long maxSize);
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test08Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
// 1 2
Stream.of(a3).limit(2).forEach(System.out::print);
}
}
skip
跳过前几个,如果参数大于流数据长度,则不操作,否则截取
Stream<T> skip(long n);
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test09Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
// 3 4
Stream.of(a3).skip(2).forEach(System.out::print);
}
}
map
将流中的元素映射到另一个流中
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
package com.xqm.jdk8.stream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test10Stream {
public static void main(String[] args) {
Integer[] a3={1,2,3,4};
// false true false false
Stream.of(a3).map(s->s==2).forEach(System.out::print);
System.out.println();
// 1 2 3 4
Stream.of(a3).map(String::valueOf).forEach(System.out::print);
}
}
sorted
数据进行排序
Stream<T> sorted();
package com.xqm.jdk8.stream;
import java.util.Comparator;
import java.util.stream.Stream;
public class Test11Stream {
public static void main(String[] args) {
Integer[] a3 = {1, 4, 3, 2,8,0,4,6};
// 01234468
Stream.of(a3).sorted().forEach(System.out::print);
System.out.println();
// 86443210
Stream.of(a3).sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}).forEach(System.out::print);
}
}
distinct
如果要去掉重复数据,可以使用distict。基本数据类型直接可以去重,自定义对象则需要重写hashCode和equals方法
Stream<T> distinct();
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test12Stream {
public static void main(String[] args) {
Integer[] a3 = {1, 4, 3, 2,8,0,4,6};
// 0123468
Stream.of(a3).distinct().sorted().forEach(System.out::print);
System.out.println();
// 8643210
Stream.of(a3).distinct().sorted((o1, o2) -> o2-o1).forEach(System.out::print);
// Person(name=张三, age=13)Person(name=李四, age=22)Person(name=张三, age=22)
// 如果要去重,必须重写hashcode和equals方法
Stream.of(new Person("张三",13),
new Person("李四",22),
new Person("张三",13))
.distinct().forEach(System.out::print);
}
}
match
判断数据是否匹配指定的条件
元素是否都满足条件
boolean allMatch(Predicate<? super T> predicate);
元素是否任意一个满足条件
boolean anyMatch(Predicate<? super T> predicate);
元素是否都不满足条件
boolean noneMatch(Predicate<? super T> predicate);
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test13Stream {
public static void main(String[] args) {
boolean b = Stream.of("1", "3", "3", "4", "0", "1", "7")
.map(Integer::parseInt)
// 里面是不是全部大于0
.allMatch(a -> a > 0);
// false
System.out.println(b);
boolean b1 = Stream.of("1", "3", "3", "4", "0", "1", "7")
.map(Integer::parseInt)
// 里面只要有一个大于4
.anyMatch(a -> a > 4);
// true
System.out.println(b1);
boolean b2 = Stream.of("1", "3", "3", "4", "0", "1", "7")
.map(Integer::parseInt)
// 一个都不匹配大于10
.noneMatch(a -> a > 10);
// true
System.out.println(b2);
}
}
find
需要找到某些数据
取第一个元素
Optional<T> findFirst();
作用就是从Stream中取任意一个元素,正常情况下一般会取第一个元素,在并行流的情况下会随机取一个元素。
Optional<T> findAny();
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test14Stream {
public static void main(String[] args) {
Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
String s = stringStream.findFirst().get();
// 1
System.out.println(s);
Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
String s1 = stringStream1.findAny().get();
// 1
System.out.println(s1);
}
}
max和min
找出最大值和最小值
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
package com.xqm.jdk8.stream;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class Test15Stream {
public static void main(String[] args) {
Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
Optional<Integer> max = stringStream.map(Integer::parseInt).max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
// 7
System.out.println(max.get());
Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
Optional<Integer> min = stringStream1.map(Integer::parseInt).min(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
// 0
System.out.println(min.get());
}
}
// 极简写法
package com.xqm.jdk8.stream;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class Test15Stream {
public static void main(String[] args) {
Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
Optional<Integer> max = stringStream.map(Integer::parseInt).max((o1, o2) -> o1 - o2);
// 7
System.out.println(max.get());
Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
Optional<Integer> min = stringStream1.map(Integer::parseInt).min(Comparator.comparingInt(o -> o));
// 0
System.out.println(min.get());
}
}
reduce
将所有数据归纳得到一个数据。聚合,比如做累加、求取最大值、最小值等
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
package com.xqm.jdk8.stream;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class Test16Stream {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(4,5,3,9);
// 两两元素相加,第一个参数identity是初始默认值,这里赋值为0,就是0+4+5+3+9
Integer reduce = stream.reduce(0, Integer::sum);
// 21
System.out.println(reduce);
// 获取最大值
Stream<Integer> stream1 = Stream.of(4,5,3,9);
Integer reduce1 = stream1.reduce(0, Integer::max);
// 9
System.out.println(reduce1);
}
}
map和reduce组合
在实际开发中,经常用map和reduce进行组合运算
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test17Stream {
public static void main(String[] args) {
// 求出所有年龄的总和
Stream<Person> personStream = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20));
Integer reduce = personStream.map(Person::getAge).reduce(0, Integer::sum);
// 55
System.out.println(reduce);
}
}
package com.xqm.jdk8.stream;
import java.util.Optional;
import java.util.stream.Stream;
public class Test18Stream {
public static void main(String[] args) {
Stream<String> stream=Stream.of("a","b","c","a","a","b","d","a");
// 统计字符a出现的次数
Optional<Integer> reduce = stream.map(c -> "a".equals(c) ? 1 : 0)
.reduce(Integer::sum);
// 4
System.out.println(reduce.get());
}
}
mapToInt、mapToLong、mapToDouble
将stream流中Integer转换为int
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
package com.xqm.jdk8.stream;
import java.util.Optional;
import java.util.stream.Stream;
public class Test19Stream {
public static void main(String[] args) {
// Integer占用的内存比int多很多,在stream流操作中有装箱和拆箱
Integer[] arr ={1,3,5,7,9};
// 会进行拆箱操作
Stream.of(arr).filter(i->i>0)
.forEach(System.out::println);
// 为了提高代码的效率,可以先将Integer转换成int,在进行其他操作
Stream.of(arr).mapToInt(Integer::intValue).filter(i->i>3).forEach(System.out::println);;
}
}
concat
如果有两个流希望合并成为一个流,那么可以使用stream静态方法cancat
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
package com.xqm.jdk8.stream;
import java.util.stream.Stream;
public class Test20Concat {
public static void main(String[] args) {
Stream<String> a = Stream.of("a", "b", "c");
Stream<String> b = Stream.of("x", "y", "z");
// abcxyz
Stream.concat(a,b).forEach(System.out::print);
}
}
综合案例
定义两个集合,存储用户信息。
1.第一个集合只保留长度为3的成员
2.第一个集合筛选之后只要前三个人
3.第二个集合只要姓张的成员
4.第二个集合筛选之后不要前两个人
5.将两个集合合并成一个集合
6.根据姓名创建Person对象
7.打印整个集合的Person信息
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
/**
* 综合案例
* 1.第一个集合只保留长度为3的成员
* 2.第一个集合筛选之后只要前三个人
* 3.第二个集合只要姓张的成员
* 4.第二个集合筛选之后不要前两个人
* 5.将两个集合合并成一个集合
* 6.根据姓名创建Person对象
* 7.打印整个集合的Person信息
*/
public class Test21Test {
public static void main(String[] args) {
List<String> nameList=Arrays.asList("迪丽热巴","宋远桥","老子","庄子","孙子","洪七公");
List<String> nameList2=Arrays.asList("古力娜扎","张无忌","张三丰","赵丽颖","张二狗","张天爱","张三");
List<Person> list=new ArrayList<>();
// 第一个集合 宋远桥 洪七公
Stream<String> oneStream = nameList.stream().filter(s -> s.length() == 3).limit(3);
// 第二个集合 张二狗 张天爱 张三
Stream<String> twoStream = nameList2.stream().filter(s -> s.startsWith("张")).skip(2);
// 合并成一个集合
Stream<String> concat = Stream.concat(oneStream, twoStream);
// 根据姓名创建Person对象 打印整个集合的Person信息
concat.map(n->new Person(n,new Random().nextInt(20))).forEach(System.out::println);
}
}
collect
将收集到的结果收集起来
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
收集到集合中
package com.xqm.jdk8.stream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
*/
public class Test22Collect {
public static void main(String[] args) {
List<String> nameList = Arrays.asList("迪丽热巴", "宋远桥", "老子", "庄子", "孙子", "洪七公","洪七公");
// list集合
List<String> collect = nameList.stream().filter(s -> s.length() > 2)
.collect(Collectors.toList());
System.out.println(collect.toString());
// set集合
Set<String> collect1 = nameList.stream().filter(s -> s.length() > 2)
.collect(Collectors.toSet());
System.out.println(collect1.toString());
// 收集到为具体的类型 arrayList
ArrayList<String> collect2 = nameList.stream().filter(s -> s.length() > 2)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(collect2);
}
}
[迪丽热巴, 宋远桥, 洪七公, 洪七公]
[洪七公, 迪丽热巴, 宋远桥]
[迪丽热巴, 宋远桥, 洪七公, 洪七公]
收集到数组中
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
*/
public class Test23Collect02 {
public static void main(String[] args) {
List<String> nameList = Arrays.asList("迪丽热巴", "宋远桥", "老子", "庄子", "孙子", "洪七公","洪七公");
// 数组
Object[] objects = nameList.stream().filter(s -> s.length() > 2)
.toArray();
// 指定返回数据类型
String[] strings = nameList.stream().filter(s -> s.length() > 2)
.toArray(String[]::new);
}
}
对流中数据进行聚合计算
package com.xqm.jdk8.stream;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test24 {
public static void main(String[] args) {
// 获取最大值
Optional<Person> collect = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20)).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
// Person(name=李四, age=22)
System.out.println(collect.get());
// 获取最小值
Optional<Person> collect1 = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20)).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
// Person(name=张三, age=13)
System.out.println(collect1.get());
// 求和
Integer collect2 = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20))
// .collect(Collectors.summingInt(s -> s.getAge()));
.collect(Collectors.summingInt(Person::getAge));
// 55
System.out.println(collect2);
// 平均值
Double collect3 = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20))
.collect(Collectors.averagingInt(Person::getAge));
// 18.333333333333332
System.out.println(collect3);
// 统计数量
Long collect4 = Stream.of(new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 20))
.collect(Collectors.counting());
// 3
System.out.println(collect4);
}
}
Collectors.groupingBy
分组计算
单级分组
package com.xqm.jdk8.stream;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test25Group {
public static void main(String[] args) {
// 根据姓名分组
Map<String, List<Person>> collect = Stream.of(
new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).collect(Collectors.groupingBy(Person::getName));
// 李四:[Person(name=李四, age=22), Person(name=李四, age=25)]
// 张三:[Person(name=张三, age=13), Person(name=张三, age=24), Person(name=张三, age=20)]
collect.forEach((k,v)->{
System.out.println(k+":"+v);
});
// 根据年龄分组,如果大于18 则是成年
Map<String, List<Person>> collect1 = Stream.of(
new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).collect(Collectors.groupingBy(s -> s.getAge() >= 18 ? "成年" : "未成年"));
//未成年:[Person(name=张三, age=13)]
// 成年:[Person(name=李四, age=22), Person(name=张三, age=24), Person(name=李四, age=25), Person(name=张三, age=20)]
collect1.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
}
多级分组
package com.xqm.jdk8.stream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test26Group02 {
public static void main(String[] args) {
// 多级分组
Map<String, Map<Integer, List<Person>>> collect = Stream.of(
new Person("张三", 13),
new Person("李四", 22),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getAge)));
collect.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
}
李四:{22=[Person(name=李四, age=22)], 25=[Person(name=李四, age=25)]}
张三:{20=[Person(name=张三, age=20)], 24=[Person(name=张三, age=24)], 13=[Person(name=张三, age=13)]}
partitioningBy
对流中数据进行分区处理
Collectors.partitioningBy()会根据是否为true,把集合中数据分为两个列表,一个为true,一个为false
package com.xqm.jdk8.stream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test27Partition {
public static void main(String[] args) {
// 多级分组
Map<Boolean, List<Person>> collect = Stream.of(
new Person("张三", 13),
new Person("李四", 17),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).collect(Collectors.partitioningBy(p -> p.getAge() > 18));
collect.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
}
false:[Person(name=张三, age=13), Person(name=李四, age=17)]
true:[Person(name=张三, age=24), Person(name=李四, age=25), Person(name=张三, age=20)]
对流中数据进行拼接处理
Collectors.joining会根据指定的连接符,将所有元素连接为一个字符串
package com.xqm.jdk8.stream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test28Join {
public static void main(String[] args) {
// 无参
String collect = Stream.of(
new Person("张三", 13),
new Person("李四", 17),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).map(Person::getName)
.collect(Collectors.joining());
// 张三李四张三李四张三
System.out.println(collect);
// 连接符
String collect1 = Stream.of(
new Person("张三", 13),
new Person("李四", 17),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).map(Person::getName)
.collect(Collectors.joining("_"));
// 张三李四张三李四张三
System.out.println(collect1);
// 连接符 前缀 后缀
String collect2 = Stream.of(
new Person("张三", 13),
new Person("李四", 17),
new Person("张三", 24),
new Person("李四", 25),
new Person("张三", 20)
).map(Person::getName)
.collect(Collectors.joining("_","###","$$$"));
//
System.out.println(collect2);
}
}
张三李四张三李四张三
张三_李四_张三_李四_张三
###张三_李四_张三_李四_张三$$$
并行的stream流
前面使用的stream流都是串行的。
package com.xqm.jdk8.stream;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test29 {
public static void main(String[] args) {
// 无参
long count = Stream.of(
1, 2, 3, 4, 5
).filter(s -> {
System.out.println(Thread.currentThread().getName() + "_" + s);
return s > 3;
}).count();
// main_1
// main_2
// main_3
// main_4
// main_5
}
}
获取并行流的两种方式:
- 通过list中的list.parallelStream()
- 通过串行流转换为并行流 Stream.of().parallel()
package com.xqm.jdk8.stream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* 演示并行流
*/
public class Test31 {
public static void main(String[] args) {
// 将已有的串行流转换为并行流
Stream<Integer> parallel = Stream.of(1, 5, 7, 9, 4, 5, 3).parallel();
long count = parallel.filter(s -> {
System.out.println(Thread.currentThread().getName() + "_" + s);
return s > 2;
}).count();
}
}
ForkJoinPool.commonPool-worker-4_5
main_4
ForkJoinPool.commonPool-worker-2_3
ForkJoinPool.commonPool-worker-13_7
ForkJoinPool.commonPool-worker-11_1
ForkJoinPool.commonPool-worker-6_9
ForkJoinPool.commonPool-worker-9_5
串行流和并行流对比
通过5亿个数相加来进行对比
package com.xqm.jdk8.stream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/**
* 串行流和并行流对比
*/
public class Test32 {
private static final long TIME=500000000;
public static void main(String[] args) {
Test32 test=new Test32();
test.common();
test.seri();
test.parallel();
}
private long getTime(){
return System.currentTimeMillis();
}
public void common(){
long sum=0;
long startTime = getTime();
for (long i = 0; i < TIME; i++) {
sum +=i;
}
long endTime = getTime();
System.out.println("普通for循环消耗的时间为:"+(endTime-startTime)+"ms");
}
private void seri(){
long startTime = getTime();
long reduce = LongStream.rangeClosed(0, TIME)
.reduce(0, Long::sum);
long endTime = getTime();
System.out.println("串行stream流for循环消耗的时间为:"+(endTime-startTime)+"ms");
}
private void parallel(){
long startTime = getTime();
long reduce = LongStream.rangeClosed(0, TIME).parallel()
.reduce(0, Long::sum);
long endTime = getTime();
System.out.println("并行stream流for循环消耗的时间为:"+(endTime-startTime)+"ms");
}
}
普通for循环消耗的时间为:141ms
串行stream流for循环消耗的时间为:229ms
并行stream流for循环消耗的时间为:60ms
并行流的安全问题
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.LongStream;
/**
* 串行流和并行流对比
*/
public class Test33 {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
List<Integer> listNew=new ArrayList<>();
list.parallelStream().forEach(listNew::add);
System.out.println(list.size());
// 每次出现的结果不一定
System.out.println(listNew.size());
}
}
100
95
解决方案
1.加同步锁synchronized
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
/**
* 串行流和并行流对比
*/
public class Test34 {
public static void main(String[] args) {
List<Integer> listNew=new ArrayList<>();
Object obj=new Object();
IntStream.rangeClosed(1,1000).parallel().forEach(s->{
synchronized (obj){
listNew.add(s);
}
});
System.out.println(listNew.size());
}
}
2.使用线程安全的容器
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.stream.IntStream;
/**
* 使用线程安全的容器
*/
public class Test35 {
public static void main(String[] args) {
Vector<Integer> listNew=new Vector<>();
Object obj=new Object();
IntStream.rangeClosed(1,1000).parallel().forEach(s->{
listNew.add(s);
});
System.out.println(listNew.size());
}
}
3.将线程不安全的容器转换为线程安全的容器
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 使用线程安全的容器
*/
public class Test36 {
public static void main(String[] args) {
List<Integer> listNew=new ArrayList<>();
List<Integer> list = Collections.synchronizedList(listNew);
Object obj=new Object();
IntStream.rangeClosed(1,1000).parallel().forEach(s->{
list.add(s);
});
System.out.println(list.size());
}
}
4.通过stream中的toArray、collect
package com.xqm.jdk8.stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 使用线程安全的容器
*/
public class Test36 {
public static void main(String[] args) {
List<Integer> collect = IntStream.rangeClosed(1, 1000).parallel()
.boxed()
.collect(Collectors.toList());
System.out.println(collect.size());
}
}
Optional类
optional类主要是解决空指针的问题。
以前对null的处理
package com.xqm.jdk8.optional;
import org.apache.commons.lang3.StringUtils;
public class Test01 {
public static void main(String[] args) {
String name=null;
// 会抛异常
// System.out.println(name.length());
if (!StringUtils.isEmpty(name)){
System.out.println(name.length());
}else {
System.out.println("name is null");
}
}
}
Optional类
public final class Optional<T>
optional是final关键字修饰的类,是一个可以为null的容器对象,主要作用就是避免null检查,出现NullPointerException
Optional创建
-
通过of方法创建
-
通过ofNullable方法创建
-
直接创建empty的Optional
package com.xqm.jdk8.optional; import org.apache.commons.lang3.StringUtils; import java.util.Optional; public class Test02 { public static void main(String[] args) { // of 方法不支持null Optional<String> optional=Optional.of("张三"); // 会报异常 // Optional<String> optionalNull=Optional.of(null); // 不报异常,但是不能使用get方法获取元素 Optional<String> op3=Optional.ofNullable(null); // 通过empty创建一个空的对象 Optional<String> op4=Optional.empty(); } }
Optional常用方法
package com.xqm.jdk8.optional;
import java.util.Optional;
/**
* get(): NoSuchElementException("No value present"):value
* isPresent(): public boolean isPresent() {return value != null;}
* orElse
* orElseGet
* ifPresent
*
*
*/
public class Test03 {
public static void main(String[] args) {
Optional<String> op1=Optional.of("张三");
Optional<String> op2=Optional.empty();
// 获取值 get(),如果为null则会抛异常
System.out.println(op1.get());
// isPresent()判断是否是空值,有值为true,无值为false
System.out.println(op2.isPresent());
// orElse optional如果为null则是李四,否则为自己的value
System.out.println(op2.orElse("李四"));
// orElseGet return value != null ? value : other.get();
System.out.println(op2.orElseGet(()->" Hello"));
// ifPresent(Consumer)
op1.ifPresent(s -> {
System.out.println("存在值:"+s);
});
}
}
作用:简化代码
if(student != null){
if(student.getClassId() != null){
return student.getClassId();
}else{
return 0;
}
}
return Optional.ofNullable(student).map(Student::getClassId).orElse(0);
日期和时间API
时间官网
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
旧版本日期和时间
旧版本中对时间和日期的设计差。
java.util和java.sql中都有日期类。
没有时区支持,国际化差。
格式化日期有线程安全,不方便。
package com.xqm.jdk8.time;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test01 {
public static void main(String[] args) throws Exception {
// 1.有个基数在 1900,设计不合理
Date date=new Date(2022,5,12);
// Mon Jun 12 00:00:00 CST 3922
System.out.println(date);
// 2.时间格式化
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
// Exception in thread "main" java.text.ParseException: Unparseable date: "2022-01-01"
// 必须要按格式来
System.out.println(sdf.parse("2022-01-01 12:12:12"));
// 如果在多线程中使用SimpleDateFormat会有问题,线程不安全
}
}
新版本日期和时间API
JDK 8中增加了一套全新的日期时间API,这套API设计合理,是线程安全的。新的日期及时间API位于 java.time 包
中,下面是一些关键类。
- LocalDate :表示日期,包含年月日,格式为 2019-10-16
- LocalTime :表示时间,包含时分秒,格式为 16:38:54.158549300
- LocalDateTime :表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
- DateTimeFormatter :日期时间格式化类。
- Instant:时间戳,表示一个特定的时间瞬间。
- Duration:用于计算2个时间(LocalTime,时分秒)的距离
- Period:用于计算2个日期(LocalDate,年月日)的距离
- ZonedDateTime :包含时区的时间
Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366
天。此外Java 8还提供了4套其他历法,分别是:
- ThaiBuddhistDate:泰国佛教历
- MinguoDate:中华民国历
- JapaneseDate:日本历
- HijrahDate:伊斯兰历
日期操作
/**
* 日期操作
*/
@Test
public void test01(){
// 1. 创建指定的日期时间
LocalDate date=LocalDate.of(2022,8,6);
// 2022-08-06
System.out.println("指定的日期为"+date);
// 2. 获取当前日期
LocalDate date1=LocalDate.now();
// 2022-08-06
System.out.println("当前日期为"+date1);
// 3.获取年、月、日
// 获取当前的年份
System.out.println("年份:"+date1.getYear());
// 获取当前是几月 AUGUST
System.out.println("月份:"+date1.getMonth().getValue());
//
System.out.println("日:"+date1.getDayOfMonth());
// 今年的第多少天
System.out.println("今年的第"+date1.getDayOfYear()+"天");
// 获取当前这周的星期几 SATURDAY
System.out.println("星期:"+date1.getDayOfWeek().getValue());
}
指定的日期为2022-08-06
当前日期为2022-08-06
年份:2022
月份:8
日:6
今年的第218天
星期:6
时间操作
@Test
public void test02(){
LocalTime time=LocalTime.now();
System.out.println("当前时间为:"+time);
LocalTime time1=LocalTime.of(5,2,33);
System.out.println("自定义时分秒为:"+time1);
LocalTime time2=LocalTime.of(5,2);
System.out.println("自定义时分为:"+time2);
// 获取时间信息
System.out.println("小时:"+time.getHour());
System.out.println("分钟:"+time.getMinute());
System.out.println("秒:"+time.getSecond());
// 1s=1000ms 1ms=1000us 1us=1000ns
System.out.println("纳秒:"+time.getNano());
}
当前时间为:15:52:23.299
自定义时分秒为:05:02:33
自定义时分为:05:02
15
52
23
299000000
日期时间操作
LocalDateTime localDateTime=LocalDateTime.of(2022,8,6,5,3,12);
System.out.println("自定义时间为:"+localDateTime);
LocalDateTime localDateTime1=LocalDateTime.now();
System.out.println("当前时间为:"+localDateTime1);
// 获取日期时间
System.out.println(localDateTime1.getYear());
System.out.println(localDateTime1.getMonth().getValue());
System.out.println(localDateTime1.getDayOfMonth());
System.out.println(localDateTime1.getHour());
System.out.println(localDateTime1.getMinute());
System.out.println(localDateTime1.getSecond());
自定义时间为:2022-08-06T05:03:12
当前时间为:2022-08-06T16:02:44.820
2022
8
6
16
2
44
日期时间修改和比较
修改
LocalDateTime time = LocalDateTime.now();
System.out.println(time);
// 修改日期时间 with开头 不会修改原来的日期,而是生成一个新的对象
// 单独修改一个参数
LocalDateTime localDateTime = time.withYear(2010);
LocalDateTime localDateTime1 = time.withMonth(10);
LocalDateTime localDateTime2 = time.withDayOfMonth(20);
// 同时修改多个参数
LocalDateTime localDateTime3 = time.with(LocalDate.of(2020, time.getMonth(), time.getDayOfMonth()));
System.out.println(localDateTime3);
增加和减少
// 对日期进行加法和减法的操作
// 2天后
LocalDateTime localDateTime4 = time.plusDays(2);
// 2小时后
LocalDateTime localDateTime5 = time.plusHours(2);
// 10年前
LocalDateTime localDateTime6 = time.minusYears(10);
比较
// 日期时间比较
LocalDate date=LocalDate.now();
LocalDate date1=LocalDate.of(2020,12,9);
boolean after = date.isAfter(date1);
boolean before = date.isBefore(date1);
boolean equal = date.isEqual(date1);
System.out.println(after);
System.out.println(before);
System.out.println(equal);
// 是否是闰年
boolean leapYear = date1.isLeapYear();
System.out.println(leapYear);
格式化和解析
// 格式化和解析操作
String startTime="2022-01-02 12:12:12";
LocalDateTime localDateTime=LocalDateTime.now();
// 使用系统默认格式
DateTimeFormatter formatter=DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 将日期转换为字符串 2022-08-06T16:32:07.732
System.out.println(localDateTime.format(formatter));
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 使用自己指定的时间格式 2022-08-06 16:32:07
System.out.println(localDateTime.format(formatter1));
// 将string解析成日期
System.out.println(LocalDateTime.parse(startTime, formatter1));
Instant
时间戳/时间线,内部保存了1970-01-01 00:00:00以来的秒
// 时间戳,用来统计时间消耗
Instant now=Instant.now();
// 2022-08-06T08:39:16.662Z
System.out.println(now);
// 获取现在的纳秒,因为随时会变
// 695000000
System.out.println(now.getNano());
// 获取从1970-01-01到现在的秒数
System.out.println(now.getEpochSecond())
计算日期时间差
JDK8中提供了两个工具类Duration/Period:计算日期时间差
- Duration:用来计算两个时间差(LocalTime)
- Period:用来计算两个日期差(LocalDate)
// 计算时间差
LocalTime now = LocalTime.now();
LocalTime time = LocalTime.of(22, 48, 59);
System.out.println("now = " + now);
// 通过Duration来计算时间差
Duration duration = Duration.between(now, time);
System.out.println(duration.toDays()); // 0
System.out.println(duration.toHours()); // 6
System.out.println(duration.toMinutes()); // 368
System.out.println(duration.toMillis()); // 22124240
// 计算日期差
LocalDate nowDate = LocalDate.now();
LocalDate date = LocalDate.of(1997, 12, 5);
// 后面减去前面
Period period = Period.between(date, nowDate);
System.out.println(period.getYears()); // 24
System.out.println(period.getMonths()); // 8
System.out.println(period.getDays()); // 2
TemporalAdjuster:时间校正器
有时候我们可以需要如下调整:将日期调整到"下个月的第一天"等操作。这时我们通过时间校正器效果可能会更好。
- TemporalAdjuster:时间校正器
- TemporalAdjusters:通过该类静态方法提供了大量的常用TemporalAdjuster的实现。
LocalDateTime now=LocalDateTime.now();
// 将当前的日期调整到下个月的1号
TemporalAdjuster adjuster=(temporal) -> {
LocalDateTime dateTime= (LocalDateTime)temporal;
LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
return nextMonth;
};
// 通过TemporalAdjusters
LocalDateTime with = now.with(adjuster);
// 直接获取
LocalDateTime with1 = now.with(TemporalAdjusters.firstDayOfNextMonth());
// 下个月1号
System.out.println(with);
日期时间的时区
jdk8加入对日期时间时区的支持。LocalDate、LocalTime、LocalDateTime是不带时区的,带时区的日期时间类为:ZonedTime、ZonedDate、ZonedDateTime。
其中每个时区都对应着ID。ID的格式为:区域/城市,比如Asia/Shanghai。
ZoneId:该类中包含了所有时区的信息。
@Test
public void test(){
// 获取所有时区ID
ZoneId.getAvailableZoneIds().forEach(System.out::println);
}
@Test
public void test1(){
// 获取当前时间 中国使用东八区,比标准时间晚8个小时
LocalDateTime localDateTime=LocalDateTime.now();
// 2022-08-06T18:18:32.460
System.out.println(localDateTime);
// 获取标准时间 也就是减去8个小时
ZonedDateTime now = ZonedDateTime.now(Clock.systemUTC());
// 2022-08-06T10:20:28.279Z
System.out.println(now);
// 使用计算机默认时区
ZonedDateTime zonedDateTime=ZonedDateTime.now();
// 2022-08-06T18:17:55.604+08:00[Asia/Shanghai]
System.out.println(zonedDateTime);
// 使用指定的时区创建时间
ZonedDateTime dateTime=ZonedDateTime.now(ZoneId.of("America/Cuiaba"));
// 2022-08-06T06:23:19.291-04:00[America/Cuiaba]
System.out.println(dateTime);
}
其他特性
重复注解
自从Java 5中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8中使用@Repeatable注解定义重复注解。
1.定义重复注解的容器
package com.xqm.jdk8.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @Retention作用是定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中.
*
* 从注释上看:
*
* source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
*
* class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
*
* runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
*
* 这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
*
* 生命周期长度 SOURCE < CLASS < RUNTIME
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
2.定义可以重复的注解
package com.xqm.jdk8.annotation;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 表示这个注解能重复使用
*/
@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
3.定义测试类
package com.xqm.jdk8.annotation;
import java.lang.annotation.Annotation;
/**
* 可以重复注解
*/
@MyAnnotation("test1")
@MyAnnotation("test2")
@MyAnnotation("test3")
public class MyAnnoTest {
@MyAnnotation("fun1")
@MyAnnotation("fun2")
public void test(){
}
/**
* 解析注解
* @param args
*/
public static void main(String[] args) throws NoSuchMethodException {
MyAnnotation[] annotationsByType = MyAnnoTest.class.getAnnotationsByType(MyAnnotation.class);
// 获取类中标注的注解
// test1
// test2
// test3
for (MyAnnotation myAnnotation : annotationsByType) {
System.out.println(myAnnotation.value());
}
// 获取方法中标注的重复注解
MyAnnotation[] tests = MyAnnoTest.class.getMethod("test").getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation test : tests) {
System.out.println(test.value());
}
}
}
类型注解
JDK 8为@Target元注解新增了两种类型: TYPE_PARAMETER , TYPE_USE 。
- TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 类型参数声明如: <T>
- TYPE_USE :表示注解可以在任何用到类型的地方使用。
TYPE_PARAMETER
@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}
使用:
public class TypeDemo01 <@TypeParam T> {
public <@TypeParam K extends Object> K test01(){
return null;
}
}
TYPE_USE
@Target(ElementType.TYPE_USE)
public @interface NotNull {
}
使用
public class TypeUseDemo01 {
public @NotNull Integer age = 10;
public Integer sum(@NotNull Integer a,@NotNull Integer b){
return a + b;
}
}