简介:
前段时间面了完美世界,被问到Java8的新特性,在此特地记录一下,虽然现在Java的版本可能已经很高了,但是Java8的新特性依然值得学习一下!
1. Lambda 表达式
lambda表达式是一个匿名函数,允许把函数作为一个方法的参数(函数作为参数传递进方法中,可以传递的代码),
使用Lambda 表达式可以使代码变的更加简洁紧凑。
案例1 Runable - ①无参数无返回值
// 普通写法
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("普通使用");
}
};
// lambda表达式写法
Runnable r2 = ()-> System.out.println("lambda表达式使用");
案例2 比较 - ②有参数无返回值
// 普通写法
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
int compare = comparator.compare(12, 21);
//lambda表达式写法
Comparator<Integer> comparator = (o1,o2)-> Integer.compare(o1,o2);
int compare = comparator.compare(32,23);
//③两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> comparator = (o1,o2)-> {
System.out.println(o1);
return Integer.compare(o1,o2);};
案例3 Consumer - ④有参数有返回值
// 普通写法
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("普通写法");
//lambda表达式写法
Consumer<String> consumer = (String s)->{System.out.println(s);};
consumer.accept("lambda表达式使用");
//⑤只有一个参数时候小括号可以省略 ⑥只有一条语句时,return与大括号若有,都可以省略
Consumer<String> consumer = s->{System.out.println(s);}
2. Stream流
Stream 中文称为 “流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。
流与集合的区别
1.流为特定元素类型的序列值提供接口。但是,与集合不同,流实际上并不存储元素。元素是按需计算的。可以将流视为延迟构造的Collection,可以在需要它们时计算其值。
2.流操作不会更改其源。相反,它们返回存储结果的新流。
3.可能无界。集合的大小是有限的,但是流没有。诸如limit(n)或findFirst()之类的短路操作可以允许对无限流的计算在有限时间内完成。
4.消耗品。在流的生存期内,流的元素仅被访问一次。如果要重新访问流中的同一元素,则需要根据源重新生成新的流。
中级运营与终端运营
中间操作将一个流转换为另一流,而终端操作则产生结果。在流上执行终端操作时,将无法再使用该流。
例如,在下面的代码中:
list.stream().filter(s -> s.length() > 2).count();
filter()是中间操作,而count()是终端操作。调用count()时,我们不能再使用该流
常用的中间操作包括:
筛选,映射,不同,排序,跳过,限制,flatMap
常用的终端操作包括:
1. forEach,toArray,收集,减少,计数,最小,最大
2. findFirst,findAny,anyMatch,allMatch,noneMatch
参考自:https://blog.csdn.net/pjh88/article/details/119672779
基本应用:
1.filter 过滤出年龄大于20的用户
List<User> users = new ArrayList<>();
var targetUsers = users.stream().filter(user -> user.getAge()>20).collect(Collectors.toList());
2.sorted 排序 按照年龄大小排序
List<User> users = new ArrayList<>();
var targetUsers = users.stream().sorted(Comparator.comparing(User::getAge())).collect(Collectors.toList());
3.map 将流中的每一个元素 T 映射为 R(类似类型转换) 将用户信息转成用户名称集合
var userNames= users.stream().map(User::getName()).collect(Collectors.toList());
4.distinct() 去重
var userNames= users.stream().map(User::getName()).distinct().collect(Collectors.toList());
5.limit 延迟方法,截取Stream流中的前几个元素返回新的Stream流,入参为long类型,没有方法体,若入参的值大于Stream流中的数据的长度则返回由原数据组成的新Stream流
users.stream().limit(3).forEach(user -> System.out.println(user.getName()));
6.concat Stream的静态方法,将多个Stream流的数据按入参顺序合并为一个新的Stream流
Stream<String> st1 = Stream.of("aa","bb","cc","dd","ee");
Stream<String> st2 = Stream.of("AA","BB","CC","DD","EE");
Stream<String> st3 = Stream.concat(st1,st2);
7.skip 延迟方法,入参为long类型,没有方法体,跳过前一个Stream流的前几个元素,得到由后面的元素组成的新Stream流
Stream<String> st1 = Stream.of("aa","bb","cc","dd","ee");
st1.skip(2).forEach(str -> System.out.println(str));
8.count 最终方法,没有参数,没有方法体,属于Stream流的最终方法,用于统计Stream流中的数据长度,返回long类型
long count = users.stream().count();
9.foreach() 遍历
users.steam().foreach(user -> user.setCompany("xxx公司"));
10.flatMap() 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());
11.groupingBy() 分组
Map<Integer,List<User>> = users.stream().collect(Collectors.groupingBy(User::getSex));
12.anyMatch() 有一个匹配则返回true
boolean flag = user.stream().anyMatch(user-> user.getAge() == 25);
13.noneMatch() 没有一个匹配则返回true
boolean flag = user.stream().noneMatch(user-> user.getAge() == 25);
14.mapToDouble()/mapToLong()/mapToInt() + sum() 转成double并求值
double ageSum = user.stream().mapToDouble(User::getAge()).sum();
15.max() + min() + ifPresent() 特殊函数 如果匹配则执行ifPresent()中的函数
users.stream().min(Comparator.comparing(User::getAge()).ifPresent(user-> System.out.println("最小年龄的用户为:"user.getName()));
users.stream().max(Comparator.comparing(User::getAge()).ifPresent(user-> System.out.println("最大年龄的用户为:"user.getName()));
16.reduce() reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作
1.一个参数:Optional reduce(BinaryOperator accumulator),传入求和函数式
List<Integer> list= Arrays.asList(new Integer[]{1,2,3,4,5,6,7,8,9});
Integer sum=list.stream().reduce((x,y)->x+y).get();
2.两个参数:T reduce(T identity, BinaryOperator accumulator),(默认值,求和函数式)identity参数与Stream中数据同类型,相当于一个的初始值
List<Integer> numList = Arrays.asList(1,2,3,4,5);
Integer result = numList.stream().reduce(100, (a,b) -> a + b );
3.三个参数的没怎么用过
List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5);
String result = numList.stream().reduce("__", (a, b) -> a += String.valueOf(b), (x, t) -> null);
3. Optional
Optional是一个容器对象,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解决 NullPointerExceptions的问题。本质上,Optional是一个包装器类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,并且也可以指向任何内容。从其它角度看,Optional提供一种类型级解决方案来表示可选值而不是空引用。
在Java 8之前,程序员将返回null而不是Optional。这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。相比之下,在API中返回Optional是明确的声明,其中可能没有值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查。
创建方法
1.static <T> [Optional]<T> [empty]()
// Creating an empty optional 创建一个空的Optional实例
Optional<String> empty = Optional.empty();
2. static <T> [Optional]<T> [of](T value)
// Creating an optional using of 创建特定的非空值Optional。
String name = "java";
Optional<String> opt = Optional.of(name);
3. static <T> [Optional]<T> [of](T value)
//Possible null value 描述指定值的Optional,传入一个空引用,它不会抛出异常,而是返回一个空的Optional对象
Optional<String> optional = Optional.ofNullable(name());
private String name()
{
String name = "Java";
return (name.length() > 5) ? name : null;
}
使用方法
1.isPresent() 如果存在值,则返回true;反之,返回false。如果所包含的对象不为null,则返回true,反之返回false
2.isEmpty() 如果存在值,则返回false;否则,返回ture。这与isPresent 相反
3.ifPresent([Consumer]<? super [T]> consumer) 如果存在值,则使用该值调用指定的使用者;否则,什么都不做
optional1.ifPresent(s -> System.out.println(s.length()));
4.get() 如果此Optional中存在值,则返回该值,否则抛出 NoSuchElementException
5.orElse() orElse() 方法返回包装的值(如果存在)及其参数
//底层 value != null ? value : other
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("default_name");
6.orElseGet() 该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值
//底层 value != null ? value : other.get();
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
7.orElseThrow() 抛出异常
User result = Optional.ofNullable(user).orElseThrow( () -> new IllegalArgumentException());
8.map() 转换值 filter/flatMap等可以参考stream流
User user = new User("anna@gmail.com", "1234");
String email = Optional.ofNullable(user).map(u -> u.getEmail()).orElse("default@gmail.com");
4. Java 8 默认方法
java 8 之前,接口与其实现类之间的 耦合度 太高了,当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现
public interface People{
default void print(){
System.out.println("默认方法");
}
}
多继承问题
public interface Vehicle{
default void print(){
System.out.println("默认方法0");
}
}
public interface FourWheeler {
default void print(){
System.out.println("默认方法1");
}
}
解决方法有两个:
1、子类重写默认方法覆盖接口的默认方法;
2、使用 super 来调用指定接口的默认方法
//1、重写覆盖
public class Car implements Vehicle, FourWheeler {
default void print(){
System.out.println("新默认方法");
}
}
//2、精确调用
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
静态默认方法 : Java8 支持接口中定义静态方法(需要提供默认实现),写法上将默认方法的default关键字换成static关键字即可
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
注意点
1.default 关键字只能在接口中使用(以及用在 switch 语句的 default 分支),不能用在抽象类中。
2.接口默认方法不能覆写 Object 类的 equals、hashCode 和 toString 方法。
3.接口中的静态方法必须是 public 的,public 修饰符可以省略,static 修饰符不能省略。
4.即使使用了 java 8 的环境,一些 IDE 仍然可能在一些代码的实时编译提示时出现异常的提示(例如无法发现 java 8 的语法错误),因此不要过度依赖 IDE。
5. 函数式接口
有且仅有一个抽象方法的接口,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Lambda就是Java中函数式编程的体现
1.Function (函数型接口)
//输入参数 + 输出返回值
Function<String,String> function = s -> s+"函数";
function.apply("test");
// 输入参数 1 + 输入参数 2 + 输出返回值
BiFunction<String, String, String> biFunction= (s1, s2) -> s1 + s2;
biFunction.apply("test","test1");
2.Predicate (断定型接口)
//输入参数 + 输出返回值 Boolean类型
Predicate<Integer> predicate =(n)->{return n>0;};
boolean flag = predicate.test(2);
3.Consumer (消费型接口) 只有参数没有返回值
//输入参数
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("a");
//输入参数1 + 输入参数2
BiConsumer<String, String> biConsumer = (s1, s2) -> System.out.println(s1 + s2);
biConsumer.accept("a","b");
4.Supplier (供给型接口) 没有参数只有返回值
//返回值
Supplier<Integer> supplier = ()-> {return 1024;};
Integer num = supplier.get();
6. 方法引用 lambda表达式的一种简化写法
对象::实例方法
//1.1 接口 变量名 = 匿名内部类
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("consumer1");
//1.2 接口 变量名 = lambda表达式
consumer = s -> {
System.out.println(s);
};
consumer.accept("consumer2");
//1.3 接口 变量名 = 方法引用
consumer = System.out::println;
consumer.accept("consumer3");
类::静态方法
//匿名内部类写法
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println("comparator.compare(10,20) = " + comparator.compare(10, 20));
//用lambda表达式简化
comparator = (o1, o2) -> {
return Integer.compare(o1, o2);
};
System.out.println("comparator.compare(10,20) = " + comparator.compare(10, 20));
//用方法引用简化
comparator = Integer::compare;
System.out.println("comparator.compare(10,20) = " + comparator.compare(10, 20));
类::实例方法
Person person = new Person("张三");
//匿名内部类写法
Function<Person, String> function = new Function<Person, String>() {
@Override
public String apply(Person person) {
return person.getName();
}
};
System.out.println(function.apply(person));
//用lambda表达式简化
function = p -> p.getName();
System.out.println(function.apply(person));
//用方法引用简化
function = Person::getName;
System.out.println(function.apply(person));
类::new
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return new String("abc");
}
};
System.out.println(supplier.get());
//用lambda表达式简化
supplier = () -> new String("abc");
System.out.println(supplier.get());
//用方法引用简化
supplier = String::new;
System.out.println(supplier.get());
7. Base64编码
import java.util.Base64; //导入包
public class StrConvertBase64 {
public static void main(String[] args) {
// 字符串转Base64
String enCodeStr = getBase64EnCoder("哈哈哈哈");
System.out.println(enCodeStr);
// Base64转字符串
String deCodeStr = getBase64DeCoder("uf65/rn+uf4=");
System.out.println(deCodeStr);
}
/**
* Base64编码
* @param src
* @return
*/
public static String getBase64EnCoder(String str) {
// 将源字符串转为byte数组
byte [] strBytes = str.getBytes();
// 链式调用,返回结果
return Base64.getEncoder().encodeToString(strBytes);
}
/**
* Base64解码
* @param src
* @return
*/
public static String getBase64DeCoder(String str) {
// 将Base64编码转为byte数组
byte [] base64Bytes = Base64.getDecoder().decode(str);
// 将Byte数组转为String,返回结果
return new String(base64Bytes);
}
}
8. 链式编程文章来源:https://www.toymoban.com/news/detail-691702.html
原理:链式编程的原理是返回一个this对象,也就是返回对象本身,从而达到链式效果
1.
StringBuilder buffer = new StringBuilder();
buffer.append("你").append("好").append("!").append(" ").append("世").append("界");
2.
String string = String.valueOf("123").concat(",4567890").replace(',', '!').substring(2, 8);
3.
// 将数组变成一个列表集合
List<Integer> list = Arrays.asList(5, 3, 7, 5, 4);
// 获取集合的 Stream 流对象
list.stream()
// 相同元素去重
.distinct()
// 升序排序
.sorted((c1, c2) -> c1.compareTo(c2))
// 遍历
.forEach(System.out::println);
9. Consumer文章来源地址https://www.toymoban.com/news/detail-691702.html
Java的Consumer接口来自Java 8 引入的java.util.function包
Consumer是一个功能接口,用来作为lambda表达式或方法引用的任务目标(传递一个参数执行指定的方法)。
Consumer的功能接口是一个接受单一参数并且不返回任何结果的操作,功能方法为accept(T t)
accept(T t) 对给定的参数进行操作
andThen() 此方法返回一个组合的Consumer,该Consumer先执行原始的Consumer操作,然后按照从左到右的 顺序执行给定的andThen操作。
accept(T t) 使用:
实例1 基本使用
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("use");
实例2 对基础数据类型操作
List<Integer> list = new ArrayList<>();
Consumer<Integer> c = i -> list.add(i);
c.accept(1);
c.accept(2);
实例3 对象类型操作 User 对象 name 是名字字段
Consumer<User> c = user -> System.out.println(user.getName());
andThen() 使用:default Consumer<T> andThen(Consumer<? super T> after) 必须是同一类型的
实例1
List<Integer> numList = Arrays.asList(3, 4, 5, 6);
Consumer<List<Integer>> first= list -> {
for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i) * list.get(i));
}
};
Consumer<List<Integer>> second= list -> list.forEach(n -> System.out.println(n));
first.andThen(second).accept(numList);
实例2
public static void main(String[] args) {
List<Integer> list = Arrays.asList(12, 13, 14, 15, 16, 17);
Consumer<List<Integer>> firstConsumer = Task::first;
Consumer<List<Integer>> secondConsumer = Task::second ;
Consumer<List<Integer>> lastConsumer = Task::last;
firstConsumer .andThen(secondConsumer ).andThen(lastConsumer ).accept(list);
}
}
class Task{
static void first(List<Integer> list) {
System.out.println("---first---");
list.forEach(i -> {
System.out.println("first :" + i);
});
}
static void second (List<Integer> list) {
System.out.println("---second ---");
list.forEach(i -> {
System.out.println("second :" + i);
});
}
static void last(List<Integer> list) {
System.out.println("---last---");
list.forEach(i -> {
System.out.println("last:" + i);
});
}
到了这里,关于JAVA 8 的新特性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!