Java7、Java8特性总结

一、Java7

1、try-with-resources

JDK7之前的写法

        String path = "/Users/hanxiantao/Desktop/hello.txt";
        OutputStream out = null;
        try {
            out = new FileOutputStream(path);
            out.write("hello world".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

使用try-with-resources

在try后的括号中初始化资源,可以实现资源⾃动关闭

        String path = "/Users/hanxiantao/Desktop/hello.txt";
        try (OutputStream out = new FileOutputStream(path);) {
            out.write("hello world".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

注意点

  • 实现了AutoCloseable接⼝的类,在try()⾥声明该类实例的时候,try结束后⾃动调⽤的close()⽅法,这个动作会早于finally⾥调⽤的⽅法
  • 不管是否出现异常,try()⾥的实例都会被调⽤close()⽅法
  • try()⾥⾯可以声明多个⾃动关闭的对象,越早声明的对象,会越晚被close掉

二、Java8

1、接口的增强

在JDK1.8以前接⼝⾥⾯是只能有抽象⽅法,不能有任何⽅法的实现的

JDK1.8引⼊了新的关键字default,使⽤default修饰⽅法,可以在接⼝⾥⾯定义具体的⽅法实现

默认⽅法:接⼝⾥⾯定义⼀个默认⽅法,又叫做扩展方法,在实现该接口时,该默认扩展方法在子类上可以直接使用

public interface Animal {
    void run();

    default void breath() {
        System.out.println("使用氧气呼吸");
    }
}
        Animal dog = new Animal() {
            @Override
            public void run() {
                System.out.println("狗run");
            }
        };
        dog.breath();
        dog.run();

静态方法:通过接⼝名.静态⽅法来访问接⼝中的静态⽅法

public interface Animal {
    void run();

    default void breath() {
        System.out.println("使用氧气呼吸");
    }

    static void eat() {
        System.out.println("吃");
    }
}
        Animal.eat();

2、base64加解密API

1)、什么是Base64编码

Base64是⽹络上最常⻅的⽤于传输8Bit字节码的编码⽅式之⼀,Base64就是⼀种基于64个可打印字符来表示⼆进制数据的⽅法,基于64个字符A-Z、a-z、0-9、+、/的编码⽅式(在日常使用中我们还会看到===号出现在Base64的编码结果中,=在此是作为填充字符出现),是⼀种能将任意⼆进制数据⽤64种元字符组合成字符串的⽅法,⽽这个⼆进制数据和字符串之间是可以互相转换的

2)、JDK1.8之前相关API

使⽤JDK⾥sun.misc下的BASE64Encoder和BASE64Decoder这两个类

    public static void main(String[] args) throws IOException {
        BASE64Encoder encoder = new BASE64Encoder();
        BASE64Decoder decoder = new BASE64Decoder();
        String text = "hello world";
        byte[] bytes = text.getBytes("UTF-8");

        //编码
        String encodedText = encoder.encode(bytes);
        System.out.println(encodedText);

        //解码
        byte[] bytes2 = decoder.decodeBuffer(encodedText);
        System.out.println(new String(bytes2, "UTF-8"));
    }

执行结果

aGVsbG8gd29ybGQ=
hello world

缺点:编码和解码的效率⽐较差

3)、JDK1.8相关API

        Base64.Encoder encoder = Base64.getEncoder();
        Base64.Decoder decoder = Base64.getDecoder();

        String text = "hello world";
        byte[] bytes = text.getBytes("UTF-8");

        //编码
        String encodedText = encoder.encodeToString(bytes);
        System.out.println(encodedText);

        //解码
        byte[] bytes2 = decoder.decode(encodedText);
        System.out.println(new String(bytes2, "UTF-8"));

3、时间日期处理类

参考之前的博客开发中常用Java8日期和时间相关API

4、Optional类

Optional类主要解决的是空指针异常,本质是⼀个包含有可选值的包装类,这意味着Optional类既可以含有对象也可以为空

1)、创建Optional类

of():null值作为参数传递进去,则会抛异常

        Student student = null;
        Optional<Student> optional = Optional.of(student);

ofNullable():如果对象即可能是null也可能是⾮null

        Student student = null;
        Optional<Student> optional = Optional.ofNullable(student);

2)、访问Optional对象的值

get():获取Optional对象的值

isPresent():如果值存在会返回true,调⽤get()⽅法会返回该对象⼀般使⽤get()之前需要先验证是否有值,不然还会抛出异常java.util.NoSuchElementException: No value present

        Student student = null;
        Optional<Student> optional = Optional.ofNullable(student);
        if (optional.isPresent()) {
            System.out.println("optional不为空");
            Student s = optional.get();
        } else {
            System.out.println("optional为空");
        }

3)、兜底方法

orElse():如果Optional对象有值则返回该值,否则返回传递给它的参数值

        Student s1 = null;
        Student s2 = new Student("小明", 22);
        Student student = Optional.ofNullable(s1).orElse(s2);
        System.out.println(student);
        Student s1 = null;
        Integer age = Optional.ofNullable(s1).map(obj -> obj.getAge()).orElse(6);
        System.out.println(age);

5、Lambda表达式

函数编程:即可理解是将⼀个函数(也称为⾏为)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的POJO类),⽽函数式编程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递)

        List<Integer> list = Arrays.asList(22, 30, 19);
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);//降序
            }
        });
        list.stream().forEach(e -> System.out.println(e));

或者

        List<Integer> list = Arrays.asList(22, 30, 19);
        Collections.sort(list, Comparator.reverseOrder());//降序
        list.stream().forEach(e -> System.out.println(e));

使用Lambda表达式

        List<Integer> list = Arrays.asList(22, 30, 19);
        Collections.sort(list, (a, b) -> b.compareTo(a));//降序
        list.stream().forEach(e -> System.out.println(e));

Lambda表达式使⽤场景:⼀个接⼝中只包含⼀个⽅法,则可以使⽤Lambda表达式,这样的接⼝称之为函数接⼝语法 (params) -> expression

第⼀部分为括号内⽤逗号分隔的形式参数,参数是函数式接⼝⾥⾯⽅法的参数;第⼆部分为⼀个箭头符号->;第三部分为⽅法体,可以是表达式和代码块 

参数列表:
括号中参数列表的数据类型可以省略不写 
括号中的参数只有⼀个,那么参数类型和()都可以省略不写 

⽅法体: 
如果{}中的代码只有⼀⾏,⽆论是否返回值,可以省略{},return、分号要⼀起省略;其他则需要加上

Lambda表达式的实现⽅式在本质是以匿名内部类的⽅式进⾏实现

6、@FunctionalInterface注解

@FunctionalInterface:声明该接口为函数式接口

函数式接口:当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法

    public static void main(String[] args) {
        System.out.println(operator(5, 20, (x, y) -> x + y));
        System.out.println(operator(5, 20, (x, y) -> x - y));
        System.out.println(operator(5, 20, (x, y) -> x * y));
        System.out.println(operator(5, 20, (x, y) -> x / y));
    }

    public static Integer operator(Integer x, Integer y,
                                   OperFunction<Integer, Integer> of) {
        return of.operator(x, y);
    }

7、函数式编程

1)、Function接口

@FunctionalInterface
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;
    }
}

传⼊⼀个值经过apply()的计算返回另⼀个值,T为入参类型,R为出参类型

将转换逻辑提取出来,解耦合

        Function<Integer, Integer> func = p -> p + 100;
        System.out.println(func.apply(100));//200

map()方法:

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

2)、BiFunction接口

Function只能接收⼀个参数,如果要传递两个参数,则⽤BiFunction

@FunctionalInterface
public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}
        BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
        System.out.println(biFunction.apply(10, 20));//30

3)、Consumer接口

Consumer为消费型接⼝:有⼊参,⽆返回值

@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); };
    }
}

⽤途: 因为没有出参,常⽤于打印、发送短信等消费动作

        Consumer<String> consumer = a -> System.out.println(a);
        consumer.accept("hello world");

forEach()方法:

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
        List<String> list = Arrays.asList("hello", "world");
        list.forEach(e -> System.out.println(e));

4)、Supplier接口

Supplier为供给型接⼝:⽆⼊参,有返回值

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

⽤途:泛型⼀定和⽅法的返回值类型是⼀种类型,如果需要获得⼀个数据,并且不需要传⼊参数,可以使⽤Supplier接⼝,例如:⽆参的⼯⼚⽅法,即⼯⼚设计模式创建对象,简单来说就是提供者

    public static void main(String[] args) {
        Student student = newStudent();
        System.out.println(student.getName());
    }

    public static Student newStudent() {
        Supplier<Student> supplier = () -> {
            Student student = new Student();
            student.setName("小明");
            return student;
        };
        return supplier.get();
    }

5)、Predicate接口

Predicate为断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean

@FunctionalInterface
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);
    }
}

⽤途:接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据

    public static void main(String[] args) {
        List<String> list = Arrays.asList("aaa", "xsda", "a2weqe", "akkk", "eeq");
        List<String> result = filter(list, obj -> obj.startsWith("a"));
        System.out.println(result);
    }

    public static List<String> filter(List<String> list, Predicate<String> predicate) {
        List<String> result = new ArrayList<>();
        for (String str : list) {
            if (predicate.test(str)) {
                result.add(str);
            }
        }
        return result;
    }

filter()方法:

    Stream<T> filter(Predicate<? super T> predicate);

6)、⽅法引⽤与构造函数引⽤

        //静态函数
        Function<String, Integer> func = Integer::parseInt;
        System.out.println(func.apply("1024"));
        //非静态函数
        String context = "hello world";
        Function<Integer, String> func2 = context::substring;
        System.out.println(func2.apply(1));
        //构造函数 多个参数
        BiFunction<String, Integer, Student> biFunction = Student::new;
        System.out.println(biFunction.apply("小明", 22));
        //构造函数 单个参数
        Function<String, Student> fuc3 = Student::new;
        System.out.println(fuc3.apply("小明"));

8、集合框架

1)、流stream的使⽤

1)什么是stream

stream中⽂称为流,通过将集合转换为⼀种叫做流的元素队列,通过声明性⽅式,能够对集合中的每个元素进⾏⼀系列并⾏或串⾏的流⽔线操作

元素是特定类型的对象,所以元素集合看作⼀种流,流在管道中传输,且可以在管道的节点上进⾏处理,⽐如排序、聚合、过滤等操作

在这里插入图片描述

2)操作详情

  • 数据元素:原始集合,如List、Set、Map等
  • ⽣成流:可以是串⾏流stream()或者并⾏流parallelStream()
  • 中间操作:可以是排序、聚合、过滤、转换等
  • 终端操作:很多流操作本身就会返回⼀个流,所以多个操作可以直接连接起来,最后统⼀进⾏收集

2)、map()方法

将流中的每⼀个元素T映射为R(类似类型转换)

场景:转换对象,如JavaWeb开发中集合⾥⾯的DO对象转换为DTO对象

        List<User> list = Arrays.asList(new User(1, "Tom", "123"),
                new User(2, "Jack", "123456"));
        List<Object> userDTOList = list.stream().map(obj -> {
            UserDTO dto = new UserDTO(obj.getId(), obj.getUsername());
            return dto;
        }).collect(Collectors.toList());
        System.out.println(userDTOList);

3)、filter()方法

⽤于通过设置的条件过滤出元素

需求:过滤出字符串⻓度⼤于5的字符串

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        List<String> resultList = list.stream().filter(obj -> obj.length() > 5)
                .collect(Collectors.toList());
        System.out.println(resultList);

4)、sorted()方法

sorted()对流进⾏⾃然排序,其中的元素必须实现Comparable接⼝

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        List<String> resultList = list.stream().sorted()
                .collect(Collectors.toList());
        System.out.println(resultList);

sorted(Comparator<? super T> comparator)⽤来⾃定义升降序

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        //根据长度进行生序排序
        List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length()))
                .collect(Collectors.toList());
        System.out.println(resultList);
        //根据长度进行降序排序
//        List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(), Comparator.reverseOrder()))
//                .collect(Collectors.toList());
        List<String> resultList2 = list.stream().sorted(Comparator.comparing(String::length).reversed())
                .collect(Collectors.toList());
        System.out.println(resultList2);

5)、limit()方法

截断流使其最多只包含指定数量的元素

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        //截取长度前三的元素
        List<String> resultList = list.stream().sorted(Comparator.comparing(String::length).reversed())
                .limit(3).collect(Collectors.toList());
        System.out.println(resultList);

6)、allMatch()方法

检查是否匹配所有元素,只有全部符合才返回true

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        boolean flag = list.stream().allMatch(obj -> obj.length() > 1);
        System.out.println(flag);//true
        boolean flag2 = list.stream().allMatch(obj -> obj.length() > 5);
        System.out.println(flag2);//false

7)、anyMatch()方法

检查是否⾄少匹配⼀个元素

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        boolean flag = list.stream().anyMatch(obj -> obj.length() > 5);
        System.out.println(flag);//true

8)、max()方法

最大值

        List<Student> list = Arrays.asList(new Student(10), new Student(12),
                new Student(11), new Student(9));
        Optional<Student> optional = list.stream().max(Comparator.comparingInt(Student::getAge));
        System.out.println(optional.get().getAge());

9)、min()方法

最小值

        List<Student> list = Arrays.asList(new Student(10), new Student(12),
                new Student(11), new Student(9));
        Optional<Student> optional = list.stream().min(Comparator.comparingInt(Student::getAge));
        System.out.println(optional.get().getAge());

10)、并行流

集合做重复的操作,如果使⽤串⾏执⾏会相当耗时,因此⼀般会采⽤多线程来加快,Java8的parallelStream()⽤Fork/Join框架提供了并发执⾏能⼒

底层原理:线程池(ForkJoinPool)维护⼀个线程队列,分割任务,将⽗任务拆分成⼦任务

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        //顺序输出
        numbers.stream().forEach(System.out::println);
        System.out.println("--------------------");
        //并⾏乱序输出
        numbers.parallelStream().forEach(System.out::println);

1)parallelStream()并⾏是否⼀定⽐Stream串⾏快吗?

错误,数据量少的情况,可能串⾏更快,ForkJoin会耗性能

2)多数情况下并⾏⽐串⾏快,是否可以都⽤并⾏

不⾏,部分情况会有线程安全问题,parallelStream()⾥⾯使⽤的外部变量,⽐如集合⼀定要使⽤线程安全集合,不然就会引发多线程安全问题

        for (int i = 0; i < 10; ++i) {
            List<Integer> list = new ArrayList<>();
            IntStream.range(0, 100).parallel().forEach(list::add);
            System.out.println(list.size());
        }

执行抛出异常java.lang.ArrayIndexOutOfBoundsException

        for (int i = 0; i < 10; ++i) {
            List<Integer> list = new CopyOnWriteArrayList<>();
            IntStream.range(0, 100).parallel().forEach(list::add);
            System.out.println(list.size());
        }

size均为100

11)、reduce()方法

1)reduce(BinaryOperator<T> accumulator)方法

        //累加器
        Integer result = Stream.of(1, 2, 3, 4, 5)
                .reduce((item1, item2) -> item1 + item2).get();
        System.out.println(result);

2)reduce(T identity, BinaryOperator<T> accumulator)方法

        //初始值为10,在此基础上进行累加
        Integer result = Stream.of(1, 2, 3, 4, 5)
                .reduce(10, (sum, item) -> sum + item);
        System.out.println(result);

12)、forEach()方法

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

注意点:

  • 不能修改包含外部变量的值
  • 不能⽤break或者return或者continue等关键词结束或者跳过循环

9、收集器和集合统计

1)、收集器collector

collect()方法是一个终端操作,⽤于对流中的数据进⾏归集操作,collect()方法接收的参数是⼀个Collector

    <R, A> R collect(Collector<? super T, A, R> collector);

Collector:收集器,也是⼀个接⼝,它的⼯具类Collectors提供了很多⼯⼚⽅法

Collectors:⼯具类,提供了很多常⻅的收集器实现

Collectors.toList()
Collectors.toMap() 
Collectors.toSet() 

Collectors.toCollection():⽤⾃定义的实现Collection的数据结构收集
1)Collectors.toCollection(LinkedList::new) 
2)Collectors.toCollection(CopyOnWriteArrayList::new) 
3)Collectors.toCollection(TreeSet::new)

案例

        List<User> list = Arrays.asList(new User(1, "Tom"),
                new User(2, "Jack"), new User(1, "Lucky"));
        //如果有重复的key,则保留key1,舍弃key2
        Map<Integer, User> map = list.stream().collect(
                Collectors.toMap(User::getId, e -> e, (k1, k2) -> k1));
        System.out.println(map);

2)、joining()方法

拼接函数joining()

    public static Collector<CharSequence, ?, String> joining()
      
    //参数1:元素之间的连接符  
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
      
    //参数1:元素之间的连接符 参数2:前缀 参数3:后缀 
    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix)      

案例

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        String result = list.stream().collect(Collectors.joining(",", "[", "]"));
        System.out.println(result);

3)、partitioningBy()方法

    public static <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
        return partitioningBy(predicate, toList());
    }

需求:根据list⾥⾯进⾏分组,字符串⻓度⼤于4的为⼀组,其他为另外⼀组

        List<String> list = Arrays.asList("SpringBoot", "SpringCloud",
                "Redis", "Netty", "Java", "Docker");
        Map<Boolean, List<String>> map = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 4));
        System.out.println(map);

4)、groupingBy()方法

    public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier) {
        return groupingBy(classifier, toList());
    }

需求:根据学⽣所在的省份,进⾏分组

        List<Student> students = Arrays.asList(new Student("⼴东", 23),
                new Student("⼴东", 24), new Student("⼴东", 23),
                new Student("北京", 22), new Student("北京", 20),
                new Student("北京", 20), new Student("海南", 25));
        Map<String, List<Student>> map = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince()));
        map.forEach((key, value) -> {
            System.out.println(key);
            value.stream().forEach(e -> System.out.println(e.getAge()));
            System.out.println("--------------------");
        });

需求:统计各个省份的⼈数

        List<Student> students = Arrays.asList(new Student("⼴东", 23),
                new Student("⼴东", 24), new Student("⼴东", 23),
                new Student("北京", 22), new Student("北京", 20),
                new Student("北京", 20), new Student("海南", 25));
        Map<String, Long> map = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting()));
        map.forEach((key, value) -> {
            System.out.println(key);
            System.out.println(value);
            System.out.println("--------------------");
        });

5)、summarizing()集合统计

summarizing()可以⼀个⽅法把统计相关的基本上都完成,主要有如下几种:

  • summarizingInt()
  • summarizingDouble()
  • summarizingLong()
        List<Student> students = Arrays.asList(new Student("⼴东", 23),
                new Student("⼴东", 24), new Student("⼴东", 23),
                new Student("北京", 22), new Student("北京", 20),
                new Student("北京", 20), new Student("海南", 25));
        IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));
        System.out.println("平均值:" + summaryStatistics.getAverage());
        System.out.println("⼈数:" + summaryStatistics.getCount());
        System.out.println("最⼤值:" + summaryStatistics.getMax());
        System.out.println("最⼩值:" + summaryStatistics.getMin());
        System.out.println("总和:" + summaryStatistics.getSum());

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/105473203