目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

JDK8~13新特性

接口新特性_JDK8

使用场景
 接口里面定义公用的业务逻辑,抽取出来,每个子类都必须具备;静态方法可以充当工具类

默认方法default
 接⼝⾥⾯定义⼀个默认⽅法,这个接⼝的实现类实现了这个接⼝之后,不⽤管这个 default修饰的⽅法就可以直接调⽤,即接⼝⽅法的默认实现。

Animal_interface

public interface Animal {
    void run();
    void eat();

    default void breath(){
        System.out.println("使用氧气呼吸");
    }
    
    static void test(){
        System.out.println("这个是静态方法");
    }
}

Dog

public class Dog implements Animal {
    @Override
    public void run() {
        System.out.println("dog 跑");
    }

    @Override
    public void eat() {
        System.out.println("dog 吃");
    }
}

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

Main

public class Main {

    public static void main(String[] args) {

        Dog dog = new Dog();
        dog.eat();
        dog.run();
        dog.breath();

        Animal.test();
    }
}

输出

dog 吃
dog 跑
使用氧气呼吸
这个是静态方法

Base64加解密API_JDK8

 Base64是⽹络上最常⻅的⽤于传输8Bit字节码的编码⽅式之⼀,Base64就是 ⼀种基于64个可打印字符来表示⼆进制数据的⽅法 ;基于64个字符A-Z,a-z,0-9,+,/的编码⽅式, 是⼀种能将任意⼆进制数据⽤64种字元组合成字符串的⽅法,⽽这个⼆进制数据和字符串资料之 间是可以互相转换的,在实际应⽤上,Base64除了能将⼆进制数据可视化之外,也常⽤来表示字 串加密过后的内容。

早期java要使⽤Base64怎么做?
 使⽤JDK⾥sun.misc套件下的BASE64Encoder和BASE64Decoder这两个类;编码和解码的效率比较差,公开信息说以后的版本会取消这个方法,如果使用Apache Commons Codes则需要引包。

        BASE64Encoder encoder = new BASE64Encoder();
        BASE64Decoder decoder = new BASE64Decoder();

        String text = "麦富迪";

        byte[] textByte = text.getBytes("UTF-8");

        //编码
        String encodedText = encoder.encode(textByte);
        System.out.println(encodedText);  //5bCP5ru06K++5xCC

        //解码
        System.out.println(new String(decoder.decodeBuffer(encodedText),"UTF-8"));    //麦富迪

Jdk1.8的java.util包中,新增了Base64的类
 好处:不⽤引包,编解码效率远⼤于 sun.misc 和 Apache Commons Codec。

        Base64.Encoder  encoder  =  Base64.getEncoder();
        Base64.Decoder decoder = Base64.getDecoder();
        String text = "麦富迪";
        byte[] textByte = text.getBytes("UTF-8");

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

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

时间日期处理类_JDK8

 时间处理再熟悉不过,SimpleDateFormat,Calendar等类。旧版缺点: java.util.Date 是⾮线程安全 的 API设计⽐较差,⽇期/时间对象⽐较,加减麻烦。
 Java 8通过发布新的Date-Time API (JSR 310)来进⼀步加强对⽇期与时间的处理:

  • 新增了很多常⻅的api,如⽇期/时间的⽐较,加减,格式化等
  • 包所在位置 java.time
  • 核心类
LocalDate:不包含具体时间。 只有日期。
LocalTime:不含⽇期的时间。  只有时间。
LocalDateTime:包含了⽇期及时间。 包含日期和时间。

示例

        LocalDate today = LocalDate.now();

        //LocalTime localTime = LocalTime.now();

        //今天日期 2019-10-22
        System.out.println("今天日期 "+ today);

        //获取年月日,周几

        //现在是哪年:2019
        System.out.println("现在是哪年:" + today.getYear() );

        //现在是哪月:OCTOBER
        System.out.println("现在是哪月:" + today.getMonth() );

        //现在是哪月(数字):10
        System.out.println("现在是哪月(数字):" + today.getMonthValue() );

        //现在是几号:22
        System.out.println("现在是几号:" + today.getDayOfMonth() );

        //现在是周几:TUESDAY
        System.out.println("现在是周几:" + today.getDayOfWeek() );


        //加减年份,  加后返回的对象才是修改的,旧的依然是旧的
        LocalDate changeDate =  today.plusYears(1);
        //加后是哪年:2020
        System.out.println("加后是哪年:"+changeDate.getYear());
        //旧的是哪年:2019
        System.out.println("旧的是哪年:"+today.getYear());

        //日期比较:是否在指定时候之后  isAfter:true
        System.out.println("isAfter:"+changeDate.isAfter(today));

LocalDate常用API

//getYear() int 获取当前⽇期的年份 
//getMonth() Month 获取当前⽇期的⽉份对象 
//getMonthValue() int 获取当前⽇期是第⼏⽉ 
//getDayOfWeek() DayOfWeek 表示该对象表示的⽇期是星期⼏ 
//getDayOfMonth() int 表示该对象表示的⽇期是这个⽉第⼏天 
//getDayOfYear() int 表示该对象表示的⽇期是今年第⼏天 
//withYear(int year) LocalDate 修改当前对象的年份 
//withMonth(int month) LocalDate 修改当前对象的⽉份 
//withDayOfMonth(int dayOfMonth) LocalDate 修改当前对象在当⽉的⽇ 期 
//plusYears(long yearsToAdd) LocalDate 当前对象增加指定的年份数 
//plusMonths(long monthsToAdd) LocalDate 当前对象增加指定的⽉份数 
//plusWeeks(long weeksToAdd) LocalDate 当前对象增加指定的周数 
//plusDays(long daysToAdd) LocalDate 当前对象增加指定的天数 
//minusYears(long yearsToSubtract) LocalDate 当前对象减去指定的年 数 
//minusMonths(long monthsToSubtract) LocalDate 当前对象减去注定的 ⽉数 
//minusWeeks(long weeksToSubtract) LocalDate 当前对象减去指定的周 数 
//minusDays(long daysToSubtract) LocalDate 当前对象减去指定的天数 
//compareTo(ChronoLocalDate other) int ⽐较当前对象和other对象在时 间上的⼤⼩,返回值如果为正,则当前对象时间较晚, //isBefore(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在 other对象⽇期之前 
//isAfter(ChronoLocalDate other) boolean ⽐较当前对象⽇期是否在 other对象⽇期之后 
//isEqual(ChronoLocalDate other) boolean ⽐较两个⽇期对象是否相等

日期格式化
 JDK8之前:SimpleDateFormat来进⾏格式化,但SimpleDateFormat并不是线程安全的。
 JDK8之后:引⼊线程安全的⽇期与时间DateTimeFormatter

示例

        /**
         * 日期格式化:年-月-日 时:分:秒
         */
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        DateTimeFormatter dtf =  DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String ldtStr = dtf.format(ldt);
        System.out.println(ldtStr);

        /**
         * 自定义 日期时间对象
         */
        LocalDateTime ldt = LocalDateTime.of(2020, 11, 11, 8, 20, 30);
        System.out.println(ldt);

        //当前日期时间对象
        LocalDateTime today = LocalDateTime.now();
        System.out.println(today);

        //自定义日期时间对象
        LocalDateTime changeDate = LocalDateTime.of(2020, 10, 28, 12, 32, 30);
        System.out.println(changeDate);

        //比较两个日期时间对象
        Duration duration  = Duration.between(today,changeDate);
        System.out.println("相差的天数:" + duration.toDays()); //两个时间差的天数
        System.out.println("相差的小时数:" + duration.toHours()); //两个时间差的小时数
	System.out.println("相差的秒数:" + duration.toMillis() / 1000); //两个时间差的秒数
        System.out.println("相差的分钟数:" + duration.toMinutes()); //两个时间差的分钟数
        System.out.println("相差的毫秒数:" + duration.toMillis()); //两个时间差的毫秒数
        System.out.println("相差的纳秒数:" + duration.toNanos()); //两个时间差的纳秒数

Optional类_JDK8

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

示例

public class Main {

    public static void main(String[] args) throws Exception {

        /**
         * Optional包装类可以避免空指针:因为Optional类本身可以允许null存在
         * Optional.of()
         * opt.isPresent()
         */
        Student student = new Student();

        //Optional.of() 接收的参数不能为null,如果为null则会抛出异常。
        //Optional<Student> opt = Optional.of(student);

        //Optional.ofNullable() 接收的参数可以为null
        Optional<Student> opt = Optional.ofNullable(student);

        //判断是否为null,不为null才调用get(),不为null opt.isPresent()返回true
        if(opt.isPresent()){
            System.out.println("optional不为空");
            Student s = opt.get();
        }else {
            System.out.println("optional为空");
        }

        /**
         * 兜底数据
         * Optional.ofNullable(student1).orElse(student2);
         */
        //Student student1 = new Student(5);
        Student student1 = null;
        Student student2 = new Student(2);
        //兜底数据:当没有数据的时候会使用兜底数据
        Student result = Optional.ofNullable(student1).orElse(student2);
        System.out.println(result.getAge());


        //Student std = null;
        //Student std = new Student(10);
        int res = Optional.ofNullable(std).map(obj->obj.getAge()).orElse(7);
        System.out.println(res); //7
    }
}

Lambda表达式_JDK8

 在JDK8之前,Java是不⽀持函数式编程的,所谓的函数编程,即可理解是将⼀个函数(也称为“⾏ 为”)作为⼀个参数进⾏传递, ⾯向对象编程是对数据的抽象(各种各样的POJO类),⽽函数式编 程则是对⾏为的抽象(将⾏为作为⼀个参数进⾏传递)
使用场景:Lambda表达式 使⽤场景(前提):⼀个接⼝中只包含⼀个⽅法,则可以使⽤Lambda表达式,这样 的接⼝称之为“函数接⼝”

Lambda表达式的优点

  • Lambda 表达式的实现⽅式在本质是以匿名内部类的⽅式进⾏实现
  • 重构现有臃肿代码,更⾼的开发效率,尤其是集合Collection操作的时候。

语法: (params) -> expression

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

参数列表 
	括号中参数列表的数据类型可以省略不写 
		Collections.sort(lists,( a,  b)-> b.compareTo(a));
	括号中的参数只有⼀个,那么参数类型和()都可以省略不写 
		Collections.sort(lists, b -> System.out.println(b));
		
⽅法体
	如果{}中的代码只有⼀⾏,⽆论有返回值,可以省略{},return,分号,要⼀起省略,其他 则需要加上。
		new Thread(()-> System.out.println("test")).start();
	如果有多行代码,则不能省略{},分号,如果有返回值需要加return
	        new Thread(() -> {
	            System.out.println("1");
	            System.out.println("2");
	        }).start();

示例代码

public class Main {

    public static void main(String[] args) throws Exception {
        /**
         * 创建线程对比
         */
        //匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("test");

            }
        }).start();
        //Lambda表达式
        new Thread(()->System.out.println("test")).start();

        /**
         * 字符串排序:降序 ggg fff ccc aaa
         */
        List<String> list = Arrays.asList("aaa","ggg","fff","ccc");
        //匿名内部类
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });
        for(String string : list){
            System.out.println(string);
        }

        List<String> lists = Arrays.asList("aaa","ggg","fff","ccc");
        //Lambda表达式
        Collections.sort(lists,( a,  b)-> b.compareTo(a));
        for(String string : lists){
            System.out.println(string);
        }
    }
}

自定义函数式编程实战_JDK8

定义lambda接口流程

  • 定义一个函数式接口:需要标注此接口 @FunctionalInterface,否则万一团队成员在接口上加了其他方法则容易出故障。
  • 编写一个方法,输入需要操作的数据和接口
  • 在调用方法时传入数据和Lambda表达式,用来操作数据。

示例:定义一个可以使用加减乘除的接口
定义函数式接口

@FunctionalInterface
public interface OperFunction<R,T> {
    R operator(T t1, T t2);
}

使用Lambda方式传入函数的行为

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(operator(20, 5, (Integer x, Integer y) -> {
            return x * y;
        }));

        System.out.println(operator(20, 5, (x, y) -> x + y));
        System.out.println(operator(20, 5, (x, y) -> x - y));
        System.out.println(operator(20, 5, (x, y) -> x / y));

    }

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

函数式编程Function _JDK8

 Lambda表达式必须先定义接⼝,创建相关⽅法之后才可使⽤,这样做⼗分不便,其实java8已经 内置了许多接⼝, 例如下⾯四个功能型接⼝,所以⼀般很少会由⽤户去定义新的函数式接⼝。

Java8 内置的四⼤核⼼函数式接⼝ 

Consumer<T>: 消费型接⼝:有⼊参,⽆返回值 
	void accept(T t); 

Supplier<T>: 供给型接⼝:⽆⼊参,有返回值 
	T get(); 

Function<T, R>:: 函数型接⼝:有⼊参,有返回值 
	R apply(T t); 

Predicate<T>: 断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean
	boolean test(T t);

Function

主要作用
	传入一个值经过函数的计算返回另一个值  

函数型接⼝:有⼊参,有返回值 
	T:入参类型  	R: 返回类型  

调用方法
	 R apply(T t)

Function接口源码

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

}

示例

//        Function<Integer,Integer> func = p->{
//         System.out.println("我是函数");
//         return  p*100;
//        };
        
        Function<Integer,Integer> func = p->p*100;
        System.out.println(func.apply(10));

BiFunction_JDK8

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

public class Main {

    public static void main(String[] args) throws Exception {

        /**
         * 传递函数行为
         */
        System.out.println(operator(10,21,(a,b)->a+b));
        System.out.println(operator(10,2,(a,b)->a-b));
        System.out.println(operator(8,4,(a,b)->a*b));
        System.out.println(operator(10,2,(a,b)->a/b));

        /**
         * 常规使用
         */
        BiFunction<Integer,Integer,Integer> func = (a,b)-> a+b;
        System.out.println(func.apply(1,2));

    }

    /**
     * 自定义行为
     * @param a
     * @param b
     * @param bf
     * @return
     */
    public static Integer operator(Integer a, Integer b, BiFunction<Integer,Integer,Integer> bf){

        return bf.apply(a,b);
    }
}

Consumer_JDK8

主要作用
	因为没有返回值,常用于打印、发短信等消费动作

消费型接⼝:有⼊参,无返回值 
	T:入参类型  	R: 返回类型  

调用方法
	 void accept(T t);

接口源码

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}

示例java

public class Main {

    public static void main(String[] args) throws Exception {

        Consumer<String> consumer = obj->{
            System.out.println(obj);
            System.out.println("调用短信接口发送短信,或者打印日志");
        };

        /**
         * 简写
         */
//        sendMsg("8888888",obj->{
//            System.out.println(obj);
//            System.out.println("调用短信接口发送短信,或者打印日志");
//        });

        sendMsg("8888888",consumer);

    }

    public static void sendMsg(String phone,Consumer<String> consumer){
        consumer.accept(phone);
    }
}

典型应用ForEach

        List<String> list = Arrays.asList("aaa","bbb");
	//只有入参,没有返回值
        list.forEach(obj->{
            System.out.println(obj);
        });

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

Supplier_JDK8

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

供给型接口:无⼊参,无返回值 
	T:返回值类型  	

调用方法
	 T get();

接口源码

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

示例

public class Main {

    public static void main(String[] args) throws Exception {

        //Student student = new Student();

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

class Student{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Predicate_JDK8

主要作用
	接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据.

断言型接口:有⼊参,有返回值 
	T:入参类型
	Boolean:返回值类型

调用方法
	 boolean test(T t);

接口源码

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

示例

public class Main {

    public static void main(String[] args) throws Exception {

        List<String> list = Arrays.asList("asdfdsfdsfds","xcvsdfsdf","sdfsdf","awwa","ad4432","dsfsdf");

        //filter是过滤器:过滤器可以接收一个
        //添加过滤条件: obj->obj.startsWith("a")
        List<String> results = filter(list,obj->obj.startsWith("a"));

        System.out.println(list);
        System.out.println(results);
    }

    public static List<String> filter(List<String> list, Predicate<String> predicate){
        List<String> results = new ArrayList<>();
        for(String str:list){
            //使用predicate,对每个数据进行过滤: Predicate接口中的test()方法
            if(predicate.test(str)){
                results.add(str);
            }
        }
        return results;
    }
}

方法与构造函数引用_JDK8

 ⽅法引⽤是⼀种更简洁易懂的lambda表达式,操作符是双冒号::,⽤来直接访问类或者实例已经存在的方法或构造⽅法。
 通过⽅法引⽤,可以将⽅法的引⽤赋值给⼀个变量。

语法:

 左边是容器(可以是类名,实例名)   
 中间是" :: "  
 右边是相应的⽅法名

静态⽅法
	则是ClassName::methodName。如 Object ::equals 
实例⽅法
	则是Instance::methodName 
构造函数
	则是 类名::new;

单个参数
	Function<⼊参1, 返回类型> func = ⽅法引⽤ 
	应⽤ func.apply(⼊参);

2个参数
	BiFunction<⼊参1,⼊参2, 返回类型> func = ⽅法引⽤ 
	应⽤ func.apply(⼊参1,⼊参2

示例

import java.util.function.BiFunction;
import java.util.function.Function;

public class Main {

    public static void main(String[] args) throws Exception {

        /**
         * 使用双冒号:: 来构造静态函数的引用
         */
        //Integer.parseInt("1024");
        Function<String, Integer> fun = Integer::parseInt;
        Integer value = fun.apply("1024");
        System.out.println(value);

        /**
         * 使用双冒号::来构造非静态函数引用
         */
        String content = "一二三四";
        Function<Integer, String> func = content::substring;
        String result = func.apply(1);
        System.out.println(result); //二三四

        /**
         * 构造函数引用,单个参数 Function<T,R>
         */
        Function<String,User> function = User::new;
        User user2 = function.apply("顶顶");
        System.out.println(user2);

        /**
         * 构造函数引用,多个参数  BiFunction<T,T,R>
         */
        BiFunction<String,Integer,User> biFunction = User::new;
        User user1 = biFunction.apply("秃秃",32);
        System.out.println(user1);

        /**
         * 函数引用:可以将函数引用作为方法的参数: String::toUpperCase 作为sayHello() 的参数
         * 小写转大写
         * String s = "abc".toUpperCase();
         */
        sayHello(String::toUpperCase,"abc1024.pub");
    }


    private static void  sayHello(Function<String,String> func,String param){
        String result = func.apply(param);
        System.out.println(result);
    }
    
}

class User {
    private String name;
    private int age;

    public User() {
    }

    public User( String name,int age) {
        this.age = age;
        this.name = name;
    }

    public User(String name) {
        this.name = name;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

流Stream_JDK8

 Stream 中⽂称为 “流”,通过将集合转换为这么⼀种叫做 “流”的元素队列,通过声明性⽅式, 能够对集合中的每个元素进⾏⼀系列并⾏或串⾏的流⽔线操作。
 元素是特定类型的对象,所以元素集合看作⼀种流, 流在管道中传输, 且可以在管道的节点 上进⾏处理, ⽐如 排序,聚合,过滤等操作。

操作详情

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

示例

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws Exception {

        List<String> list = Arrays.asList("springboot","微服务","并发编程","redis","消息队列");
        
        List<String> resultList = list.stream().map(obj->"学习"+obj).collect(Collectors.toList());

        System.out.println(resultList); //[学习springboot, 学习微服务, 学习并发编程, 学习redis, 学习消息队列]
    }
}

map函数
 场景:转换对象,如javaweb开发中集合⾥⾯的DO对象转换为DTO对。

DO

public class User {

    private int id;

    private String name;

    private String pwd;

    public User(){

    }

    public  User(int id, String name,String pwd){
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

DTO

public class UserDTO {

    private int userId;

    private String username;


    public UserDTO(){}

    public UserDTO(int userId, String username){
        this.userId = userId;
        this.username = username;
    }


    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "UserDTO{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                '}';
    }
}

示例

public class Main {

    public static void main(String[] args) throws Exception {
        /**
         * TO to TDO
         */
        List<User> list = Arrays.asList(new User(1, "小东", "123"),
                new User(21, "jack", "rawer"),
                new User(155, "tom", "sadfsdfsdfsd"),
                new User(231, "marry", "234324"),
                new User(100, "小D", "122223")
        );
        List<UserDTO> userDTOList = list.stream().map(obj -> {
            UserDTO userDTO = new UserDTO(obj.getId(), obj.getName());
            return userDTO;
        }).collect(Collectors.toList());
        System.out.println(userDTOList);    //[UserDTO{userId=1, username='小东'}, UserDTO{userId=21, username='jack'}, UserDTO{userId=155, username='tom'}, UserDTO{userId=231, username='marry'}, UserDTO{userId=100, username='小D'}]

    }
}

filter函数
 ⽤于通过设置的条件过滤出元素.
 需求:过滤出字符串⻓度⼤于5的字符串

        /**
         * filter过滤
         */
        List<String> list2 = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
        List<String> resultList = list2.stream().filter(obj -> obj.length() > 5).collect(Collectors.toList());
        System.out.println(resultList); //[springboot, springcloud, docker]

sorted与limit函数
 对流进⾏⾃然排序, 其中的元素必须实现Comparable 接⼝。

public class Main {

    public static void main(String[] args) throws Exception {

        /**
         * 对元素进行排序,排序的元素必须实现 Comparable 接⼝
         */
        List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");
        List<String> resultList1 = list.stream().sorted().collect(Collectors.toList());
        System.out.println(resultList1);


        //根据长度排序:升序
        //List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());

        //根据长度排序:降序
        //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);

        //limit截取: 先降序,再截取前三个
        List<String> resultList3 = list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());

        System.out.println(resultList3);
    }
}

allMatch和anyMatch函数

public class Main {

    public static void main(String[] args) throws Exception {

        List<String> list = Arrays.asList("springboot", "springcloud", "redis", "git", "netty", "java", "html", "docker");

        //全部符合
        boolean flag1 = list.stream().allMatch(obj->obj.length()>5);
        System.out.println(flag1);

        //部分符合
        boolean flag2 = list.stream().anyMatch(obj->obj.length()>5);
        System.out.println(flag2);
    }
}

max和min函数

public class Main {

    public static void main(String[] args) throws Exception {
        List<Student> list = Arrays.asList(
                new Student(32),
                new Student(33),
                new Student(21),
                new Student(29),
                new Student(18)
        );

        /**
         * 寻找年龄最大的学生
         */

        //list.stream().max(Comparator.comparingInt(Student::getAge));

//        Optional<Student> optionalStudent =  list.stream().max((s1, s2)->{
//            return Integer.compare(s1.getAge(),s2.getAge());
//        });

        /**
         * 寻找年龄最小的学生
         */
        Optional<Student> optionalStudent = list.stream().min((s1, s2) -> {
            return Integer.compare(s1.getAge(), s2.getAge());
        });

        Student student = optionalStudent.get();
        System.out.println(student.getAge());

    }
}


class Student {
    private int age;

    public Student() {
    }

    public Student(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

并行流parallelStream_JDK8

 集合做重复的操作,如果使⽤串⾏执⾏会相当耗时,因此⼀般会采⽤多线程来加快, Java8的 paralleStream⽤fork/join框架提供了并发执⾏能⼒。
底层原理

  • 线程池(ForkJoinPool)维护⼀个线程队列
  • 可以分割任务,将⽗任务拆分成⼦任务,完全贴合分治思想

示例

public class Main {

    public static void main(String[] args) throws Exception {
        /**
         * 并行无法保证执行的有序性,执行过程是乱序随机的
         * 适合对顺序无要求的场景
         */
        List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9);
        numbers.stream().forEach(System.out::println);//有序
        numbers.parallelStream().forEach(System.out::println);//乱序

        /**
         * 操作并行集合需要使用线程安全的集合:CopyOnWriteArrayList
         */
        for(int i=0;i<10;i++){
            //List list = new ArrayList();
            List list = new CopyOnWriteArrayList();//线程安全,每次add之前加锁
            IntStream.range(0,100).parallel().forEach(list::add);
            System.out.println(list.size());
        }
    }
}

paralleStream并⾏是否⼀定⽐Stream串⾏快?
 数据量少的情况,可能串⾏更快,ForkJoin会耗性能。

多数情况下并⾏⽐串⾏快,是否可以都⽤并⾏
 不⾏,部分情况会有线程安全问题,parallelStream⾥⾯使⽤的外部变量,⽐如集合⼀ 定要使⽤线程安全集合,不然就会引发多线程安全问题。

reduce_JDK8

 聚合操作,中⽂意思是 “减少”;根据⼀定的规则将Stream中的元素进⾏计算后返回⼀个唯⼀的值。
接口源码

//accumulator 计算的累加器
Optional<T> reduce(BinaryOperator<T> accumulator);

示例

        //lambda
        int value1 = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();
        System.out.println(value1);

        //匿名内部类
        int value2 = Stream.of(1, 2, 3, 4, 5).reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        }).get();
        System.out.println(value2);

接口源码

T reduce(T identity, BinaryOperator<T> accumulator);

示例

        /**
         * 例⼦: 100作为初始值,然后和第⼀个元素相加,结果在和第⼆个元素相加,直到全部相加完成
         * identity ⽤户提供⼀个循环计算的初始值
         * accumulator 计算的累加器
         */
        int value = Stream.of(1, 2, 3, 4, 5).reduce(100,(item1,item2)->item1+item2);
        System.out.println(value);

练习:求最⼤值

        int value3 = Stream.of(1645, 234345, 32, 44434, 564534, 435, 34343542, 212)
                .reduce((item1, item2) -> item1 > item2 ? item1 : item2).get();
        System.out.println(value);

forech_JDK8

public class Main {

    public static void main(String[] args) throws Exception {
        /**
         * forEach使用
         */
        List<String> list = Arrays.asList("sdfsdf", "xxxx", "bbb");
        list.forEach(obj -> System.out.println(obj));
        list.forEach(System.out::println);

        List<Student> studentList = Arrays.asList(
                new Student(2),
                new Student(21),
                new Student(32),
                new Student(1)
        );

        int age = 0;

        /**
         * forEach注意点
         * 不能修改包含外部的变量的值:  这里age
         * 不能⽤break或者return或者continue等关键词结束或者跳过循环
         */
        studentList.forEach(student -> {
            System.out.println(student.getAge());
            if (student.getAge() == 21) {
                System.out.println("进入if");
                return;
                //break;
            }
        });
    }
}

class Student {

    private int age;

    public Student(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

收集器_collector_JDK8

collect()⽅法的作⽤

  • ⼀个终端操作, ⽤于对流中的数据进⾏归集操作,collect⽅法接受的参数是⼀个Collector。
  • 有两个重载⽅法,在Stream接⼝⾥⾯。
//重载方法一
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);

//重载方法二	推荐(第一种方法需要自定义,第二个方法提供了很多收集器)
<R, A> R collect(Collector<? super T, A, R> collector);

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

  • Collectors.toMap()
  • Collectors.toSet()
  • Collectors.toCollection() :
Collectors.toCollection(LinkedList::new) 
Collectors.toCollection(CopyOnWriteArrayList::new) 
Collectors.toCollection(TreeSet::new)

Collectors.toList()源码

/**
 * ArrayList::new,创建⼀个ArrayList作为累加器
 * List::add,对流中元素的操作就是直接添加到累加器中
 * reduce操作, 对⼦任务归集结果addAll,后⼀个⼦任务的结果直接全部添加到 前⼀个⼦任务结果中
*/ CH_ID 是⼀个unmodifiableSet集合
    public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }

示例

public class Main {

    public static void main(String[] args) throws Exception {
         List<String> list = Arrays.asList("sdfsdf","xxxx","bbb","bbb");

        //List<String> results = list.stream().collect(Collectors.toList());
        //list.stream().collect(Collectors.toSet());

        //List<String> result = list.stream().collect(Collectors.toCollection(LinkedList::new));
        List<String> result = list.stream().collect(Collectors.toCollection(CopyOnWriteArrayList::new));
        Set<String> stringSet = list.stream().collect(Collectors.toCollection(TreeSet::new));
        System.out.println(result);
        System.out.println(stringSet);

    }
}

字符串拼接_joing函数_JDK8

三种重载方法

Collectors.joining() 
Collectors.joining("param") 
Collectors.joining("param1", "param2", "param3")

Collectors.joining()源码

    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
        return joining(delimiter, "", "");
    }

    public static Collector<CharSequence, ?, String> joining() {
        return new CollectorImpl<CharSequence, StringBuilder, String>(
                StringBuilder::new, StringBuilder::append,
                (r1, r2) -> { r1.append(r2); return r1; },
                StringBuilder::toString, CH_NOID);
    }

    public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix) {
        return new CollectorImpl<>(
                () -> new StringJoiner(delimiter, prefix, suffix),
                StringJoiner::add, StringJoiner::merge,
                StringJoiner::toString, CH_NOID);
    }

示例

public class Main {
    public static void main(String[] args) throws Exception {
         List<String> list = Arrays.asList("springboot","springcloud","java","dubbo");

         String result1 = list.stream().collect(Collectors.joining());
         System.out.println(result1);

        String result2 = list.stream().collect(Collectors.joining("||"));
        System.out.println(result2);

        String result3 = list.stream().collect(Collectors.joining("||","[","]"));
        System.out.println(result3);
        
	//该⽅法可以将Stream得到⼀个字符串, joining函数接受三个参数,分别表示 元素之间的连 接符、前缀和后缀。
        String result = Stream.of("springboot","mysql","html5","css3").collect(Collectors.joining(",","[","]"));
        System.out.println(result);
    }
}

收集器_partitioningBy_分组_JDK8

partitioningBy源码

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

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

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static java.util.stream.Collectors.*;

public class Main {

    public static void main(String[] args) throws Exception {
        List<String> list = Arrays.asList("java", "springboot", "HTML5", "CSS3");
        Map<Boolean, List<String>> result = list.stream().collect(partitioningBy(obj -> obj.length() > 4));
        System.out.println(result); // {false=[java, CSS3], true=[springboot, HTML5]}
    }
}

收集器_groupby_进阶_JDK8

源码

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

示例

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) throws Exception {
        //数据
        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>> listMap =  students.stream().collect(Collectors.groupingBy(obj->obj.getProvince()));
        listMap.forEach((key,value)->{
            System.out.println(key+"省:");
            value.forEach(obj->{
                System.out.println(obj.getAge());
            });
        });
    }
}

class Student {
    private int age;
    private String province;

    public Student(String province, int age) {
        this.province = province;
        this.age = age;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }
}

Collectors.counting统计元素个数_JDK8

源码

    public static <T, K, A, D>
    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream) {
        return groupingBy(classifier, HashMap::new, downstream);
    }

示例:统计各个省份的⼈数

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws Exception {

        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> listMap =  students.stream().collect(Collectors.groupingBy(obj->obj.getProvince(),Collectors.counting()));

     listMap.forEach((key,value)->{
         System.out.println(key+"人数有:"+value);
     });
    }
}

class Student {
    private int age;
    private String province;

    public Student(String province, int age) {
        this.province = province;
        this.age = age;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }
}

summarizing集合统计_JDK8

 作⽤:可以⼀个⽅法把统计相关的基本上都完成
源码

    public static <T>
    Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper) {
        return new CollectorImpl<T, IntSummaryStatistics, IntSummaryStatistics>(
                IntSummaryStatistics::new,
                (r, t) -> r.accept(mapper.applyAsInt(t)),
                (l, r) -> { l.combine(r); return l; }, CH_ID);
    }

示例:统计学⽣的各个年龄信息

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws Exception {
        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());//平均值22.428571428571427
        System.out.println("人数"+summaryStatistics.getCount());//人数7
        System.out.println("最大值"+summaryStatistics.getMax());//最大值25
        System.out.println("最小值"+summaryStatistics.getMin());//最小值20
        System.out.println("总和"+summaryStatistics.getSum());//总和157
    }
}

class Student {
    private int age;
    private String province;

    public Student(String province, int age) {
        this.province = province;
        this.age = age;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
}

Collection、Lambda练习

准备数据
POJO

package chapter8;


public class VideoOrder {

    private String tradeNo;

    private int money;

    private String title;

    public VideoOrder(String tradeNo, String title, int money) {
        this.tradeNo = tradeNo;
        this.title = title;
        this.money = money;
    }

    public String getTradeNo() {
        return tradeNo;
    }

    public void setTradeNo(String tradeNo) {
        this.tradeNo = tradeNo;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof  VideoOrder){
            VideoOrder o1 =  (VideoOrder)obj;
            return title.equals(o1.title);
        }
        return super.equals(obj);
    }


    @Override
    public int hashCode() {
        return title.hashCode();
    }


    @Override
    public String toString() {
        return "VideoOrder{" +
                "title='" + title + '\'' +
                '}';
    }
}

示例

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) throws Exception {
        /**
         * 原始订单数据
         */
        //订单1:总价 35
        List<VideoOrder> videoOrders1 = Arrays.asList(
                new VideoOrder("20190242812", "springboot", 3),
                new VideoOrder("20194350812", "springCloud", 5),
                new VideoOrder("20190814232", "Redis", 9),
                new VideoOrder("20190523812", "html", 9),
                new VideoOrder("201932324", "Netty", 9));
        //订单2:总价 54
        List<VideoOrder> videoOrders2 = Arrays.asList(
                new VideoOrder("2019024285312", "springboot", 3),
                new VideoOrder("2019081453232", "Redis", 9),
                new VideoOrder("20190522338312", "html", 9),
                new VideoOrder("2019435230812", "Jmeter", 5),
                new VideoOrder("2019323542411", "Git+Jenkins", 7),
                new VideoOrder("2019323542424", "Idea", 21));

        /**
         * 需求一:统计出同时被两个⼈购买的商品列表(交集)
         * 需要重写List中的 equals() & hashCode()
         */
        List<VideoOrder> intersectionList = videoOrders1.stream().filter(videoOrders2::contains).collect(Collectors.toList());
        System.out.println(intersectionList);//[VideoOrder{title='springboot'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}]

        /**
         * 需求二:统计出两个⼈购买商品的差集
         * ! 取反
         */
        //方式二
        List<VideoOrder> diffList1 =  videoOrders1.stream().filter(obj->!videoOrders2.contains(obj)).collect(Collectors.toList());
        System.out.println("差集1="+diffList1);//差集1=[VideoOrder{title='springCloud'}, VideoOrder{title='Netty'}]
        //方式一
        List<VideoOrder> diffList2 =  videoOrders2.stream().filter(obj->!videoOrders1.contains(obj)).collect(Collectors.toList());
        System.out.println("差集2="+diffList2);//差集2=[VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]


        /**
         * 需求三:统计出全部被购买商品的去重并集
         * ! 取反
         */
        List<VideoOrder> allVideoOrder = videoOrders1.parallelStream().collect(Collectors.toList());//这里使用了并行流,因为对执行顺序没有特殊的要求
        allVideoOrder.addAll(videoOrders2);
        System.out.println("并集="+allVideoOrder);//并集=[VideoOrder{title='springboot'}, VideoOrder{title='springCloud'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Netty'}, VideoOrder{title='springboot'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]

        List<VideoOrder> allVideoOrderDistinct = allVideoOrder.parallelStream().distinct().collect(Collectors.toList());
        System.out.println("去重并集="+allVideoOrderDistinct);//去重并集=[VideoOrder{title='springboot'}, VideoOrder{title='springCloud'}, VideoOrder{title='Redis'}, VideoOrder{title='html'}, VideoOrder{title='Netty'}, VideoOrder{title='Jmeter'}, VideoOrder{title='Git+Jenkins'}, VideoOrder{title='Idea'}]

        /**
         * 需求四:统计两个⼈的分别购买订单的平均价格
         */
        Double videoOrder1Price = videoOrders1.stream().collect(Collectors.averagingDouble(VideoOrder::getMoney));
        System.out.println("订单一的平均价格:" + videoOrder1Price);//订单一的平均价格:7.0
        Double videoOrder2Price = videoOrders2.stream().collect(Collectors.averagingDouble(VideoOrder::getMoney));
        System.out.println("订单二的平均价格:" + videoOrder2Price);//订单二的平均价格:9.0

        /**
         * 需求五:统计两个⼈的分别购买订单的总价格
         */
        IntSummaryStatistics summaryStatisticsOrder1 = videoOrders1.stream().collect(Collectors.summarizingInt(VideoOrder::getMoney));
        System.out.println("订单1的总价:" + summaryStatisticsOrder1.getSum());//订单1的总价:35

        IntSummaryStatistics summaryStatisticsOrder2 = videoOrders2.stream().collect(Collectors.summarizingInt(VideoOrder::getMoney));
        System.out.println("订单1的总价:" + summaryStatisticsOrder2.getSum());//订单1的总价:54
    }
}

新增内存空间_MetaSpace_JDK8

 JVM 种类有很多,⽐如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM,Hotspot才有MetaSpace,JRockit以及J9是没有这个区域。

  • 作⽤:该块内存主要是被JVM⽤来存放 class 和 mate 信息的,当 class 被加载 loader 的时候就会 被存储到该内存区中,如⽅法的编译信息及字节码、常量池和符号解析、类的层级信息,字段,名 字等。
JVM内存知识 在JDK8之前的HotSpot JVM,有个区域叫做“永久代(permanent generation), 
	通过 在命令⾏设置参数-XX:MaxPermSize来设定永久代最⼤可分配的内存空间 
	如果JDK8⾥⾯设置了PermSize 和 MaxPermSize 会被忽略并给出警告

java.lang.OutOfMemoryError: PermGen space 
	原因是: 永久代空间不够,类太多导致

修改JDK8 HotSpot JVM 
	使⽤本地内存来存储类元数据信息,叫做 元空间(Metaspace) 在默认情况下Metaspace的⼤⼩只与本地内存⼤⼩有关
	常⽤的两个参数
		-XX:MetaspaceSize=N //指Metaspace扩容时触发FullGC的初始化阈值 
		-XX:MaxMetaspaceSize=N //指⽤于限制Metaspace增⻓的上限,防⽌因为某些情况导致 Metaspace⽆限的使⽤本地内存 
	不管两个参数如何设置,都会从20.8M开始,然后随着类加载越来越多不断扩容调整直到最⼤

查看大小: MC: current metaspace capacity MU: mateaspace utilization 单位是KB
C:\Users\soulboy>jstat -gc  14772	
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
4352.0 4352.0 451.0   0.0   34944.0  16996.8   391728.0   345809.7  379220.0 364943.1 49576.0 44274.5   1408    4.032   0      0.000    4.032

try-with-resources_JDK7

 在try( ...)⾥声 明的资源,会在try-catch代码块结束后⾃动关闭掉

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

    public static void main(String[] args) throws Exception {
        String path = "D:\\a.txt";
        test(path);
    }

    /**
     * try(资源初始化过程){业务逻辑}catch(Exception e){}
     * OutputStream implements Closeable
     * @param filepath
     */
    private static void test(String filepath) {
        try(OutputStream out = new FileOutputStream(filepath);) {
            out.write((filepath+"  你好!世界!").getBytes());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // TO DO 
        }
    }

    /**
     * try{} catch{}  过于繁琐
     * @param filepath
     * @throws FileNotFoundException
     */
    private static void testOld(String filepath) throws FileNotFoundException {
        FileOutputStream out = new FileOutputStream(filepath);
        try {
            out.write((filepath+"  你好!世界!").getBytes());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                out.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

新增测试工具Jshell_JDK9

 从java9开始,jdk引⼊了交互式 REPL(Read-Eval-Print-Loop,读取-求值-输出-循环)
 Java Shell工具(JShell)是用于学习Java编程语言和Java代码原型的交互式工具。JShell是一个Read-Evaluate-Print Loop(REPL),它在输入声明,语句和表达式时对它们进行评估,并立即显示结果。该工具从命令行运行。
Jshell官方文档
常用命令

帮助命令
	/help
	/help intro
列出输入的源(每个操作都是一个源)
	/list
编辑某个源
	/edit [源id]
删除
	/drop [源id]
退出jshell命令
	/exit
重置
	/reset
查看历史编辑
	/history
⾃动化补⻬功能
	Tab键

接口的私有方法_JDK9

 JDK8新增了静态⽅法和默认⽅法,但是不⽀持私有方法
 jdk9中新增了私有⽅法

  • 接⼝中的静态⽅法不能被实现类继承和⼦接⼝继承,但是接⼝中的⾮静态的默认⽅法可以被 实现类继承
例如List.of() ⽅法,ArrayList虽然继承了List,但是不能⽤ArrayList.of()⽅法
  • 类的静态⽅法才可以被继承
    示例
    接口
public interface OrderPay {

    void pay();

    default void defaultPay(){
        privateMethod();
    }

    //接口的私有方法可以在Jdk9中使用
    private void privateMethod(){
        System.out.println("调用接口的私有方法");
    }
}
实现类
public class OrderPayImpl implements OrderPay {
    @Override
    public void pay() {
        System.out.println("我实现了接口");
    }
}
测试
public class Main {
    public static void main(String[] args)  {
        OrderPay orderPay = new OrderPayImpl();
        orderPay.defaultPay();  //调用接口的私有方法
        orderPay.pay(); //我实现了接口
    }
}

增强try-with-resource_JDK9

在JDK7中try-with-resource
 在JDK7中,新增了try-with-resources语句,可以在try后的括号中初始化资源,可以实现资 源⾃动关闭。

在JDK9中的增强try-with-resource

  • 在JDK9中,改进了try-with-resources语句,在try外进⾏初始化,在括号内引⽤,即可实现 资源⾃动关闭,多个变量则⽤分号进⾏分割
  • 不需要声明资源 out 就可以使⽤它,并得到相同的结果
public class Main {

    public static void main(String[] args)throws Exception  {
        test("");
    }
    
    private static void test(String filepath) throws Exception {
        OutputStream out = new FileOutputStream(filepath);
	OutputStream out2 = new FileOutputStream(filepath);
        try(out;out2) {
            out.write((filepath+"可以学习java架构课程").getBytes());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

只读集合_JDK9

        List<String> list = List.of("SpringBoot","dubbo","SpringCloud");
        System.out.println(list);

        Set<String> set = Set.of("Mysql","Linux","Git");
        System.out.println(set);

        Map<String, String> map = Map.of("key1","课程1","key2","课程2");
        System.out.println(map);

新增Stream API_JDK9

takeWhile
 有序的集合:从 Stream 中获取⼀部分数据, 返回从头开始的尽可能多的元素, 直到遇到第⼀ 个false结果,如果第⼀个值不满⾜断⾔条件,将返回⼀个空的 Stream.

        /**
         * takeWhile
         * 截取一直匹配的对象,直到不匹配终止
         */
        List<String> list2 = List.of("SpringBoot", "java", "html", "", "git").
                stream().takeWhile(obj -> !obj.isEmpty()).collect(Collectors.toList());
        System.out.println(list2);  //[SpringBoot, java, html]

dropWhile
 与 takeWhile相反,返回剩余的元素,和takeWhile⽅法形成互补.

        /**
         * dropWhile
         * 从第一个不匹配对象开始截取到结束
         */
        List<String> list = List.of("SpringBoot", "java", "html", "", "git").
                stream().dropWhile(obj -> !obj.isEmpty()).collect(Collectors.toList());
        System.out.println(list);

局部变量类型推断var_JDK10

  • 仅适⽤于局部变量,如 增强for循环的索引,传统for循环局部变量
  • 不能使⽤于⽅法形参、构造函数形参、⽅法返回类型或任何其他类型的变量声明
  • 标识符var不是关键字,⽽是⼀个保留类型名称,⽽且不⽀持类或接⼝叫var,也不符合命 名规范
public class Main {
    public static void main(String[] args) throws Exception {

        //根据推断为字符串
        var strVar = "springboot";
        System.out.println(strVar instanceof String);

        //根据10L 推断long类型
        var longVar = 10L;
        
        //根据true推断 boolean类型
        //var flag = true;
        var flag = Boolean.valueOf("true");
        System.out.println(flag instanceof Boolean);


        //推断 ArrayList<String>
        var listVar = new ArrayList<String>();
        System.out.println(listVar instanceof ArrayList);

        var streamVar = Stream.of("aa","bb","cc");
        System.out.println(streamVar instanceof  Stream);


        if(flag){
            System.out.println("这个是 flag 变量,值为true");
        }

        for(var i = 0; i<10;i++){
            System.out.println(i);
        }

        try(var input = new FileInputStream("")){

        }
    }
}

HttpClient客户端_JDK11

 这个功能在JDK 9中引⼊并在JDK 10中得到了更新
 最终JDK11正式发布,⽀持 HTT/1.1, HTTP/2,(JDK10相关课程⾥⾯未讲解该知识点)
官网

HttpClient.Builder
	HttpClient 构建⼯具类
HttpRequest.Builder
	HttpRequest 构建⼯具类
HttpRequest.BodyPublisher
	将java 对象转换为可发送的HTTP request body字节流, 如form表单提
HttpResponse.BodyHandler
	处理接收到的 Response Body

HttpClient提交Post和异步请求

    private static final void testPost() {


        //设置建立连接的超时 connect timeout
        var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000)).build();

        var request = HttpRequest.newBuilder().uri(uri)

                //json格式则下面数据
                //.header("Content-Type","application/json")
                //.POST(HttpRequest.BodyPublishers.ofString("{\"phone\":\"13113777338\",\"pwd\":\"1234567890\"}"));

                //form表单则使用下面的配置
                .header("Content-Type","application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString("phone=13113777338&pwd=1234567890")).build();

        try {
            var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            System.out.println(response.body());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

HttpClient提交Post和异步请求

    private static final void testAsyncGet() {

        //var httpClient = HttpClient.newHttpClient();
        var httpClient = HttpClient.newBuilder().build();

        var request = HttpRequest.newBuilder().timeout(Duration.ofMillis(3000))
                .header("key1", "v1")
                .header("key2", "v2")
                .uri(uri).build();


        try {
//            var response = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
//                    .thenApply(HttpResponse::body);

            CompletableFuture response = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                    .thenApply(HttpResponse::body);

            System.out.println(response.get());

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private static final void testPost() {

        //设置建立连接的超时 connect timeout
        var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000)).build();

        var request = HttpRequest.newBuilder().uri(uri)

                //json格式则下面数据
                //.header("Content-Type","application/json")
                //.POST(HttpRequest.BodyPublishers.ofString("{\"phone\":\"13113777338\",\"pwd\":\"1234567890\"}"));

                //form表单则使用下面的配置
                .header("Content-Type","application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString("phone=13113777338&pwd=1234567890")).build();
        try {
            var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            System.out.println(response.body());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

HttpClient提交Http2请求_ JDK11

  • HTTP2协议的强制要求https,如果⽬标URI是HTTP的,则⽆法使⽤HTTP 2协议
  • 如何判断⽹站是否是http2协议,浏览器,network⾯板,选择protocol
public class Main {
    public static void main(String[] args) throws Exception {
        //testGet();
        //testPost();
        //testAsyncGet();
        testHttp2();
    }
    
    private static final String targetUrl = "https://http2.akamai.com/demo";
    private static final URI uri = URI.create(targetUrl);
    
    private static final void testHttp2() {
        //var httpClient = HttpClient.newHttpClient();
        //设置建立连接的超时 connect timeout
        var httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(5000))
                .version(HttpClient.Version.HTTP_2)
                .build();

        var request = HttpRequest.newBuilder().timeout(Duration.ofMillis(3000))
                .header("key1", "v1")
                .header("key2", "v2")
                .uri(uri).build();

        try {
            var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            System.out.println(response.body());
            System.out.println(response.version());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

javac和java命令优化_JDK11

JDK11之前

javac xxx.java
java xxx

JDK11后运⾏java程序(本地不会⽣成class⽂件

java xxx.java

JDK各个版本

OpenJDK和OracleJDK版本区别

  • OpenJDK是JDK的开放源码版本,以GPL协议的形式发布(General Public License)
  • Oracle JDK采⽤了商业实现

LTS

  • Long Term Support ⻓期⽀持的版本,如JDK8、JDK11都是属于LTS
  • JDK9 和 JDK10 这两个被称为“功能性的版本”不同, 两者均只提供半年的技术⽀持
    1. 甲⻣⽂释出Java的政策,每6个⽉会有⼀个版本的释出,⻓期⽀持版本每三年发布⼀次,根据 后续的发布计划,下⼀个⻓期⽀持版 Java 17 将于2021年发布

8u20、11u20

  • 就是Java的补丁,⽐如JDK8的 8u20版本、8u60版本; java11的 11u20、11u40版本

JDK要收费了?

  • Oracle 宣布 Java8 在 2019 年 1⽉之后停⽌更新,另外 Java11 及以后版本将不再提供免 费的 long-term support (LTS) ⽀持,猜测未来将有越来越多 Java 开发者转向使⽤ OpenJDK

OpenJDK

  • OpenJDK是免费的,想要不断体验新特性的developer来说是不错的选择
  • OracleJDK不是免费的,对于企业⽤户来说,有钱的情况下就选择OracleJDK。对应oracleJDK ,我们可以⾃⼰⽤来写代码,调试,学习即可

作者:Soulboy