文章目录
JAVA8新特性简介
1.Lambda 表达式:Lambda 表达式是函数式编程的重要组成部分。它们提供了一种简洁的方式来表示匿名函数,可以作为参数传递给方法或使用函数式接口进行操作。
2.Stream API:Stream API 提供了一种流式处理集合数据的方式。它支持函数式编程风格的操作,可以进行过滤、映射、排序、归约等操作,简化了对集合数据的处理。
3.方法引用:方法引用提供了一种更简洁的语法来调用已经存在的方法。它可以通过名称引用静态方法、实例方法或构造函数,并且可以与Lambda表达式配合使用。
4.默认方法(Default Methods):默认方法是接口中的一种新特性,它允许在接口中定义具有默认实现的方法。这样,在接口中添加新方法时,现有的实现类不需要修改代码即可获得默认方法的实现。
5.函数式接口:函数式接口是只有一个抽象方法的接口。Java 8引入了@FunctionalInterface注解,用于标识函数式接口。函数式接口可以被Lambda表达式直接使用。
6.Optional 类:Optional 类是一个容器对象,用于包装可能为null的值。它提供了一种优雅的方式来处理可能为空的对象,避免了空指针异常。
7.新的日期/时间 API(java.time包):Java 8引入了全新的日期和时间API,提供了更强大、更可靠的日期和时间处理功能。这些API具有不可变性、线程安全性和更好的设计。
除了上述特性之外,还有一些其他改进,例如重复注解、类型注解、CompletableFuture 异步编程、新的并发工具等。
Lambda表达式
语法
Lambda 表达式: 在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
- 匿名内部类实现的接口一定是函数接口(只有一个抽象方法的接口)
- 左侧: 指定了 Lambda 表达式需要的参数列表
- 右侧: 指定了 Lambda 体, 是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。
类型推断
Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断” 。
/**
* 一个参数的语法
*/
public class LamdbaDemo5 {
public static void show(B b) {
b.f1("hello lambda 表达式");
}
/**
* 匿名内部类
*/
public void test1() {
//匿名内部类
B b = new B() {
@Override
public void f1(String s) {
System.out.println(s);
}
};
show(b);
}
/**
* lambda表达式
*/
@Test
public void test2() {
//匿名内部类
// B b = (String str) -> {
// System.out.println(str);
// };
// show(b);
show((String str) -> {
System.out.println(str);
});
}
/**
* lambda表达式左边参数列表,可以根据上下文判断参数类型
*/
@Test
public void test3() {
show((str) -> {
str += " hello abc";
System.out.println(str);
});
}
/**
* 只有一个参数
*/
@Test
public void test4() {
show(str -> {
str += " hello abc";
System.out.println(str);
});
}
@FunctionalInterface
interface B {
void f1(String s);
}
}
两个参数
/**
* 多个参数的语法
*/
public class LamdbaDemo6 {
public static void show(C c) {
c.f1(10, 20);
}
/**
* 打印两个数的大的值
*/
@Test
public void test1() {
C c = new C() {
@Override
public void f1(int x, int y) {
System.out.println(x > y ? x : y);
}
};
show(c);
}
@Test
public void test2() {
show((x, y) -> {
// System.out.println(x > y ? x :y);
System.out.println(x + y);
});
}
@FunctionalInterface
interface C {
void f1(int x, int y);
}
@Test
public void test3() {
List list = new ArrayList();
list.add("555");
list.add("zzz");
list.add("xxx");
list.add("yyy");
list.add("mmm");
list.add("nnn");
list.add("aaa");
list.add("aaa");
list.add("111");
list.add("111");
list.add("222");
list.add("333");
list.add("444");
Comparator com = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
};
Collections.sort(list, com); //定制排序
System.out.println(list);
}
@Test
public void test4() {
List list = new ArrayList();
list.add("555");
list.add("zzz");
list.add("xxx");
list.add("yyy");
list.add("mmm");
list.add("nnn");
list.add("aaa");
list.add("aaa");
list.add("111");
list.add("111");
list.add("222");
list.add("333");
list.add("444");
// Comparator com = new Comparator() {
// @Override
// public int compare(Object o1, Object o2) {
// String s1 = (String) o1;
// String s2 = (String) o2;
// return -s1.compareTo(s2);
// }
//
// };
//定制排序器
Comparator<String> com = (s1, s2) -> {
return -s1.compareTo(s2);
};
Collections.sort(list, com); //定制排序
System.out.println(list);
}
}
函数式接口
函数式接口是指只包含一个抽象方法的接口,用于支持函数式编程和Lambda表达式。在Java中,函数式接口使用@FunctionalInterface
注解进行标识。
函数式接口可以作为方法的参数或返回值,用于简化代码并提供更灵活的编程方式。Java 8及以后版本提供了一些常见的函数式接口,如下所示:
1.Consumer<T>
:消费型接口,代表接受一个输入参数并且没有返回值的操作。方法是 void accept(T t)
。
2.Supplier<T>
:供给型接口,代表一个无参数的函数,用于提供一个返回值。方法是 T get()
。
3.Function<T, R>
:函数型接口,代表接受一个输入参数并产生一个结果的函数。方法是 R apply(T t)
。
4.Predicate<T>
:断言型接口,代表一个输入参数的判断条件,返回一个布尔值。方法是 boolean test(T t)
。
5.BiConsumer<T, U>
:代表接受两个输入参数并且没有返回值的操作。方法是 void accept(T t, U u)
。
6.BiFunction<T, U, R>
:代表接受两个输入参数并产生一个结果的函数。方法是 R apply(T t, U u)
。
除了上述常见的函数式接口,Java还提供了一些其他的函数式接口,如UnaryOperator<T>
、BinaryOperator<T>
等,用于特定的场景和操作。
函数式接口的使用可以大大简化代码,并提高代码的可读性和灵活性。使用Lambda表达式可以方便地实现函数式接口的抽象方法,从而实现函数式编程的思想。
自定义函数式接口
@FunctionalInterface
public interface MyInterface {
void method1();
// void method2();
}
Consumer函数式接口
Consumer:接受一个输入参数,并且不返回任何结果。它对输入参数执行某些操作,可以用来消费(处理)一个对象。
Consumer<String> printer = str -> System.out.println(str);
printer.accept("Hello, world!");
Supplier函数式接口
不接受任何参数,但是返回一个结果。它用于生成(提供)一个值。
Supplier<Integer> randomNumberGenerator = () -> new Random().nextInt(100);
int randomNumber = randomNumberGenerator.get();
Function<T, R>函数式接口
接受一个输入参数,并且返回一个结果。它将输入参数转换为特定类型的输出。
Function<Integer, String> convertToString = num -> String.valueOf(num);
String strNumber = convertToString.apply(42);
Predicate函数式接口
接受一个输入参数,并且返回一个布尔值结果。它用于判断输入参数是否满足某个条件。
Predicate<Integer> isEven = num -> num % 2 == 0;
boolean result = isEven.test(7);
函数式接口在具体案例中的用法
public class ConsumerTest1 {
@Test
public void test1() {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
/**
* stream中使用Consumer
*/
Consumer<User> c = o -> System.out.println(o);
list.forEach(c);
}
@Test
public void test2() {
Consumer<String> c = s -> System.out.println(s);
Consumer<String> c2 = s -> {
System.out.println(s.substring(6));
};
c.accept("hello world");
c2.accept("hello world");
}
/**
* 处理金额
* @param c
* @param money
* 这个处理金额方法是因为,在创建完Consumer函数式接口之后是不会自动调用所写的方法体的
* 用这个方法来调用所实现的方法体
*/
public void f1(Consumer<Double> c, Double money) {
c.accept(money);
}
@Test
public void test4() {
f1(m -> {
DecimalFormat df = new DecimalFormat("#,###.##");
System.out.println(df.format(m));
}, 12345623434.789);
System.out.println("----------------------");
f1(m -> {
DecimalFormat df = new DecimalFormat("0,000.000");
System.out.println(df.format(m));
}, 12345623434.789755555);
}
/**
* DecimalFormat保留2位小数,每三位一个多厚(金额)
*/
@Test
public void test3() {
DecimalFormat df = new DecimalFormat("#,###.##");
System.out.println(df.format(12345623434.789));
}
}
案例2
public class PredicatTest1 {
/**
* Predicate在Stream中的应用
*/
@Test
public void test1() {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
list.add(new User(2, "john", 33));
list.add(new User(3, "doc", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "tom", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysql", 73));
list.add(new User(8, "java", 84));
/**
* 删除年龄是20的元素
*/
list.removeIf(u -> u.getAge() == 20);
System.out.println(list);
System.out.println("----------------");
/**
* 查找>=5的用户,循环
*/
list.stream().filter(u -> {
return u.getId() >= 5;
}).forEach((u) -> {
System.out.println(u);
});
}
public static List<String> filterList(List<String> list, Predicate<String> p) {
List<String> result = new ArrayList<>();
for (String s : list) {
if (p.test(s)) { //对s进行判断
result.add(s);
}
}
return result;
}
@Test
public void test2() {
List<String> list = new ArrayList<>();
list.add("hello abc");
list.add("welcome abc");
list.add("hello world");
list.add("你好 abc");
list.add("大家好 abc");
list.add("hello mickey");
filterList(list, s -> {
return s.contains("hello");
}).forEach(s -> System.out.println(s));
System.out.println("----------------");
filterList(list, s -> {
return s.contains("abc");
}).forEach(s -> System.out.println(s));
}
}
案例3
public class FunctionTest1 {
/**
* function在stream的使用
*/
@Test
public void test1() {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
// //得到集合中的id
// List<Integer> ids = new ArrayList<>();
// for (User user : list) {
// ids.add(user.getId());
// }
//得到集合中的所有的id
List<String> ids = list.stream().map(u -> u.getId() + u.getName()).collect(Collectors.toList());
System.out.println(ids);
}
public Object performUser(Function<User, Object> f, User u) {
return f.apply(u);
}
@Test
public void test2() {
User user = new User(1, "mickey", 18);
//得到学员名字
System.out.println(performUser(u -> {
return u.getName();
}, user));
System.out.println("-------");
System.out.println(performUser(u -> {
return u.getName() + u.getAge();
}, user));
}
}
案例4
public class SupplierTest1 {
public static void printBean(Supplier s) {
System.out.println(s.get());
}
@Test
public void test1() {
printBean(() -> new User(1, "mickey", 18));
System.out.println("------");
printBean(() -> "hello stream");
}
}
案例5
public class BinaryOperatorTest1 {
@Test
public void test1() {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
//得到所有的id,然后相加得到结果 -> List<Integer> ids =
Optional<Integer> optionalInteger = list.stream().map(u -> u.getId()).reduce((d1, d2) -> d1 + d2);
System.out.println(optionalInteger.get());
}
}
方法引用
方法引用是Lambda表达式的一个语法糖,也就是lambda的简化
要求:
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
- 如下三种主要使用情况:
- 对象::实例方法名
/**
* lambda表达式的语法糖
*/
@Test
public void test2() {
printBean(User :: new);
}
- 类::静态方法名
@Test
public void test1() {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
//匿名内部类
list.forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
System.out.println("-------------");
/**
* stream中使用Consumer
*/
list.forEach(o -> System.out.println(o));
System.out.println("---------------");
//方法引用
list.forEach(System.out :: println);
}
- 类::实例方法名
@Test
public void test5() {
//匿名内部类
int i1 = f1(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer d1, Integer d2) {
return Integer.max(d1, d2);
}
}, 12, 15);
System.out.println("--------");
//lambda表达式
int i2 = f1((d1, d2) -> Integer.max(d1, d2), 12, 15);
//方法引用-lambda表达式的语法糖
int i3 = f1(Integer::max, 12, 15);
System.out.println(i1 + " " + i2 + " " + i3);
}
Stream API
Stream API是Java 8引入的一个强大的功能,它提供了一种用于处理集合数据的函数式编程方式。Stream(流)可以被视为一种高级迭代器,它允许对集合进行一系列的操作,如过滤、映射、排序、聚合等。
Stream API的主要特点和优势包括:
1.函数式编程:Stream API是基于函数式编程思想实现的,可以使用Lambda表达式来进行操作,使代码更简洁、可读性更强。
2.延迟执行:Stream API支持延迟执行,只有在终止操作被调用时才会真正进行计算,这样可以提高效率并节省资源。
3.内部迭代:Stream API封装了对集合的遍历和操作过程,可以自动进行迭代,简化了代码编写。
4.并行处理:Stream API支持并行处理,可以在多核处理器上利用并行性来提高性能。
Stream API的使用通常包括以下几个步骤:
1.创建流:通过集合、数组、IO通道等方式创建一个流。
2.中间操作:对流进行一系列的中间操作,如过滤、映射、排序、去重等。这些操作可以串联起来形成一个操作流水线。
3.终止操作:对流进行最终的操作,如收集结果、聚合数据、遍历元素等。终止操作会触发流的计算,并返回最终结果。
Stream API提供了丰富的操作方法,如filter()
、map()
、sorted()
、distinct()
等,可以根据需要进行选择和组合。使用Stream API可以简化集合数据的处理,提高代码的可读性和可维护性,并且可以充分利用多核处理器的优势来提高性能。
中间操作
Stream API提供了丰富的中间操作方法,可以在流的元素上进行过滤、映射、排序、去重等操作。下面是一些常用的中间操作方法:
1.filter(Predicate<T> predicate)
: 过滤符合条件的元素,接受一个谓词(Predicate)作为参数,返回一个包含符合条件元素的新流。
2.map(Function<T, R> mapper)
: 对流的每个元素应用一个函数,将元素映射为另一种类型,接受一个函数(Function)作为参数,返回一个包含映射结果的新流。
3.flatMap(Function<T, Stream<R>> mapper)
: 将流的每个元素映射为一个流,然后将这些流合并为一个流,接受一个函数(Function)作为参数,返回一个新流。
4.sorted()
: 对流的元素进行排序,默认按照自然顺序进行排序。
5.sorted(Comparator<T> comparator)
: 对流的元素进行排序,根据指定的比较器(Comparator)进行排序。
6.distinct()
: 去除流中重复的元素,根据元素的equals()方法和hashCode()方法判断是否相同。
7.limit(long maxSize)
: 截断流,获取流的前n个元素。
8.skip(long n)
: 跳过流的前n个元素,返回一个新流。
9.peek(Consumer<T> action)
: 对流的每个元素执行一个操作,不会改变流的内容。
这些中间操作方法可以按需组合使用,形成一个操作流水线。这些操作都是惰性执行的,只有当终止操作被调用时才会触发计算。通过合理地使用中间操作方法,可以对流的元素进行灵活、高效的处理。
终止操作
Stream API提供了各种终止操作方法,用于触发流的计算并返回最终结果。下面是一些常用的终止操作方法:
1.forEach(Consumer<T> action)
: 对流的每个元素执行指定操作,无返回值。
2.count()
: 返回流中元素的总个数。
3.collect(Collector<T, A, R> collector)
: 将流中的元素收集到一个结果容器中,使用指定的收集器(Collector)进行处理,并返回最终结果。
4.min(Comparator<T> comparator)
: 返回流中的最小元素,根据指定的比较器(Comparator)进行比较。
5.max(Comparator<T> comparator)
: 返回流中的最大元素,根据指定的比较器(Comparator)进行比较。
6.findFirst()
: 返回流中的第一个元素(按照流的遍历顺序)。
7.findAny()
: 返回流中的任意一个元素。
8.allMatch(Predicate<T> predicate)
: 检查流中的所有元素是否都符合指定条件,返回一个布尔值。
9.anyMatch(Predicate<T> predicate)
: 检查流中是否存在至少一个元素符合指定条件,返回一个布尔值。
10.noneMatch(Predicate<T> predicate)
: 检查流中是否没有任何元素符合指定条件,返回一个布尔值。
11.reduce(T identity, BinaryOperator<T> accumulator)
: 将流中的元素通过累加器(BinaryOperator)进行累积操作,返回一个包含累积结果的值。
12.reduce(BinaryOperator<T> accumulator)
: 将流中的元素通过累加器(BinaryOperator)进行累积操作,返回一个Optional对象,表示累积结果。
这些终止操作可以根据需求选择使用,它们会触发流的计算,并返回最终的结果。通过合理地组合中间操作和终止操作,可以灵活、高效地处理流的数据。