java-----JDK(8)的新特性
文章目录
前言
Open jdk 和 Oracle jdk
open jdk :
Sun 2006年开源项目 是java se 平台版的开源和免费实现
Oracle jdk:
基于Open jdk 原码的商业版本
JDK8 新特性
1、Lambda 表达式
Lambda是一个匿名函数,可以理解为一段可以传递的代码。
优点:简化匿名内部类的使用,语法更加简单。
Lambda的语法非常简洁,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意:
- 方法的参数或局部变量类型必须为接口才能使用Lambda
- 接口中有且仅有一个抽象方法
1.1.标准格式
(参数类型 参数名称) -> {
代码体;
}
格式说明:
(参数类型 参数名称):参数列表
{代码体;}:方法体
-> :箭头,分隔参数列表和方法体
Lambda与方法的对比
匿名内部类
public void run() {
System.out.println("aa");
}
Lambda
() -> System.out.println("bb!")
/**
* @author zhangyifan
* @version 8.0
* @description:
* @date 2021/12/21 16:59
*/
public class Demo01Lambdaintro {
public static void main(String[] args) {
//很冗余匿名的方法
//lambda表达式 体现的是函数式编程思想 ,只需要将要执行的代码放到函数中(函数就是类中的方法)
//Lambda 就是一个匿名函数,只需要吧代码放到Lambda表达式中
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("原始的线程1");
}
}).start();
// Lambda表达式
new Thread(() -> {
System.out.println("sdsdsd");
}).start();
//Lambda 表达式的缓存 : 可以简化匿名内部类 ,语法更简单
}
}
1.2、Lambda和匿名内部类对比
- 所需的类型不一样
匿名内部类,需要的类型可以是类,抽象类,接口
Lambda表达式,需要的类型必须是接口 - 抽象方法的数量不一样
匿名内部类所需的接口中抽象方法的数量随意
Lambda表达式所需的接口只能有一个抽象方法 - 实现原理不同
匿名内部类是在编译后会形成class
Lambda表达式是在程序运行的时候动态生成class
1.3、方法引用
了解Lambda的冗余场景
掌握方法引用的格式
了解常见的方法引用方式
使用Lambda表达式求一个数组的和
public class Demo01MethodRefInfo {
public static void getMax(int[] arr){
int sum = 0;
for (int j : arr) {
sum += j;
}
System.out.println(sum);
}
public static void main(String[] args) {
/*printMax((int[] arr)->{
getMax(arr);
});*/
//如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引
//用”过去就好了:
printMax(Demo01MethodRefInfo::getMax);
//请注意其中的双冒号::写法,这被称为“方法引用”,是一种新的语法。
}
public static void printMax(Consumer<int[]> consumer){
int[] arr = {1,2,3,4,5,6,7,8,9,10};
consumer.accept(arr);
}
}
符号表示 : ::
符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用。
应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。
1.3.1、常见的方法引用方式
- instanceName::methodName 对象::方法名
- ClassName::staticMethodName 类名::静态方法
- ClassName::methodName 类名::普通方法
- ClassName::new 类名::new 调用的构造器
- TypeName[]::new String[]::new 调用数组的构造器(和上面有个一样其实)
1.3.2、对象名::引用成员方法
最常见的一种用法,与上例相同。
如果一个类中已经存在了一个成员方法,则可以通过对象名引用成员方法,
@Test
public void test01(){
Date date = new Date();
Supplier<Long> supp = () -> {
return date.getTime();
};
System.out.println(supp.get());
//方法引用的注意事项
//1. 被引用的方法,参数要和接口中抽象方法的参数一样
// 2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值
Supplier<Long> getTime = date::getTime;
System.out.println(getTime.get());
}
1.3.3、类名::引用静态方法
由于在java.lang.System类中已经存在了静态方法方法时,可以使用方法引用
// 类名::静态方法
@Test
public void test02(){
java.util.function.Supplier<Long> supplier = () -> {
return System.currentTimeMillis();
};
System.out.println(supplier.get());
java.util.function.Supplier<Long> currentTimeMillis = System::currentTimeMillis;
System.out.println( currentTimeMillis.get());
}
1.3.4、类名::引用实例方法
Java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,
实际上是拿第一个参数作为方法的调用者。
1.3.5、类名::new引用构造器
1.3.6、数组::new 引用数组构造器
小结
方法引用是对Lambda表达式符合特定情况下的一种缩写,它使得我们的Lambda表达式更加的精简,也可以理解为 Lambda表达式的缩写形式
, 不过要注意的是方法引用只能"引用"已经存在的方法!
2、JDK 8接口新增的两个方法
jdk8以前的接口
interface 接口名 {
静态常量;
抽象方法;
}
jdk8以后
interface interfaceName{
静态常量;
抽象方法;
默认方法;
静态方法;
}
为什么要进行这个修改
就是不利于接口的扩展
白话就是,如果使用接口在以前就必须重写抽象方法,但有时候根本就不需要,
jdk8在Map中新增了forEach方法
这个forEach方法是 默认方法,如果是抽象方法,那么所有实现类都必须实现这个方法,这样是非常浪费的,而默认方法就解决了这个功能
接口中的默认方法实现类可以不重写,可以直接用,或者根据需要重写,方便扩展,开闭原则
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
2.1、接口的默认方法
interface interfaceName{
修饰符 default 返回值类型 方法名(){
代码块;
}
}
使用
- 实现类直接调用接口默认方法
- 实现类重写接口默认方法
2.2、接口的静态方法
为了方便接口扩展 ,注意静态方法不能被重写,
interface interfaceName {
修饰符 static 返回值类型 方法名(){
代码;
}
}
直接使用接口名调用即可:接口名.静态方法名();
2.3、接口默认方法和静态方法的区别
- 默认方法通过实例调用,静态方法通过接口名调用。
- 默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法。
- 静态方法不能被继承,实现类不能重写接口静态方法,只能使用接口名调用。
如果这个方法需要被实现类继承或重写,使用默认方法,如果接口中的方法不需要被继承就使用静态方
法
3、内置函数式接口
3.1、为什么要有内置函数
因为Lambda表达式的前提需要函数式接口,而且不需要去关心接口和抽象方法的名字,所以为了Lambda的使用,jdk提供了很多常用的函数式接口。
package org.example.demo03functionalinterface;
import java.lang.reflect.Array;
import java.security.Key;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author zhangyifan
* @version 8.0
* @description: 函数式接口
* @date 2022/3/31 22:45
*/
public class Demo01UserFunctionInterface {
public static void main(String[] args) {
//调用函数式接口中的方法
method(arr -> {
int sum = 0;
for (int n : arr){
sum +=n;
}
return sum;
});
methodArrayList((ArrayList<Integer> arrayList ) -> {
//吧集合转数组
Object[] objects1 = arrayList.toArray();
Integer[] objects= Arrays.copyOfRange(objects1,0,objects1.length,Integer[].class);
/*
//排序1
for (int i=0;i< objects.length;i++){
for (int j=0;j< objects.length;j++){
if ( objects[i] > objects[j] ) {
// 赋值
Integer interger=objects[i];
objects[i]=objects[j];
objects[j]=interger;
}
}
}*/
//排序2
Arrays.sort(objects);
ArrayList<Integer> list=new ArrayList<>(Arrays.asList(objects));
return list ;
});
}
//使用自定义的函数式接口作为方法参数
public static void method(Operator operator){
int[] arr = {1,2,3,4};
int sum = operator.getSum(arr);
System.out.println("sum="+sum);
}
//特别垃圾的排序
public static void methodArrayList( Operation operation){
ArrayList arrayList = new ArrayList<Integer>();
arrayList.add(4);arrayList.add(5);arrayList.add(6);arrayList.add(1);arrayList.add(10);
ArrayList<Integer> sort = operation.getSort(arrayList);
sort.forEach(System.out::println);
}
}
@FunctionalInterface
interface Operator{
public abstract int getSum(int[] arr);
}
interface Operation{
public abstract ArrayList<Integer> getSort(ArrayList<Integer> arrayList);
}
3.1.1、Supplier接口
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类
型的对象数据。
供给型接口,通过接口的get方法得到一个值,没有参数有返回值的接口。
实例
- 使用Lambda表达式返回数组元素最大值
- 使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用
java.lang.Integer 类
package org.example.demo03functionalinterface;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* @author zhangyifan
* @version 8.0
* @description: Supplier函数式接口 供应商
* @date 2022/4/1 12:32
*/
public class Demo02Supplier {
public static void main(String[] args) {
printMax(()->{
int [] arr= {12,13,14,15,11};
//先排序,最后就是最大的
Arrays.sort(arr);//排序方法
return arr[arr.length - 1];// 最后就是最大的
});
}
public static void printMax(Supplier<Integer> supplier){
int max = supplier.get();
System.out.println("max="+ max);
}
}
3.1.2、Consumer接口
java.util.function.Consumer
接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出
NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
实例
使用Lambda表达式将一个字符串转成大写和小写的字符串
Consumer消费型接口,可以拿到accept方法参数传递过来的数据进行处理, 有参无返回的接口。基本使用如:
import java.util.Locale;
import java.util.function.Consumer;
/**
* @author zhangyifan
* @version 8.0
* @description: 消费者
* @date 2022/4/1 13:33
*/
public class Demo03Comsumer {
public static void main(String[] args) {
//只要一个值的时候不用小括号标数据类型
test(s -> {
System.out.println(s.toLowerCase(Locale.ROOT));
});
}
public static void test (Consumer<String> consumer){
consumer.accept("HelloWorld");
}
}
默认方法:andThen
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。
要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合
的情况:
package org.example.demo03functionalinterface;
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author zhangyifan
* @version 8.0
* @description:运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。
* 当然,通过链式写法可以实现更多步骤的组合。
* @date 2022/4/1 14:14
*/
public class Demo03AndThen {
public static void main(String[] args) {
//Lambd表达式
test((s) -> {
System.out.println(s.toLowerCase());//大写
},s -> {
System.out.println(s.toUpperCase());//小写
});
//Lambda表达式简写 如果只有一个方法 大括号就可以取消
test(s -> System.out.println(s.toLowerCase()), s -> System.out.println(s.toLowerCase()));
}
public static void test (Consumer<String> c1, Consumer<String > c2){
String str = "HelloWord";
// c1.accept(str);
// c2.accept(str);
// c1.andThen(c2).accept(str);
c2.andThen(c1).accept(str);
}
}
3.1.3、Function接口
java.util.function.Function<T,R>
接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
package java.util.function;
import java.util.Objects;
@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;
}
}
例如:将String类型转换为Integer类型。
public static void main(String[] args) {
test(Integer::parseInt);
}
public static void test(Function<String,Integer> function){
Integer in = function.apply("10");
System.out.println("in: " + (in +5));
}
例子: 使用Lambda表达式将字符串转成数字
public static void main(String[] args) {
String a = "AAAA";
Consumer<String> consumer = v -> {
System.out.println( v.toLowerCase(Locale.ROOT));
};
consumer.accept(a);
test(v -> {
System.out.println(v+1);
});
}
public static void test (Consumer<Integer> consumer){
System.out.println("sssssssssss");
consumer.accept(1);
}
默认方法:andThen
将字符转Integer 在乘5
public static void main(String[] args) {
getName(Integer::parseInt, (Integer i)-> i* 5)
;
}
public static void getName (Function<String,Integer> f1,Function<Integer,Integer> f2){
Integer apply = f1.andThen(f2).apply("6");
System.out.print("apply"+apply );
}
请注意,Function的前置条件泛型和后置条件泛型可以相同。
3.1.4、Predicate接口
Predicate接口用于做判断,返回boolean类型的值
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
例子 使用Lambda判断一个人名如果超过3个字就认为是很长的名字
public class Demo05Predicate {
public static void main(String[] args) {
test(s -> s.length() > 3, "古力娜扎");
}
public static void test(Predicate<String> predicate, String name) {
boolean test = predicate.test(name);
System.out.println("名字答于3不" + test);
}
}
默认方法: and
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个现“并且”的效果时,可以使用default方法and
默认方法 negate (取反)
默认方法:or (或)
public class Demo05PredicateAndOrNegate {
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
// 使用Lambda表达式判断一个字符串中包含W或者包含H
// 使用Lambda表达式判断一个字符串中即不包含W
public static void main(String[] args) {
/* test((String s)->{
return s.contains("W");
},(String s)->{
return s.contains("H");
});*/
test(s -> s.contains("H"), s -> s.contains("W"));
}
public static void test(Predicate<String> predicate, Predicate<String> predicate1) {
String result = "elloWorld";
// 使用Lambda表达式判断一个字符串中即包含W,也包含H
/* boolean out = predicate.test(result);
boolean test = predicate.test(result);
if (out && test){
System.out.println("即包含W,也包含H");
}*/
/* boolean test = predicate.and(predicate1).test(result);
if (test){
System.out.println("即包含W,也包含H");
}*/
//使用Lambda表达式判断一个字符串中包含W或者包含H
// 与and的“与”类似,默认方法or实现逻辑关系中的“或
/* boolean test = predicate.or(predicate1).test(result);
if (test){
System.out.println("包含W或者包含H");
}*/
//“与”、“或”已经了解了,剩下的“非”(取反)也会简单。 negate
boolean test = predicate.negate().test(result);
if (test) {
System.out.println("不包含H");
}
}
}
4、Stream流
parallelStream线程问题
5、Optional类
6、新日期类和时间api
6.1 新时间介绍
API
- 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 :泰国佛教历
- MinGuo Date :中华民国历
- JapaneseDate :日本
- HiRahDate :伊斯兰
6.2 LocalDate
@Test
public void test01() {
//指定日期
LocalDate localDate = LocalDate.of(2019, 10, 16);
//2019-10-16
System.out.println(localDate);
//当前日期
LocalDate localDate1 = LocalDate.now();
//2024-05-25
System.out.println(localDate1);
//获取时间信息
//2024
System.out.println(localDate1.getYear());
//5
System.out.println(localDate1.getMonthValue());
//25
System.out.println(localDate1.getDayOfMonth());
//SATURDAY
System.out.println(localDate1.getDayOfWeek());
//146
System.out.println(localDate1.getDayOfYear());
//检查年份是否为闰年。
//true
System.out.println(localDate1.isLeapYear());
}
接口返回格式化
@JsonFormat(pattern = "yyyy-MM")
private LocalDate time;
接口传递格式化 yyyy-MM
前台传递月份,自动补齐
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate TimeBegin;
public class LocalDateDeserializer extends StdDeserializer<LocalDate> {
public LocalDateDeserializer() {
this(null);
}
public LocalDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public LocalDate deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
String dateString = jsonparser.getText();
// 假设每月的第一天
if (dateString.isEmpty()) {
return null;
}
dateString += "-01";
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
return LocalDate.parse(dateString, formatter);
}
}
6.3 LocalTime
@Test
public void test02() {
LocalTime localTime = LocalTime.of(10, 16, 9);
System.out.println(localTime);
// 得到指定的时间
LocalTime time = LocalTime.of(12,15, 28, 129_900_000);
System.out.println("time = " + time);
LocalTime localTime1 = LocalTime.now();
System.out.println(localTime1);
System.out.println(localTime1.getHour());
System.out.println(localTime1.getMinute());
System.out.println(localTime1.getSecond());
System.out.println(localTime1.getNano());
System.out.println(localTime1.plusHours(2));
System.out.println(localTime1.minusHours(2));
System.out.println(localTime1.plusMinutes(2));
System.out.println(localTime1.minusMinutes(2));
System.out.println(localTime1.plusSeconds(2));
System.out.println(localTime1.minusSeconds(2));
System.out.println(localTime1.plusNanos(2));
System.out.println(localTime1.minusNanos(2));
}
6.4 LocalDateTime
//LocalDateTime :获取日期时间的信息。LocalDateTime格式为yyyy-mm-ddTHH:mm:ss.n
@Test
public void test03() {
LocalDateTime localDateTime = LocalDateTime.of(2019, 10, 16, 10, 16, 9);
System.out.println(localDateTime);//2019-10-16T10:16:09
LocalDateTime localDateTime1 = LocalDateTime.now();
System.out.println(localDateTime1);//2024-05-13T17:08:27.455
System.out.println(localDateTime1.getYear());
System.out.println(localDateTime1.getMonthValue());
System.out.println(localDateTime1.getDayOfMonth());
System.out.println(localDateTime1.getDayOfWeek());
System.out.println(localDateTime1.getDayOfYear());
//......
}
修改日期
@Test//修改日期
public void test04() {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
LocalDateTime localDateTime = now.withYear(2042);
System.out.println("修改年份"+localDateTime);//修改年份2042-05-13T17:13:45.887
localDateTime = now.withMonth(12);
System.out.println("修改月份"+localDateTime);//修改月份2024-12-13T17:13:45.887
//再当前对象的基础上加上或减去指定的时间
LocalDateTime localDateTime3 = now.plusDays(5);
System.out.println("5天后: " + localDateTime3);
System.out.println("now == localDateTime: " + (now == localDateTime3));
System.out.println("10年后: " + now.plusYears(10));
System.out.println("20月后: " + now.plusMonths(20));
System.out.println("20年前: " + now.minusYears(20));
System.out.println("5月前: " + now.minusMonths(5));
System.out.println("100天前: " + now.minusDays(100));
}
7、重复注解与类型注解
参考文章
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)