Java 7、Java 8常用新特性

Java 8 常用新特性

1、Lambda 表达式

Java 8 引入了 Lambda 表达式,这是一种新的函数式编程特性,允许你以简洁的方式表示匿名函数。Lambda 表达式特别适用于那些需要简短函数作为参数的场景,比如 Java 8 中的 Stream API。
Lambda 表达式的基本语法如下:

(parameter-list) -> { function-body }

其中,parameter-list 是输入参数列表,-> 是 Lambda 符号,function-body 是 Lambda 体的代码。


2、方法引用

在Java 8中,方法引用是一个简洁的方式来表示Lambda表达式。它允许你直接引用已存在的方法、构造器或实例方法,而不是创建一个匿名类。使用方法引用可以使代码更加简洁易读,特别是当你需要传递一个函数作为参数时(例如在Stream API中)。方法引用主要有四种类型:

2.1 静态方法引用

使用类名引用一个静态方法。

        List<String> list = new ArrayList<>();
        list.add("d");
        list.add("b");
        list.add("c");

        list.sort(String::compareToIgnoreCase);// 比较ascll码
        System.out.println(list);

2.2 特定对象的实例方法引用

使用特定对象的引用来调用其实例方法。

        String l="hello world";
        Predicate<String> predicate = l::endsWith; // 从尾部开始比较
        boolean b = predicate.test("ld");
        System.out.println(b);

2.3 特定类型的任意对象的实例方法引用

可以用于引用特定类型的任意对象的实例方法。

        List<String> list = new ArrayList<>();
        list.add("d");
        list.add("b");
        list.add("c");
        
        List<String> collect = list.stream()
                .filter(String::isEmpty) // false
                .collect(Collectors.toList());
        System.out.println(collect); // []

2.4 构造器引用

用于引用类的构造器。

        Supplier<String> supplier1=String::new;
        Supplier<List<String>> supplier2=ArrayList::new;
        Supplier<Map<String,String>> supplier3=HashMap::new;
        supplier1.get();
        supplier2.get();
        supplier3.get();

3、接口中的默认方法

在Java 8中,接口引入了默认方法(Default Methods)的概念。默认方法允许我们在接口中定义实现的方法,而不需要子类必须实现它们。这提供了一种方式,允许我们在不破坏与现有代码的兼容性的情况下,向接口添加新方法。默认方法允许我们逐步演进接口,同时保持与旧代码库的兼容性。默认方法使用default关键字进行标记。以下是一个简单示例:

public interface MyInterface {

    // 抽象方法
    void abstractMethod();

    // 默认方法
    default void defaultMethod() {
        System.out.println("调用默认方法");
    }

    // 静态方法
    static void staticMethod() {
        System.out.println("调用静态方法");
    }
}
@Service
public class MyInterfaceImpl implements MyInterface {

    @Override
    public void abstractMethod() {
        System.out.println("重写抽象方法");
    }
}
        MyInterfaceImpl myInterface = new MyInterfaceImpl();
        myInterface.abstractMethod();
        myInterface.defaultMethod();

        MyInterface.staticMethod();

4、函数式接口

在Java 8中,函数式接口(Functional Interface)是一个只有一个抽象方法的接口。由于Lambda表达式的引入,函数式接口变得特别有用,因为Lambda表达式可以被用来简洁地表示这些接口的实现。函数式接口是Java实现函数式编程特性的基础。

4.1 自定义函数式接口

Lambda 表达式经常与函数式接口一起使用。定义一个无实现类的接口,接口方法有且只有一个

@FunctionalInterface
public interface MyService {
    void myMethod(String l);
}

MyService myService=(s)-> System.out.println(s);
//或
MyService myService=(s)-> {
	System.out.println(s);
};

myService.myMethod("这是一个自定义的函数式接口");


/* 等价写法 */
public interface MyService {
	void myMethod(String l);
}

public class MyServiceImpl implements MyService {
    @Override
    public void myMethod(String l) {
        System.out.println(l);
    }	
}

myService.myMethod("这是一个字符串");

4.2 内置函数式接口

Java 8 中添加了很多内置的函数式接口,比如 Runnable, Callable, Comparator, Predicate 等,路径在java.util.function包中。
在这里插入图片描述


类型参数定义及示例:

  • < T > 参数输入类型
  • < U > 第二个参数输入类型
  • < R > 参数输出类型

Predicate< T > 接口

        Predicate<Integer> predicate = s -> s>6;
        boolean result = predicate.test(7); // true
        System.out.println(result);

Function<T, R>接口

        Function<Integer, Integer> function = s -> s * s;
        Integer result = function.apply(6); // 36
        System.out.println(result);

BiFunction<T, U, R> 接口

        BiFunction<Integer,Integer,String> biFunction = (x,y)->{
            Integer z=x*y;
          return "计算结果为:"+z;
        };
        String result =biFunction.apply(3,6);
        System.out.println(result); // 计算结果为:18

5、Date/Time API

Java 8引入了全新的Date/Time API,它提供了更简洁、易读和强大的日期时间处理能力。这个新的API是线程安全的,并且是不可变的,这意味着每次对日期时间的修改都会返回一个新的对象,而原始对象则保持不变,路径在java.time包中。以下是Java 8 Date/Time API的一些主要类和接口:

方法说明默认输出格式
LocalDate表示没有时区的日期2024-03-27
LocalTime表示没有时区的时间17:00:23.741
LocalDateTime表示包含日期和时间的日期时间,没有时区2024-03-27T17:00:23.741
ZonedDateTime表示包含日期、时间、时区的日期时间2024-03-27T17:00:23.742+08:00[GMT+08:00]
OffsetTime表示带有时区偏移的时间17:00:23.742+08:00
OffsetDateTime表示带有时区偏移的日期时间2024-03-27T17:00:23.743+08:00
Period表示年、月、日之间的时间段P2024Y1M1D
Duration表示时间量,通常以秒和纳秒为单位PT60H
TemporalAdjuster用于调整日期和时间的接口/
ChronoUnit枚举类型,表示时间单位,如天、小时、分钟等/
ZoneId表示时区ID的类/
ZoneOffset表示时区偏移的类/
DateTimeFormatter格式化日期时间/

一些常用方法示例:

        //表示没有时区的日期
        LocalDate localDate1 =LocalDate.now();
        LocalDate localDate2 =LocalDate.of(2024, 1,1);
        System.out.println(
                "当前日期:"+localDate1.toString()+"\n"+
                "设置规定日期:"+localDate2.toString()
        );


        //格式化日期
        DateTimeFormatter dateTimeFormatter1 =DateTimeFormatter.ofPattern("yyyy/MM/dd");
        String format1 = localDate1.format(dateTimeFormatter1);
        String format2 = localDate1.format(DateTimeFormatter.ISO_DATE);
        System.out.println(
                "自定义日期格式:"+format1+"\n"+
                "内置日期格式:"+format2
        );

        //解析字符串为日期
        LocalDate parse1 = LocalDate.parse("2024-01-01");
        LocalTime parse2 = LocalTime.parse("12:00:00",DateTimeFormatter.ISO_TIME);
        System.out.println(parse1+"\n"+parse2);


        //表示没有时区的时间
        LocalTime localTime1 =LocalTime.now();
        LocalTime localTime2 =LocalTime.of(10,0,0);
        System.out.println(
                "当前时间:"+localTime1.toString()+"\n"+
                "设置规定时间:"+localTime2.toString()
        );

        //格式化时间
        DateTimeFormatter dateTimeFormatter2 =DateTimeFormatter.ofPattern("HH点mm分ss秒");
        String format3 = localTime1.format(dateTimeFormatter2);
        String format4 = localTime1.format(DateTimeFormatter.ISO_TIME);
        System.out.println(
                "自定义时间格式:"+format3+"\n"+
                "内置时间格式:"+format4
        );


        //表示包含日期和时间的日期时间,没有时区
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);

        //格式化时间日期
        DateTimeFormatter dateTimeFormatter3 =DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        String format5 = localDateTime.format(dateTimeFormatter3);
        String format6 = localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        System.out.println(
                "自定义时间日期格式:"+format5+"\n"+
                "内置时间日期格式:"+format6
        );

        //表示包含日期、时间、时区的日期时间
        ZonedDateTime zonedDateTime =ZonedDateTime.now();
        System.out.println(zonedDateTime);

        //表示带有时区偏移的时间
        OffsetTime offsetTime =OffsetTime.now();
        System.out.println(offsetTime);

        //表示带有时区偏移的日期时间
        OffsetDateTime offsetDateTime =OffsetDateTime.now();
        System.out.println(offsetDateTime);

        //表示年、月、日之间的时间段
        Period period =Period.of(2024,1,1);
        System.out.println(period.toString());

        //表示时间量,通常以秒和纳秒为单位
        Duration duration=Duration.of(60, ChronoUnit.HOURS);
        System.out.println(duration);

        //用于调整日期和时间的接口
        TemporalAdjuster temporalAdjuster =(s)->{
            return s;
        };
        LocalDate localDate = localDate1.plusDays(1);
        Temporal temporal = temporalAdjuster.adjustInto(localDate);
        System.out.println(temporal.toString());

        //枚举类型,表示时间单位,如天、小时、分钟等
        ChronoUnit chronoUnit=ChronoUnit.DAYS;
        System.out.println(chronoUnit.toString());

        //表示时区ID的类
        ZoneId zoneId=ZoneId.of("GMT+08:00");
        System.out.println(zoneId.toString());

        //表示时区偏移的类
        ZoneOffset zoneOffset =ZoneOffset.of("+08:00");
        System.out.println(zoneOffset.toString());

6、Optional 容器类型

Optional 是 Java 8 引入的一个容器对象,用于表示某个值存在或不存在,而不是传统的 null 值。Optional 提供了一种更优雅和类型安全的方式来处理可能为 null 的值,从而避免了 NullPointerException。当你有一个值可能是 null,并且你想以更清晰、更简洁的方式处理这种情况时,Optional 就非常有用。使用 Optional 可以使你的代码更易于阅读和维护,同时减少处理 null 值时的错误。

/* 示例 */
        // 使用 of 方法创建一个包含值的 Optional
        Optional<String> optional1 = Optional.of("hello world");

        // 使用 ofNullable 方法创建一个可能包含 null 的 Optional
        Optional<String> optional2 = Optional.ofNullable(null);

        // 创建一个空的 Optional
        Optional<String> optional3 = Optional.empty();

        System.out.println(
                "optional1的值:"+optional1+"\n"+
                "optional2的值:"+optional2+"\n"+
                "optional3的值:"+optional3
        );
		/* 常用方法 */
		// isPresent() 检查值是否存在
        boolean present = optional1.isPresent(); //true
        System.out.println(present);

        // ifPresent() 如果值存在则执行给定的操作
        optional1.ifPresent(s->{
            System.out.println("打印:"+s); //打印:hello world
        });

        // get() 获取值,如果 Optional 为空则抛出 NoSuchElementException
        String s1 = optional1.get();

        // orElse() 如果值存在则返回它,否则返回提供的默认值
        String s2 = optional2.orElse("optional2值为空");

        // orElseGet() 如果值存在则返回它,否则使用提供的 Supplier 生成默认值
        String s3 = optional3.orElseGet(() -> {
            return "optional3值为空";
        });

        System.out.println(
                "s1:"+s1+"\n"+
                "s2:"+s2+"\n"+
                "s3:"+s3
        );

        // map() 如果值存在则应用函数并返回新的 Optional,不存在则不执行map()
        Optional<Integer> optional4 = optional1.map(String::length);
        Optional<Integer> optional5 = optional2.map(String::length);
        System.out.println(
                "optional4:"+optional4.orElse(-1)+"\n"+
                "optional5:"+optional5.orElse(-1)
        );

        // flatMap() 如果值存在则应用函数并返回结果的 Optional,否则返回空的 Optional
        Optional<String> optional6 = optional1.flatMap(s -> {
            return Optional.ofNullable("获取optional1的值:" + s);
        });
        Optional<String> optional7 = optional2.flatMap(s -> {
            return Optional.ofNullable("获取optional2的值:" + s);
        });
        System.out.println(
                "optional6:"+optional6.orElse(null)+"\n"+
                "optional7:"+optional7.orElse(null)
        );

7、Stream API

7.1 Streams流

在Java 8及更高版本中,Stream API 提供了一种新的、函数式的方法来处理集合(如 List、Set 等)。使用 Stream API,你可以轻松地对集合进行各种操作,如过滤、映射、排序、聚合等,而无需显式地编写循环。

方法简要说明
forEach(Consumer action)遍历元素,执行指定的操作
peek(Consumer action)遍历元素,插入节点调试,不影响原本操作
distinct()去除重复元素
sorted()升序排序
count()计数
filter(Predicate predicate)过滤出所有符合条件的元素 相当于if
map(Function mapper)将元素转换为特定类型
flatMap(Function mapper)嵌套map
IntStream.sum()聚合元素
boxed()将基本数据类型转换为对应的包装类类型
limit(long maxSize)限制元素数量,设定集合容量
skip(long n)跳过前N个元素
findFirst()返回第一个元素
findAny()返回任意一个元素
reduce(BinaryOperator accumulator)将所有元素归成一个约定的结果
collect(Collector collector)收集结果到一个集合中
allMatch(Predicate predicate)判断是否所有元素符合给定条件
anyMatch(Predicate predicate)判断是否有元素符合给定条件
noneMatch(Predicate predicate)判断是否所有元素都不符合给定条件
/* 示例 */
        // forEach 遍历元素,执行指定的操作
        List<String> list1 = Arrays.asList("a", "b", "c");
        list1.stream().forEach(s-> System.out.println(s));
        list1.stream().forEach(System.out::println);

        //peek 遍历元素,插入节点调试,不影响原本操作
        List<Integer> list = Arrays.asList(1,2,3,4,5);
        List<Integer> collect = list.stream()
                .filter(s->s>2)
                .peek(s -> System.out.println("输出:"+s))
                .collect(Collectors.toList());

        // distinct 去除重复元素
        List<Integer> list2 = Arrays.asList(1, 2, 1, 3);
        List<Integer> collect2 = list2.stream().distinct().collect(Collectors.toList());
        System.out.println(collect2);

        // sorted 升序排序
        List<String> list3 = Arrays.asList("a", "l", "c","j");
        List<String> collect3 = list3.stream().sorted().collect(Collectors.toList());
        System.out.println(collect3);

        // count 计数
        long count = list3.stream().count();
        System.out.println(count);

        // filter 过滤出所有符合条件的元素 相当于if
        List<Integer> list4 = Arrays.asList(9, 8, 7, 6);
        List<Integer> collect4 = list4.stream().filter(s -> s > 6).collect(Collectors.toList());
        System.out.println(collect4);

        // map 将元素转换为特定类型
        List<Integer> list5 = Arrays.asList(1,2,3,4,5);
        List<String> collect5 = list5.stream().map(s -> {
            return "计算结果为:"+s*s;
        }).collect(Collectors.toList());
        System.out.println(collect5);

        // flatMap 嵌套map
        List<Integer> collect6 = list5.stream().flatMap(s -> {
            return list5.stream().filter(a -> a > 4);
        }).collect(Collectors.toList());
        System.out.println(collect6);

        //sum 聚合元素
        int sum = list5.stream().mapToInt(s -> s * 2).sum();
        System.out.println("sum:" + sum);

        // boxed 将基本数据类型转换为对应的包装类类型
        int [] a ={1,2,3,4,5};
        List<Integer> collect7 = Arrays.stream(a).boxed().collect(Collectors.toList());
        System.out.println(collect7);

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        IntStream intStream = numbers.stream().mapToInt(i -> i);  //转成IntStream
        Stream<Integer> boxed = intStream.boxed();                //转成Stream<Integer>
        List<Integer> collect8 = boxed.collect(Collectors.toList());
        System.out.println(collect8);


        //limit 限制元素数量,设定集合容量
        List<Integer> list9 = Arrays.asList(1,2,3,4,5);
        List<Integer> collect9 = list9.stream().limit(2).collect(Collectors.toList());
        System.out.println(collect9);

        //skip 跳过前N个元素
        List<Integer> list10 = Arrays.asList(1,2,3,4,5);
        List<Integer> collect10 = list10.stream().skip(3).collect(Collectors.toList());
        System.out.println(collect10);

        //findFirst() 返回第一个元素,findAny() 返回任意一个元素
        List<Integer> list11 = Arrays.asList(1,2,3,4,5);
        Optional<Integer> first = list11.stream().findFirst();
        Optional<Integer> any = list11.stream().findAny();
        System.out.println(
                "返回第一个元素:"+first.toString()+"\n"+
                "返回任意一个元素:"+any.toString()
        );

        /* 将所有元素归成一个约定的结果 */
        List<Integer> list1 = Arrays.asList(1,2,3,4,5);
        Integer reduce1 = list1.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce1);

        List<String > list2 = Arrays.asList("a","b","c");
        Optional<String> reduce2 = list2.stream().reduce((x, y) -> x + y);
        System.out.println(reduce2.toString());

        List<Integer> list3 = Arrays.asList(1,2,3,4,5);
        BinaryOperator<Integer> binaryOperator = (x, y)->x*y;
        Integer reduce3 = list3.stream().reduce(1,binaryOperator, binaryOperator);
        System.out.println(reduce3);
        List<Integer> list = Arrays.asList(1,2,3,4,5);

        //allMatch 判断是否所有元素符合给定条件
        Predicate<Integer> predicate1 = s -> s>0;
        boolean b1 = list.stream().allMatch(predicate1);
        System.out.println("判断是否所有元素符合给定条件: "+b1);

        //anyMatch 判断是否有元素符合给定条件
        Predicate<Integer> predicate2 = s -> s>4;
        boolean b2 = list.stream().anyMatch(predicate2);
        System.out.println("判断是否有元素符合给定条件: "+b2);

        //noneMatch 判断是否所有元素都不符合给定条件
        Predicate<Integer> predicate3 = s -> s>10;
        boolean b3 = list.stream().noneMatch(predicate3);
        System.out.println("判断是否所有元素都不符合给定条件: "+b3);

7.2 Collectors收集器

在Java 8中,Collectors是一个静态工具类,它提供了许多有用的收集器(Collector)实现,用于支持在流(Stream)上的各种聚合操作。这些收集器可以用于收集流中的元素,并生成一个汇总结果或一个汇总结果的集合。以下是一些常用的Collectors收集器方法:

方法简要说明
toList()将流中的元素收集到一个List中
toSet()将流中的元素收集到一个Set中,去除重复项
toCollection(Supplier collectionFactory)将流中的元素收集到指定的集合类型中
toMap(Function keyMapper,Function valueMapper,BinaryOperator mergeFunction)将流中的元素收集到一个Map中
counting()计算流中元素的数量
summingInt(ToIntFunction mapper)对流中元素的某个属性进行求和,针对不同类型有不同方法,如:summingDouble,summingLong
averagingInt(ToIntFunction mapper)计算流中元素的某个属性的平均值,针对不同类型有不同方法,如:averagingDouble,averagingLong
maxBy(Comparator comparator)根据指定比较器找到流中最大的元素
minBy(Comparator comparator)根据指定比较器找到流中最小的元素
joining(CharSequence delimiter)将流中的元素连接成一个字符串,可以使用指定的分隔符
groupingBy(Function classifier)根据分类函数对流中的元素进行分组
reducing(BinaryOperator op)对流中的元素进行规定约定的操作
mapping(Function mapper,Collector downstream)对流中的元素应用映射函数,并对结果进行收集
collectingAndThen(Collector downstream,Function finisher)包裹另一个收集器,对其结果进行二次处理
/* 示例 */
        //toList 将流中的元素收集到一个List中
        List<Integer> list1 = Arrays.asList(1,2,3,4,5);
        List<Integer> collect1 = list1.stream()
                .filter(s->s>3)
                .collect(Collectors.toList());
        System.out.println(collect1);

        //toSet 将流中的元素收集到一个Set中,去除重复项
        List<Integer> list2 = Arrays.asList(1,2,3,4,5,3);
        Set<Integer> collect2 = list2.stream().collect(Collectors.toSet());
        System.out.println(collect2);

        //toCollection 将流中的元素收集到指定的集合类型中
        List<Integer> list3 = Arrays.asList(1,2,3,4,5,3);
        HashSet<Integer> collect3 = list3.stream().collect(Collectors.toCollection(HashSet::new));
        System.out.println(collect3);

        //toMap 将流中的元素收集到一个Map中
        List<Person> list4 = Arrays.asList(
                new Person("L", 1, 18),
                new Person("J", 2, 18),
                new Person("A", 1, 30),
                new Person("A", 1, 20)
        );

        //key不鞥重复,针对有重复的情况需要处理 ------ (t,u)->u:当两个key相同时怎么处理
        Map<String, Integer> collect4 = list4.stream()
                .collect(Collectors.toMap(Person::getName, Person::getAge, (t, u) -> u));
        System.out.println(collect4);

        //counting 计算流中元素的数量
        List<Integer> list5 = Arrays.asList(1,2,3,4,5);
        Long collect5 = list5.stream().collect(Collectors.counting());
        System.out.println(collect5);

        //summingInt 对流中元素的某个属性进行求和,针对不同类型有不同方法,如:summingDouble,summingLong
        List<Integer> list6 = Arrays.asList(1,2,3,4,5);
        Integer collect6 = list6.stream().collect(Collectors.summingInt(Integer::intValue));
        System.out.println(collect6);

        //averagingInt 计算流中元素的某个属性的平均值,针对不同类型有不同方法,如:averagingDouble,averagingLong
        List<Integer> list7 = Arrays.asList(1,2,3,4,5);
        Double collect7 = list7.stream().collect(Collectors.averagingInt(Integer::intValue));
        System.out.println(collect7);

        //maxBy 根据指定比较器找到流中最大的元素
        List<Integer> list8 = Arrays.asList(1,2,3,4,5);
        Optional<Integer> collect8 = list8.stream()
                .collect(Collectors.maxBy(Comparator.comparingInt(Integer::intValue)));
        System.out.println(collect8.get());

        //minBy 根据指定比较器找到流中最小的元素
        List<Integer> list9 = Arrays.asList(1,2,3,4,5);
        Optional<Integer> collect9 = list9.stream()
                .collect(Collectors.minBy(Comparator.comparingInt(Integer::intValue)));
        System.out.println(collect9.get());

        //joining 将流中的元素连接成一个字符串,可以使用指定的分隔符
        List<String> list10 = Arrays.asList("a","b","c");
        String collect10 = list10.stream().collect(Collectors.joining("/"));
        System.out.println(collect10);
        //groupingBy 根据分类函数对流中的元素进行分组
        List<Person> list11 = Arrays.asList(
                new Person("L", 1, 18),
                new Person("J", 2, 18),
                new Person("A", 1, 30)
        );
        Map<Integer, List<Person>> collect11 = list11.stream()
                .collect(Collectors.groupingBy(Person::getSex));
        System.out.println(collect11);

        //reducing 对流中的元素进行规定约定的操作
        List<Integer> list12 = Arrays.asList(1,2,3,4,5);
        Optional<Integer> collect12 = list12.stream().collect(Collectors.reducing(Integer::sum));
        System.out.println(collect12.get());
        BinaryOperator<Integer> binaryOperator12=(x,y)->x+y;
        Optional<Integer> collect13 = list12.stream().collect(Collectors.reducing(binaryOperator12));
        System.out.println(collect13.get());

        //mapping 对流中的元素应用映射函数,并对结果进行收集
        List<Integer> list14 = Arrays.asList(1,2,3,4,5);
        List<String> collect14 = list14.stream()
                .collect(Collectors.mapping(String::valueOf, Collectors.toList()));
        System.out.println(collect14);

        Function<Integer,Integer> function15 =(s)->s*s;
        List<Integer> collect15 = list14.stream()
                .collect(Collectors.mapping(function15, Collectors.toList()));
        System.out.println(collect15);

        //collectingAndThen 包裹另一个收集器,对其结果进行二次处理
        List<Integer> list16 = Arrays.asList(1,2,3,4,5);
        Function<Integer,String> function16 =(s)->{
            int l =s*s;
            return "计算它的平方:" + l;
        };
        String collect16 = list16.stream()
                .collect(Collectors.collectingAndThen(Collectors.summingInt(Integer::intValue), function16));
        System.out.println(collect16);

7.3 Parallel Streams(扩展)

Java中的Parallel Streams是Java 8引入的一个新特性,它允许你以并行的方式处理数据集合,从而充分利用多核处理器的优势来提高程序的执行效率。通过使用Parallel Streams,你可以将原本顺序执行的任务分解成多个子任务,并在多个线程上同时执行这些子任务,从而加速数据的处理速度。

//示例
        List<Long> list = new ArrayList<>();
        for (long i = 1; i <=1_000_000 ; i++) {
            list.add(i);
        }

        long start1 = System.currentTimeMillis();
        List<Long> collect1 = list.stream()
                .map(s -> s * s)
                .collect(Collectors.toList());
        long end1 = System.currentTimeMillis();
        System.out.println(new StringBuffer().append(end1-start1).append("ms"));

        long start2 = System.currentTimeMillis();
        List<Long> collect2 = list.parallelStream()
                .map(s -> s * s)
                .collect(Collectors.toList());
        long end2 = System.currentTimeMillis();
        System.out.println(new StringBuffer().append(end2-start2).append("ms"));
使用并行流(parallel stream)需要注意:
确保你的任务是可以并行化的,即任务之间没有依赖关系,可以独立执行。
对于大规模数据集,使用并行流可以提高性能。但对于小数据集,顺序流可能更加高效。
避免在并行流中使用阻塞操作,这可能会导致线程阻塞和性能下降。
在使用并行流时,要注意线程安全问题,确保你的代码是线程安全的。

Java 7 常用新特性(补充)

1、二进制字面量

在Java 7中,引入了对二进制字面量的支持。二进制字面量以0b或0B开头,后跟一系列的0和1。

/* 示例 */
        int i =0b11111111;
        int j =0B00000010;
        System.out.println("i: "+i+"\n"+"j: "+j); //i: 255 j: 2

2、数字字面量下划线支持

在Java 7中,引入了对数字字面量下划线的支持。这个特性允许在数字字面量中添加下划线(_)作为分隔符,以提高数字的可读性。这对于表示非常大的数字或者需要分组显示的数字特别有用。这个特性不会影响数字的值,下划线只是作为视觉上的分隔符。下划线可以出现在数字字面量的任何位置,除了数字的首位和末位,以及小数点的前面和后面。这个特性只适用于整数和浮点数的字面量,不适用于十六进制或八进制字面量。此外,虽然下划线可以提高代码的可读性,但过度使用可能会使代码变得难以阅读,因此建议适度使用。

/* 示例 */
        int i=1_000_000;
        double r =3.14_1_5_92_7;
        long l=7_7_7_7L;
        System.out.println(i); //1000000
        System.out.println(r); //3.1415927
        System.out.println(l); //7777

3、switch中使用String

在Java 7之前,switch语句仅支持字节型(byte)、短整型(short)、整型(int)、字符型(char)、枚举(enum)以及从Java 5开始引入的字符串类型(String)。然而,在Java 7中,对于switch语句中使用String类型的支持得到了扩展,允许你在switch语句中直接使用字符串。switch语句对字符串的比较是区分大小写的,switch语句中字符串的比较是通过字符串的equals方法进行的

/* 示例 */
        String a ="List";
        switch (a){
            case "list":
                System.out.println("list");
                break;
            case "List":
                System.out.println("List");
                break;
            case "set":
                System.out.println("set");
                break;
            default:
                System.out.println("default");
        }

4、对集合类的语言支持

Java 7 对集合类(Collections)的语言支持主要体现在对泛型实例创建的简化以及"diamond"操作符的引入,它减少了泛型代码中的冗余和错误。

4.1 泛型实例创建的简化

在Java 7之前,当创建一个泛型集合时,通常需要为集合和它的元素类型都指定泛型参数。Java 7中,对于类型推断的改进使得这种指定变得不必要,因为编译器可以根据上下文自动推断类型。

/* 示例 */
        //Java 7之前写法
        List<String> list1 = new ArrayList<String>();
        
        //Java 7及以后的版本
        List<String> list2 = new ArrayList<>();

4.2 "diamond"操作符

"diamond"操作符在其他泛型上下文中得到应用,如方法返回类型、方法参数、构造函数参数等。它使得泛型代码更加简洁,减少了重复的类型参数声明。

/* 示例 */
        //Java 7之前写法
        List<String> list1 = Arrays.asList(new String[]{"a", "b", "c"});

        //Java 7及以后的版本
        List<String> list2 = Arrays.asList("a", "b", "c");
/* asList 源码 */
	
	// <T>:表示数组中对象的类,编译器可通过上下文自动推断类型
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

5、自动资源管理

在Java 7中,为了更方便地管理资源(如文件流、数据库连接、网络连接等),引入了自动资源管理(Automatic Resource Management)的概念,这个特性允许开发者在try语句块中声明一个或多个资源,这些资源在try语句块执行完毕后会自动关闭。这避免了手动关闭资源的繁琐操作,并减少了因忘记关闭资源而导致的资源泄露问题。要使用这个特性,资源类必须实现AutoCloseable接口或Closeable接口(Closeable接口是AutoCloseable接口的子接口)。

/* 示例 */
	try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
	    String line;
	    while ((line = br.readLine()) != null) {
	        System.out.println(line);
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	}

在上面的代码中,BufferedReader实现了Closeable接口,因此在try语句块执行完毕后,BufferedReader会自动关闭,无需手动调用br.close()。

6、try-catch多异常捕获

在Java 7及以后的版本中,try-catch语句块支持捕获多个异常类型,这是通过在一个catch块中指定多个异常类型来实现的,这些类型之间用竖线(|)分隔。这种语法允许你在一个catch块中处理多个不同类型的异常,而无需为每个异常类型编写单独的catch块。

/* 示例 */
        //当try块中的代码抛出异常时,Java虚拟机(JVM)会检查catch块中列出的异常类型,并找到第一个与抛出的异常类型匹配的catch块。
        try {
            int i =1/0; // class java.lang.ArithmeticException

            String s =null; //class java.lang.NullPointerException
            s.indexOf(0);
        }catch (ArithmeticException | NullPointerException e){
            System.out.println("程序报错:"+e.getClass());
        }

7、文件流

在Java 7中,文件流(File Streams)的处理得到了显著改进,特别是通过引入java.nio.file包中的新API。这些新API提供了更简洁、更直观的方式来处理文件I/O操作,同时提供了对文件属性、文件访问权限以及异步文件I/O的更全面支持。

7.1 文件路径(Path)

java.nio.file.Path接口是文件路径的抽象表示。它提供了许多方法来操作路径,如解析、组合、规范化等。Paths类是一个工具类,提供了静态方法来创建Path对象。

/* 示例 */
        Path path = Paths.get("C:\\Users\\777\\Desktop","MyPaths.txt");
        System.out.println(path); //C:\Users\777\Desktop\MyPaths.txt

7.2 文件属性(FileAttribute)

Java 7引入了FileAttribute接口,它允许你获取和设置文件的各种属性,如创建时间、最后访问时间、文件类型等。

/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        System.out.println(path);

        BasicFileAttributes basicFileAttributes = Files.readAttributes(path, BasicFileAttributes.class);
        System.out.println(
                "文件创建时间:"+basicFileAttributes.creationTime()+"\n"+
                "文件大小"+basicFileAttributes.size()
        );

7.3 文件I/O

java.nio.file包中的Files类提供了一组静态方法,用于简化文件I/O操作。以下是一些常见方法:

检查文件路径是否存在
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        try{
            //检查文件路径是否存在
            boolean exists = Files.exists(path);
            System.out.println(exists);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }
创建目录
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths");
        try{
            /*
            创建目录,如果目录已存在,会抛出java.nio.file.FileAlreadyExistsException
            如果是上级路径目录不存在,会抛出java.nio.file.NoSuchFileException
            */
            //创建目录
            Files.createDirectory(path);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }
读取文件
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        try{
            //读取文件
            List<String> lines = Files.readAllLines(path);
            System.out.println(lines);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }
写入文件
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        try{
            //写入文件
            String content = "Hello, World!";
            Files.write(path, content.getBytes());
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }
复制文件
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        Path copyPath = Paths.get("D:\\File","Copy_Paths.txt");
        try{
            /*
            复制文件,如果目标文件已存在,会抛出java.nio.file.FileAlreadyExistsException
            * */
            //复制文件
            Files.copy(path,copyPath);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

/****************************/

		/*
		强制复制文件
		*/
        Path path = Paths.get("D:\\File","MyPaths.txt");
        Path copyPath = Paths.get("D:\\File","Copy_Paths.txt");
        try{
            //使用copy方法第三个参数强制复制文件
            Files.copy(path,copyPath,StandardCopyOption.REPLACE_EXISTING);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

移动文件
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        Path movePath = Paths.get("D:\\File","Move_Paths.txt");
        try{
            /*
            移动文件,如果目标文件已存在,会抛出java.nio.file.FileAlreadyExistsException
            * */
            //移动文件
            Files.move(path,movePath);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

/****************/

		/*
		强制移动文件
		*/
        Path path = Paths.get("D:\\File","MyPaths.txt");
        Path movePath = Paths.get("D:\\File","Move_Paths.txt");
        try{
            //使用move方法第三个参数强制移动文件
            Files.move(path,movePath,StandardCopyOption.REPLACE_EXISTING);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

删除文件
/* 示例 */
        Path path = Paths.get("D:\\File","MyPaths.txt");
        try{
            //删除目录文件
            Files.delete(path);
        }catch (IOException e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

7.4 异步文件I/O

Java 7引入了异步文件I/O,允许非阻塞的文件读写操作。AsynchronousFileChannel类提供了异步读写文件的方法。

/* 读文件示例 */
        Path path = Paths.get("D:\\File","My_Paths.txt");
        try(
                AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
        ){

            ByteBuffer buffer = ByteBuffer.allocate(1024);
            channel.read(buffer,0,buffer,new CompletionHandler<Integer,ByteBuffer>(){
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    // 完成读取后
                    attachment.flip(); //切换为读数据模式
                    while (attachment.hasRemaining()) {
                        System.out.print((char) attachment.get());
                    }
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    // 读取失败后
                    exc.printStackTrace();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
/* 写文件示例 */
        Path path = Paths.get("D:\\File","My_Paths.txt");
        try(
                AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
        ){
            ByteBuffer buffer = ByteBuffer.allocate(64);
            buffer.put("写入文字".getBytes());
            buffer.flip();//切换为读数据模式
            channel.write(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    System.out.println("写入完成");
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    exc.printStackTrace();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }

7.5 文件遍历

在Java 7中,java.nio.file包提供了Files和Path类,它们提供了一种强大的方式来遍历文件系统。特别是,Files类中的walk和walkFileTree方法提供了遍历文件系统的能力。

Files.walk方法

Files.walk方法默认会遍历所有的子目录。如果你只想遍历顶层目录,你可以传递一个参数1给Files.walk方法。

/* 示例 */
        Path path = Paths.get("D:","File");
        try (
                Stream<Path> walk = Files.walk(path);
                ){
            walk.forEach(System.out::println);
        }catch (Exception e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }
Files.walkFileTree方法

Files.walkFileTree允许你提供一个FileVisitor,你可以在其中定义如何处理每个文件。
示例:
当我们使用Files.delete删除目录时,如果目录不为空(目录内还有文件),则会抛出java.nio.file.DirectoryNotEmptyException异常。

        Path path = Paths.get("D:","File");//路径目录非空
        try {
            Files.delete(path);
        }catch (Exception e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

此时需要使用Files.walkFileTree方法遍历目录,先删除路径目录下的文件,最后再删除目录本身。

/* 示例 */
        Path path = Paths.get("D:","File");//路径目录非空
        try {
            Files.walkFileTree(path, new FileVisitor<Path>() {
                //访问目录前调用
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    System.out.println("开始遍历目录:"+dir);
                    return FileVisitResult.CONTINUE;
                }

                //遍历目录时调用
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    System.out.println("执行文件操作(delete):"+file);
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }

                访问失败时调用
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    System.out.println("Failed File!!! "+file);
                    return FileVisitResult.CONTINUE;
                }

                //遍历目录完成后调用
                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    System.out.println("执行目录操作(delete):"+dir);
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        }catch (Exception e){
            System.out.println(e.getClass());
            e.printStackTrace();
        }

如何学习

学习参考资料查阅至文心大模型。 百度文心一言,中国人自己的chatgpt。

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值