文章目录
Interface
初衷是面向抽象,提高扩展性。Interface 修改的时候,实现它的类也必须跟着改。
为了解决接口的修改与现有的实现不兼容的问题。新 interface
的方法可以用default
或 static
修饰,这样就可以有方法体,实现类也不必重写此方法。
这 2 个修饰符的区别主要也是普通方法和静态方法的区别:
default
修饰的方法,是普通实例方法,可以用this
调用,可以被子类继承、重写static
修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用
java
public interface InterfaceNew {
static void sm() {
System.out.println("interface提供的方式实现");
}
default void def() {
System.out.println("interface default方法");
}
//须要实现类重写
void f();
}
public interface InterfaceNew1 {
default void def() {
System.out.println("InterfaceNew1 default方法");
}
}
这里可以看到两个接口都有 def
方法,而且两个接口没有继承关系,那么如果一个类实现了这两个接口,就必须要重写 def
方法
java
public class InterfaceNewImpl implements InterfaceNew , InterfaceNew1{
public static void main(String[] args) {
InterfaceNewImpl interfaceNew = new InterfaceNewImpl();
interfaceNew.def();
}
// `def` 方法的重写
@Override
public void def() {
InterfaceNew1.super.def();
}
//对应的方法实现
@Override
public void f() {
}
}
这样接口就可有自己的方法实现了!!!那岂不是和抽象类很像了???
区别:
- 接口多实现,类单继承
- 接口的方法是 public abstract 修饰,变量是 public static final 修饰。 abstract class 可以用其他修饰符
牢记:接口是接口,类是类
函数式接口
SAM 接口:Single Abstract Method interfaces 有且只有一个抽象方法,但可以有多个非抽象方法的接口
在包 java.util.function
包里
Lambda表达式
Lambda 表达式可以使代码变的更加简洁紧凑,提高代码的可读性
语法格式
java
(parameters) -> expression 或
(parameters) ->{ statements; }
替代匿名类
过去给方法传动态参数的唯一方法是使用内部类
Runnnable
接口
java
//以前的格式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The runable now is using!");
}
}).start();
//用lambda的格式
new Thread(() -> System.out.println("It's a lambda function!")).start();
Comparator
接口
java
List<Integer> strings = Arrays.asList(1, 2, 3);
Collections.sort(strings, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
Listener
接口
java
JButton button = new JButton();
button.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
e.getItem();
}
});
//lambda
button.addItemListener(e -> e.getItem());
- 自定义接口
java
@FunctionalInterface
public interface LambdaInterface {
void f();
}
//使用
public class LambdaClass {
public static void forEg() {
lambdaInterfaceDemo(()-> System.out.println("自定义函数式接口"));
}
//函数式接口参数
static void lambdaInterfaceDemo(LambdaInterface i){
i.f();
}
}
集合迭代
java
void lamndaFor() {
List<String> strings = Arrays.asList("1", "2", "3");
//传统foreach
for (String s : strings) {
System.out.println(s);
}
//Lambda foreach
strings.forEach((s) -> System.out.println(s));
//or 这里把参数也一同省略了
strings.forEach(System.out::println);
//map
Map<Integer, String> map = new HashMap<>();
map.forEach((k,v)->System.out.println(v));
}
方法引用
::
关键字来传递方法或者构造函数引用,要求表达式的返回类型是 functiuon-interface
java
public class LambdaClassSuper {
LambdaInterface sf(){
return null;
}
}
public class LambdaClass extends LambdaClassSuper {
public static LambdaInterface staticF() {
return null;
}
public LambdaInterface f() {
return null;
}
void show() {
//1.调用静态函数,返回类型必须是functional-interface
LambdaInterface t = LambdaClass::staticF;
//2.实例方法调用
LambdaClass lambdaClass = new LambdaClass();
LambdaInterface lambdaInterface = lambdaClass::f;
//3.超类上的方法调用
LambdaInterface superf = super::sf;
//4. 构造方法调用
LambdaInterface tt = LambdaClassSuper::new;
}
}
作用域范围
和匿名内部类一样,不要在 Lambda 表达式主体内对方法内的局部变量进行修改,否则编译也不会通过
Lambda 表达式中要用到的,但又未在 Lambda 表达式中声明的变量,必须声明为 final
或者是 effectively final
,否则就会出现编译错误
final
变量:
- 一旦被初始化之后,其值就不能被改变,不能再次修改
- 可以是类的成员变量、局部变量或者方法参数
final
变量在声明时可以不立即初始化,但必须在成为非局部作用域之前被初始化
effectively final
变量:
- 指在代码的实际执行过程中,变量的值在初始化之后不会被改变,尽管在语法上它并没有被声明为
final
- 通常用于匿名类和Lambda表达式中。在这些场景下,如果变量不在这个匿名类或Lambda表达式内部被声明,那么它必须不可变,即使没有使用
final
关键字 - 实际上是一个语言规则,确保变量在匿名类或Lambda表达式中安全使用
- 把变量声明为 static
java
public class ModifyVariable2StaticInsideLambda {
static int limit = 10;
public static void main(String[] args) {
Runnable r = () -> {
limit = 5;
for (int i = 0; i < limit; i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
//输出
// 0 1 2 3 4 方案可行
- 变量声明为 AtomicInteger
AtomicInteger
可以确保int
值的修改是原子性 的,可以使用set()
方法设置一个新的int
值,get()
方法获取当前的int
值
java
public class ModifyVariable2AtomicInsideLambda {
public static void main(String[] args) {
final AtomicInteger limit = new AtomicInteger(10);
Runnable r = () -> {
limit.set(5);
for (int i = 0; i < limit.get(); i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
- 使用数组
在声明数组的时候设置为final
,但更改int
的值时却修改的是数组的一个元素
java
public class ModifyVariable2ArrayInsideLambda {
public static void main(String[] args) {
final int [] limits = {10};
Runnable r = () -> {
limits[0] = 5;
for (int i = 0; i < limits[0]; i++) {
System.out.println(i);
}
};
new Thread(r).start();
}
}
this关键字
Lambda 表达式并不会引入新的作用域,这一点和匿名内部类是不同的。也就是说,Lambda 表达式主体内使用的 this 关键字和其所在的类实例相同。
Stream流
Stream
不存储数据,可以检索(Retrieve)
和逻辑处理集合数据、包括筛选、排序、统计、计数等。
就好像一个高级的迭代器,但只能遍历一次。
- 通过简单的链式编程,使得它可以方便地对遍历处理后的数据进行再处理。
- 方法参数都是函数式接口类型
- 一个 Stream 只能操作一次,操作完就关闭了,继续使用这个 stream 会报错。
- Stream 不保存数据,不改变数据源
流类型
- stream 串行流
- parallelStream 并行流,可多线程执行
创建流
如果是数组 的话,可以使用Arrays.stream()
或者 Stream.of()
创建流;
如果是集合 的话,可以直接使用 stream()
方法创建流,因为该方法已经添加到 Collection
接口中
java
public class CreateStreamDemo {
public static void main(String[] args) {
String[] arr = new String[]{"加油1", "加油2", "加油3"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("加油1", "加油2", "加油3");
List<String> list = new ArrayList<>();
list.add("加油1");
list.add("加油2");
list.add("加油3");
stream = list.stream();
}
}
注: of()
方法内部其实调用了Arrays.stream()
方法
java
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
集合还可以调用 parallelStream()
方法创建并发流,默认使用的是 ForkJoinPool.commonPool()
线程池
java
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();
中间操作流
过滤 filter
通过filter()
方法可以从流中筛选出我们想要的元素
filter()
方法接收的是一个 Predicate
(Java 8 新增的一个函数式接口,接受一个输入参数返回一个布尔值结果)类型的参数
forEach()
方法接收的是一个 Consumer
(Java 8 新增的一个函数式接口,接受一个输入参数并且无返回的操作)类型的参数
java
public class FilterStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<String> stream = list.stream().filter(element -> element.contains("王"));
stream.forEach(System.out::println);
}
}
映射 map
通过某种操作把一个流中的元素转化成新的流中的元素,map()
接收的是一个 Function
(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数
java
public class MapStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<Integer> stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}
}
去重 distinct
它能够去除流中的重复元素
java
public class StreamDistinctExample {
public static void main(String[] args) {
// 创建一个包含重复数字的列表
List<Integer> numbersWithDuplicates = Arrays.asList(1, 2, 2, 3, 4, 4, 5, 5, 5);
// 使用Stream API对列表进行处理,去除重复的数字
List<Integer> distinctNumbers = numbersWithDuplicates.stream() // 创建一个Stream
.distinct() // 去除重复元素
.collect(Collectors.toList()); // 将结果收集到一个新的列表中
// 输出结果
distinctNumbers.forEach(System.out::println);
}
}
排序 sorted
它可以对流中的元素进行排序
java
public class StreamSortedExample {
public static void main(String[] args) {
// 创建一个包含数字的列表,这些数字将被排序
List<Integer> numbers = Arrays.asList(3, 5, 1, 4, 2);
// 使用Stream API对列表进行处理,对数字进行升序排序
List<Integer> sortedNumbers = numbers.stream() // 创建一个Stream
.sorted() // 对流中的元素进行排序
.collect(Collectors.toList()); // 将排序后的流收集到一个新的列表中
// 输出排序后的结果
sortedNumbers.forEach(System.out::println);
}
}
这里使用的是`sorted()``的默认方案,是进行自然排序,即为进行升序排序。
可以传递一个Comparator给sorted()方法:来实现改变排序规则
java
// 对字符串列表按照字符串长度降序排序
List<String> strings = Arrays.asList("banana", "apple", "cherry", "date");
List<String> sortedStrings = strings.stream()
.sorted((s1, s2) -> s2.length() - s1.length()) // 按字符串长度降序排序
.collect(Collectors.toList());
sortedStrings.forEach(System.out::println);
限制 limit
用来限制流中元素的数量,当想要从流中获取前 n 元素时,limit()
操作非常有用
java
public class StreamLimitExample {
public static void main(String[] args) {
// 创建一个包含多个数字的列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用Stream API对列表进行处理,获取前5个数字
List<Integer> firstFiveNumbers = numbers.stream() // 创建一个Stream
.limit(5) // 限制流中的元素数量为5
.collect(Collectors.toList()); // 将结果收集到一个新的列表中
// 输出获取到的数字
firstFiveNumbers.forEach(System.out::println); // 1,2,3,4,5
}
}
跳过 skip
用于跳过流中的前n个元素
java
// 获取列表中的第6到10个数字
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> sixthToTenthNumbers = numbers.stream()
.skip(5) // 跳过前5个数字
.limit(5) // 限制流中的元素数量为5
.collect(Collectors.toList());
sixthToTenthNumbers.forEach(System.out::println); // 6,7,8,9,10
flatMap
可以简洁地将复杂的多级结构简化为单级结构
java
public class StreamFlatMapExample {
public static void main(String[] args) {
// 创建一个包含多个列表的列表,即列表的列表
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("a1", "a2"),
Arrays.asList("b1", "b2", "b3"),
Arrays.asList("c1")
);
// 使用Stream API对列表进行处理,将列表的列表扁平化为一个单一的流
List<String> flattenedList = listOfLists.stream() // 创建一个Stream
.flatMap(list -> list.stream()) // 将每个列表扁平化为流
.collect(Collectors.toList()); // 将扁平化的流收集到一个新的列表中
// 输出扁平化后的列表
flattenedList.forEach(System.out::println);
// a1 a2 b1 b2 b3 c1
}
}
终结操作流
forEach
java
public class MapStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<Integer> stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}
}
count
count
操作是一个终端操作,它返回一个long类型的值,表示流中元素的数量
java
public class StreamCountExample {
public static void main(String[] args) {
// 创建一个包含一些数字的列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用Stream API对列表进行处理,计算大于5的数字的数量
long count = numbers.stream() // 创建一个Stream
.filter(num -> num > 5) // 过滤出大于5的数字
.count(); // 计算过滤后的数量
// 输出结果
System.out.println("大于5的数字的数量是: " + count);
}
}
min/max
用于找出流中元素的最小值和最大值。这些操作返回的是Optional
类型的结果,因为流中可能不包含任何元素,在这种情况下,Optional
将不会包含任何值
java
public class StreamMinMaxExample {
public static void main(String[] args) {
// 创建一个包含数字的列表
List<Integer> numbers = Arrays.asList(10, 4, 5, 8, 6, 11, 2);
// 使用Stream API找出数字中的最小值
Optional<Integer> minNumber = numbers.stream()
.min(Integer::compareTo); // 使用Integer::compareTo作为比较器
// 使用Stream API找出数字中的最大值
Optional<Integer> maxNumber = numbers.stream()
.max(Integer::compareTo); // 使用Integer::compareTo作为比较器
// 输出最小值和最大值
minNumber.ifPresent(System.out::println); // 输出: 2
maxNumber.ifPresent(System.out::println); // 输出: 11
}
}
这两个方法都需要一个Comparator
作为参数来定义如何比较元素。在这个例子中,我们使用Integer::compareTo
作为比较器,它是比较两个Integer
对象的标准方式
collect
将流转换成其他形式。最常见的用途是将流收集到一个集合中,如列表、集合或映射。
java
public class StreamCollectExample {
public static void main(String[] args) {
// 创建一个包含数字的列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用Stream API将流收集到一个列表中
List<Integer> collectedNumbers = numbers.stream()
.collect(Collectors.toList());
// 输出收集到的列表
collectedNumbers.forEach(System.out::println);
}
}
除了toList()
,Collectors
类还提供了许多其他有用的收集器,例如:
toSet()
: 将流收集到一个Set中,自动去除重复元素。toMap()
: 将流收集到一个Map中,需要提供键和值的函数。groupingBy()
: 根据某个属性对流中的元素进行分组。joining()
: 将流中的元素连接成一个字符串。
匹配
Stream 类提供了三个方法可供进行元素匹配,它们分别是:
-
anyMatch()
,只要有一个元素匹配传入的条件,就返回 true。 -
allMatch()
,只有有一个元素不匹配传入的条件,就返回 false;如果全部匹配,则返回 true。 -
noneMatch()
,只要有一个元素匹配传入的条件,就返回 false;如果全部不匹配,则返回 true。
java
public class MatchStreamDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王"));
boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1);
boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉"));
System.out.println(anyMatchFlag); // true
System.out.println(allMatchFlag); // true
System.out.println(noneMatchFlag); // true
}
}
组合 reduce
把 Stream 中的元素组合起来,最终得到一个汇总的结果。
有两种用法
Optional<T> reduce(BinaryOperator<T> accumulator)
没有起始值,只有一个参数,就是运算规则,此时返回Optional
T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有运算规则,两个参数,此时返回的类型和起始值类型一致
java
// 创建一个包含数字的列表
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用Stream API和reduce操作计算数字的总和
Optional<Integer> sum = numbers.stream()
.reduce((x, y) -> x + y);
// 输出总和
sum.ifPresent(System.out::println); // 输出: 15
reduce
方法返回的是Optional<Integer>
,因为如果流中没有元素,reduce
操作将没有结果。我们使用ifPresent()
方法来检查Optional是否包含值,并在控制台上打印出来
java
Integer[] ints = {0, 1, 2, 3};
List<Integer> list = Arrays.asList(ints);
int reduce = list.stream().reduce(6, (a, b) -> a + b);
System.out.println(reduce); //12
int reduce1 = list.stream().reduce(6, Integer::sum);
System.out.println(reduce1); // 12
相当于给了一个起始值:6
延迟执行
在执行返回 Stream
的方法时,并不立刻执行,而是等返回一个非Stream
的方法后才执行。因为拿到 Stream
并不能直接用,而是需要处理成一个常规类型。
java
@Test
public void laziness(){
List<String> strings = Arrays.asList("abc", "def", "gkh", "abc");
Stream<Integer> stream = strings.stream().filter(new Predicate() {
@Override
public boolean test(Object o) {
System.out.println("Predicate.test 执行");
return true;
}
});
System.out.println("count 执行");
stream.count();
}
/*-------执行结果--------*/
count 执行
Predicate.test 执行
Predicate.test 执行
Predicate.test 执行
Predicate.test 执行
filter
中的方法并没有立刻执行,而是等调用count()
方法后才执行
并行流
并行 parallelStream
在使用方法上和串行一样。主要区别是parallelStream
可多线程执行,是基于ForkJoin
框架实现的。
这里可以简单的理解它是通过线程池来实现的 ,这样就会涉及到线程安全,线程消耗等问题。
java
@Test
public void parallelStreamTest(){
List<Integer> numbers = Arrays.asList(1, 2, 5, 4);
numbers.parallelStream()
.forEach(num->System.out.println(Thread.currentThread().getName()+">>"+num));
}
//执行结果
main>>5
ForkJoinPool.commonPool-worker-2>>4
ForkJoinPool.commonPool-worker-11>>1
ForkJoinPool.commonPool-worker-9>>2
Optional
该类提供了一种用于表示可选值而非空引用的类级别解决方案
就是一种针对 NPE(NullPointerException) 解决方案
创建对象
- 可以使用静态方法
empty()
创建一个空的Optional
对象
java
Optional<String> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty
- 可以使用静态方法
of()
创建一个非空的Optional
对象
java
Optional<String> opt = Optional.of("你好");
System.out.println(opt); // 输出:Optional[你好]
传递给 of()
方法的参数必须是非空的,也就是说不能为 null,否则仍然会抛出 NullPointerException
- 可以使用静态方法
ofNullable()
创建一个即可空又可非空的Optional
对象
java
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional.empty
ofNullable()
方法内部有一个三元表达式:
- 如果为参数为 null,则返回私有常量 EMPTY;
- 否则使用 new 关键字创建了一个新的 Optional 对象.
判断值是否存在
通过方法 isPresent()
判断一个Optional
对象是否存在,如果存在,该方法返回 true
,否则返回 false
------取代了 obj != null
的判断
java
Optional<String> opt = Optional.of("hello");
System.out.println(opt.isPresent()); // 输出:true
Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull.isPresent()); // 输出:false
Java 11 后还可以通过方法 isEmpty()
判断与 isPresent()
相反的结果
非空表达式
ifPresent()
可以直接将 Lambda 表达式传递给该方法
java
Optional<String> opt = Optional.of("hello");
opt.ifPresent(str -> System.out.println(str.length()));
Java 9 后还可以通过方法 ifPresentOrElse(action, emptyAction)
执行两种结果,非空时执行action
,空时执行 emptyAction
java
Optional<String> opt = Optional.of("hello");
opt.ifPresentOrElse(str -> System.out.println(str.length()),
() -> System.out.println("为空"));
设置默认值
在创建(获取) Optional 对象的时候,需要一个默认值
orElse
orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回;否则返回默认值
java
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("hello");
System.out.println(name); // 输出:hello
orElseGet
参数类型不同。如果 Optional
对象中的值为 null
,则执行参数中的函数
java
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"hello");
System.out.println(name); // 输出:hello
获取值
建议 orElseGet()
方法获取Optional
对象的值
使用get()
方法的话,因为假如 Optional 对象的值为 null,该方法会抛出NoSuchElementException
异常
过滤值 filter
filter()
方法的参数类型为 Predicate
(Java 8 新增的一个函数式接口),也就是说可以将一个 Lambda 表达式传递给该方法作为条件
如果表达式的结果为 false
,则返回一个 EMPTY
的 Optional
对象,否则返回过滤后的 Optional
对象
java
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
password = "1234567";
Optional<String> opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);
转换值 map
将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改
java
// 创建一个非空的Optional
Optional<String> optionalString = Optional.of("Hello");
// 使用map方法将String转换为大写形式
Optional<String> optionalUpperCase = optionalString.map(String::toUpperCase);
// 输出转换后的结果
optionalUpperCase.ifPresent(System.out::println); // 输出: HELLO
// 创建一个空的Optional
Optional<String> emptyOptional = Optional.empty();
// 使用map方法尝试转换空Optional中的值
Optional<String> emptyOptionalResult = emptyOptional.map(String::toUpperCase);
// 输出空Optional的结果
emptyOptionalResult.ifPresent(System.out::println); // 不会执行,因为没有值