lambda表达式
Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda 表达式(Lambda expression)可以看作是一个匿名函数,基于数学中的λ演算得名,也可称为闭包
基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表达式由三部分组成:
-
paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
-
->:可理解为“被用于”的意思
-
方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法
注意:
-
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
-
如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
定义方式:@FunctionalInterface interface NoParameterNoReturn { //注意:只能有一个方法 void test(); }
@FunctionalInterface interface NoParameterNoReturn { void test(); default void test2() { System.out.println("JDK1.8新特性,default默认方法可以有具体的实现"); } }
Lambda表达式的基本使用
1.无返回值函数式接口
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
//无返回值两个参数
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
public class TestDemo {
public static void main(String[] args) {
NoParameterNoReturn n = ()->{
System.out.println("无参数无返回值");
};
n.test();
OneParameterNoReturn o = (a)-> {
System.out.println("无返回值一个参数"+a);
};
o.test(666);
MoreParameterNoReturn m = (int a,int b)->{
System.out.println("无返回值两个参数"+a+" "+b);
};
m.test(666,999);
}
}
2.有返回值函数接口
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
//有返回值多个参数
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
public class TestDemo {
public static void main(String[] args) {
NoParameterReturn n = ()->{
return 666;
};
int ret1 = n.test();
System.out.println(ret1);
System.out.println("================");
OneParameterReturn o = (int a)->{
return a;
};
int ret2 = o.test(999);
System.out.println(ret2);
System.out.println("================");
MoreParameterReturn m = (int a,int b)-> {
return a+b;
};
int ret3 = m.test(10,90);
System.out.println(ret3);
}
}
3.语法精简
Lambda表达式的语法还可以精简,显得非常有逼格,但是可读性就非常差。
-
参数类型可以省略,如果需要省略,每个参数的类型都要省略。
-
参数的小括号里面只有一个参数,那么小括号可以省略
-
如果方法体当中只有一句代码,那么大括号可以省略
-
如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字
把上面的代码精简示例:
public static void main(String[] args) {
MoreParameterNoReturn moreParameterNoReturn = (a, b)->{
System.out.println("无返回值多个参数,省略参数类型:"+a+" "+b);
};
moreParameterNoReturn.test(20,30);
OneParameterNoReturn oneParameterNoReturn = a ->{
System.out.println("无参数一个返回值,小括号可以省略:"+ a);
};
oneParameterNoReturn.test(10);
NoParameterNoReturn noParameterNoReturn = ()->System.out.println("无参数无返回值,方法体中只有 一行代码");
noParameterNoReturn.test();
//方法体中只有一条语句,且是return语句
NoParameterReturn noParameterReturn = ()-> 40;
int ret = noParameterReturn.test();
System.out.println(ret);
}
三、变量捕获
Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解Lambda 表达式的作用域 。Java当中的匿名类中,会存在变量捕获
这样会报错
总结
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读
优点:
- 代码简洁,开发迅速
- 方便函数式编程
- 非常容易进行并行计算
- Java 引入 Lambda,改善了集合操作
缺点: - 代码可读性变差
- 在非并行计算中,很多计算未必有传统的 for 性能要高
- 不容易进行调试
接口中新增的方法
jdk1.8后对接口做了增加,接口中可以有默认方法和静态方法’
interface name{
静态常量;
抽象方法;
默认方法;
静态方法;
}
2.默认方法
2,1为什么要增加默认方法
接口中如果要新增抽象方法,所有实现类必须要重写这个方法,不利于接口的扩展.
2.2接口中默认方法的语句格式
interface 接口名{
修饰符 default 返回值类型 方法名{
}
}
2.3接口中的默认方法是的使用方式
- 实现类直接调用接口的默认方法.
- 实现类重写接口的默认方法.
3.静态方法
3,1语法规则
interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}
3.2静态方法的使用
接口的静态方法在实现类中是不能被重写的,调用的话只能通过接口类型来实现:接口名.静态方法();
3.3两者的区别
1.默认方法通过实例对象调用,静态方法通过接口名调用
2.默认方法可以被继承,实现类可以直接调用默认方法,也可以重写默认方法
3.静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用
函数式接口
1.函数式接口的由来
使用lambda表达式的前提是需要函数式接口,而lambda表达式不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型
因此jdk中提供了大量的常用的函数式接口.
2.函数式接口的介绍
2.1supplier
无参有返回值接口
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
2.2consumer
有参无返回值的
@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); };
}
}
2.3function
有参有返回值的
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;
}
}
2.4Predicate
返回值为boollean值的
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);
}
}
方法引用
1.为什么要用
1.lambda表达式冗余
package org.example.fun;
import java.util.Arrays;
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
test(ConsumerTest::sum);
}
public static void sum(int[] a){
int sum=0;
for (int i : a) {
sum += i;
}
System.out.println(sum);
}
public static void test(Consumer<int[]> consumer){
int[] a={1,4,5,6,7};
consumer.accept(a);
}
}
2.方法引用的格式
符号表示 ::
符号说明:双冒号为方法应用运算符,而它所在的表达式被称为方法引用
常见的引用方式:
-
对象:方法名
public class Test01 { public static void main(String[] args) { Date now = new Date(); Supplier<Long> supplier= now::getTime; System.out.println(supplier.get()); } }
-
类名:静态方法
public class Test01 { public static void main(String[] args) { Supplier<Long> supplier= System::currentTimeMillis; System.out.println(supplier.get()); } }
-
类名::引用实例方法
public class Test01 { public static void main(String[] args) { BiFunction<String,Integer,String> function=String::substring; String hello = function.apply("hello", 3); System.out.println(hello); } }
-
new 类名::new调用 的构造器
-
new string[]::new 调用数组的构造器
stream API
1.集合处理数据的弊端
当我们在需要对集合中的元素进行操作时,除了必须的添加,删除,获取外,最典型的就是集合的遍历
public class StreamTest01 {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三", "李四", "王五");
ArrayList<String> strings = new ArrayList<>();
//1.获取所有张的信息
for (String s : list) {
if (s.startsWith("张")){
strings.add(s);
}
}
System.out.println(strings);
}
}
public class StreamTest01 {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三三", "李四", "王五");
ArrayList<String> strings = new ArrayList<>();
//1.获取所有张的信息
/*for (String s : list) {
if (s.startsWith("张")){
strings.add(s);
}
}*/
//所有姓张并且长度为三
list.stream()
.filter(s->s.startsWith("张"))
.filter(s->s.length()==3)
.forEach(System.out::println);
}
}
stream和io没有任何关系,更像一个流水线,通过多个过程加工数据.
stream的来各种获取方式
根据collection获取
collection接口中加入了default方法stream,collection接口下的所有实现都可以通过stream获取Stream流.
map接口没有实现collection接口,可以根据map获取对应的key,value的集合再获取stream
通过stream的of方法
在实际开发中.不可避免的会操作数组中的数据,由于数组对象不能添加默认方法,所有stream接口中提供了静态方法of
Stream<String> stream = Stream.of("1", "2", "2");
String[] strings={"aa", "bb", "cc", "dd"};
Stream<String> strings1 = Stream.of(strings);
strings1.forEach(System.out::println);
//基本数据类型的数组是不行的
int[] a={1,2,3,4};
Stream.of(a).forEach(System.out::println);
注意事项
stream只能操作一次,stream方法返回的是新的流,stream不调用终结方法,中间的操作不会执行.
foreach
用来遍历流中的数据的
void forEach(Consumer<? super ?> action)
该方法接受一个Consumer接口,会将每一个流函数处理
conut
long count()
public class StreamTest03 {
public static void main(String[] args) {
long count = Stream.of("1", "2", "2").count();
}
}
用来统计其中的元素个数的
fillter
用来过滤数据,返回符合条件的.
Stream<T> filter(Predicate<? super T> predicate);
limit
limit方法可以对流进行截取
Stream<T> limit(long maxSize);
skip
如何希望跳过前面几个元素则可以使用skip
Stream<T> skip(long n);
map
如果我们需要将流中的元素映射到另一个流中,可以使用map方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public class StreamTest03 {
public static void main(String[] args) {
Stream.of("1", "2", "2").map(Integer::parseInt)
.forEach(System.out::println);
}
}
sorted
Stream<T> sorted();
可以对流对象进行排序
distinct
去掉重复数据
match
find
max和min
reduce方法
Integer sum = Stream.of(1, 2, 3).reduce(0, Integer::sum);
map和reduce组合
maptoint :Integer占用的内存比int多很多,为了提高代码效率可以先将integer数据转换为int数据让后再操作
public class StreamTest03 {
public static void main(String[] args) {
Integer[] arr={1,2,3};
Stream.of(arr).mapToInt(Integer::intValue)
.filter(i->i>=3)
.forEach(System.out::println);
}
}
contat
如何有两个流想要合并可以使用stream的静态方法
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));
}
stream结果收集
public class StreamReduceTest {
public static void main(String[] args) {
List<Integer> collect = Stream.of(1, 2, 3)
.collect(Collectors.toList());
System.out.println(collect);
Set<Integer> collect1 = Stream.of(1, 2, 3)
.collect(Collectors.toSet());
//具体的类型转化如arraylist,hashset
ArrayList<Integer> collect2 = Stream.of(1, 2, 3)
.collect(Collectors.toCollection(ArrayList::new));
}
}
对流的数据做聚合计算
当我们使用stream流处理数据后,可以数据库的聚合函数一言对某个字段进行操作
optional类
主要解决空指针
以前对null的处理
String userName=null;
if(userName!=null) {
System.out.println(userName.length());
}else {
System.out.println("string is empty");
}
optional是一个没有子类的工具类,optionnal是一个可以为空的容器对象,它的主要作用就是避免null检查,防止出现空指针异常
获取方式
public class OptionalTest01 {
public static void main(String[] args) {
//通过of获取
Optional<String> op1 = Optional.of("张三");
//of方法不支持null
// Optional<Object> o = Optional.of(null);
//ofNullable可以传入空
Optional<Object> o = Optional.ofNullable(null);
//将空值封装成对象
Optional<Object> empty = Optional.empty();
}
}
常用的用法
Optional<String> op1 = Optional.of("张三");
Optional<String> op2 = Optional.empty();
//获取optional里面的值,get()如果optional有值则返回,否则就抛出NoSuchElementException
String s = op1.get();
//isPresent判断是否包含值,包含返回true不包含返回false
//orEls如果是空值则会替换
String s1 = op1.orElse("李四");
System.out.println(s1);
op2.orElseGet(()->"aaa");
System.out.println(op2);
//根据person对象将name大写返回
public static String test03(Optional<Person> optional){
if(optional.isPresent()){
return optional.map(Person::getName)
.map(String::toUpperCase)
.orElse(null);
}
return null;
}
新时间日期api
-
设计不合理,在java.util和java.sql的包中都含有日期类,java.util同时包含日期和时间的,java.sql.date仅仅包含日期
-
线程不安全,java.util.date是线程不安全的
-
时区处理麻烦,日期并不提供国际化,灭有时区支持
常用的方法
package org.example.date;
import com.sun.org.apache.xpath.internal.SourceTree;
import javax.xml.soap.Node;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
public class DateDemo01 {
public static void main(String[] args) {
//创建指定的日期
LocalDate localDate = LocalDate.of(2023, 9, 22);
System.out.println(localDate);
//获取当前日期
LocalDate now = LocalDate.now();
System.out.println(now);
//根据localDate对象获取对应的日期信息
System.out.println("年"+now.getYear());
System.out.println("月"+now.getMonth().getValue());
System.out.println("日"+now.getDayOfMonth());
System.out.println("周"+now.getDayOfWeek().getValue());
time();
dateTime();
}
public static void time(){
//获取指定的时间
LocalTime localTime = LocalTime.of(10, 46, 33, 1242);
System.out.println(localTime);
//获取当前时间
LocalTime now = LocalTime.now();
System.out.println(now);
//获取时间
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
System.out.println(now.getNano());
}
public static void dateTime(){
//获取指定的日期时间
//LocalDateTime.of()
//获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//获取日期时间信息
//now.get()
}
}
instant类
时间戳类,内部保存了从1970:1:1:00:00:00的秒和纳秒
duration
用来计算时间差
);
dateTime();
}
public static void time(){
//获取指定的时间
LocalTime localTime = LocalTime.of(10, 46, 33, 1242);
System.out.println(localTime);
//获取当前时间
LocalTime now = LocalTime.now();
System.out.println(now);
//获取时间
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
System.out.println(now.getNano());
}
public static void dateTime(){
//获取指定的日期时间
//LocalDateTime.of()
//获取当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//获取日期时间信息
//now.get()
}
}
### instant类
时间戳类,内部保存了从1970:1:1:00:00:00的秒和纳秒
### duration
用来计算时间差