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());
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章