乐趣区

关于java:JDK8新特性JDK11新特性

JDK 新个性

Oracle 对 Java 8 的官网反对工夫继续到 2020 年 12 月,之后将不再为集体桌面用户提供 Oracle JDK 8 的修复更新。

不过,还会有很多第三方会通过 openjdk8 持续保护 jdk8.

Java 11 仅将提供长期反对服务(LTS, Long-Term-Support),还将作为 Java 平台的默认反对版本,并且会提供技术支持直至 2023 年 9 月,对应的补丁和平安正告等反对将继续至 2026 年。

目前 Oracle 官网最近版本是 JDK15,也就是不必注册 Oracle 账户就能下载的最新版,当然这是一个短期版本,下一个长期 LTS 版本将是往年 9 月份将要公布的 JDK17。

尽管官网曾经不在修复更新 JDK8,然而 JDK8 依然是具备里程碑意义的一个重要的 JDK 版本,也是少数人仍在应用的一个版本,所以我要讲的内容是 JDK8、JDK11 的新个性。

JDK8 新个性

1. Lambda 表达式 & 函数式接口

Lambda 表达式,也可称为闭包,它是推动 Java 8 公布的最重要新个性。

Lambda 容许把函数作为一个办法的参数(函数作为参数传递进办法中)。

应用 Lambda 表达式能够使代码变的更加简洁紧凑。

什么是函数式接口?

Lambda 表达式须要函数式接口的反对。

函数式接口是指只有一个形象办法的接口。

JDK8 提供了注解 @FunctionInterface 在编译时校验函数式接口。

JDK 内置的函数式接口在 java.util.function;

例如:Runnable 接口就是一个函数式接口:

/**
 * @author yx
 * @since 2021/2/2 15:18
 */
public class JDKRunnable {public static void main(String[] args) {
        // 线程 1
        Runnable r1 = new Runnable() {
            @Override
            public void run() {System.out.println("JDK8 之前的写法");
            }
        };
        // 线程 2
        Runnable r2 = () -> System.out.println("JDK8 之后的写法");

        new Thread(r1).start();
        new Thread(r2).start();}
}
/**
 * 实现 treeSet 的比拟器排序
 */
private void comparator() {
    // TreeSet 是一个有序的汇合,它的作用是提供有序的 Set 汇合。这是应用比拟器排序。TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {return o1.length() - o2.length();}
    });
    // Lambda 表达式写法
    TreeSet<String> treeSet1 = new TreeSet<>((m, n) -> m.length() - n.length());
    // 办法援用写法
    TreeSet<String> treeSet2 = new TreeSet<>(Comparator.comparingInt(String::length));
}

办法援用介绍

办法援用通过办法的名字来指向一个办法。

办法援用能够使语言的结构更紧凑简洁,缩小冗余代码。

办法援用应用一对冒号 ::

在咱们应用 Lambda 表达式的时候,-> 左边局部是要执行的代码,即要实现的性能,能够把这部分称作 Lambda 体。有时候,当咱们想要实现一个函数式接口的那个形象办法,然而曾经有类实现了咱们想要的性能,这个时候咱们就能够用办法援用来间接应用现有类的性能去实现。

援用办法

  • 对象援用 :: 实例办法名
  • 类名 :: 静态方法名
  • 类名 :: 实例办法名
  1. 对象援用 :: 实例办法名

    private static void method() {
        Consumer<String> consumer = System.out::println;
        consumer.accept("办法援用 1 之对象援用:: 实例办法名");
    }

    System.out就是一个 PrintStream 类型的对象援用,而 println 则是一个实例办法名,须要留神的是没有括号的哟。其中 ConsumerJava 内置函数式接口 ,上面的例子用到的都是 Java 内置函数式接口。Consumer 中的惟一形象办法 accept 办法参数列表与 println 办法的参数列表雷同,都是接管一个 String 类型参数。

  2. 类名:: 静态方法名

    private static void method() {
        Function<Integer, Integer> f = Math::abs;
        final Integer apply = f.apply(-3);
        System.out.println(apply);
    }

    Math是一个类而 abs 为该类的静态方法。Function中的惟一形象办法 apply 办法参数列表与 abs 办法的参数列表雷同,都是接管一个 Integer 类型参数。

  3. 类名:: 实例办法名

    private static void method() {
        BiPredicate<String, String> n = String::equals;
        final boolean test = n.test("aaa", "bbb");
        System.out.println(test);
    }

    String是一个类而 equals 为该类的定义的实例办法。BiPredicate中的惟一形象办法 test 办法参数列表与 equals 办法的参数列表雷同,都是接管两个 String 类型参数。

援用结构器

private static void method() {
    Function<Integer, StringBuffer> is = StringBuffer::new;
    final StringBuffer sb = is.apply(10);
    System.out.println(sb.capacity());
}

Function接口的 apply 办法接管一个参数,并且有返回值。在这里接管的参数是 Integer 类型,与 StringBuffer 类的一个构造方法 StringBuffer(int capacity) 对应,而返回值就是 StringBuffer 类型。下面这段代码的性能就是创立一个 Function 实例,并把它 apply 办法实现为创立一个指定初始大小的 StringBuffer 对象。

援用数组

private static void method() {Function<Integer, int[]> fii = int[]::new;
    final int[] apply1 = fii.apply(20);
    System.out.println(apply1.length);

    Function<Integer, Double[]> fid = Double[]::new;
    final int[] apply2 = fii.apply(30);
    System.out.println(apply2.length);
}

援用数组和援用结构器很像,格局为 类型[]::new,其中类型能够为根本类型也能够是类。

2. Stream 流式编程

Stream 流是 java8 中解决数组、汇合的抽象概念,他能够指定你心愿对汇合进行的操作,能够执行非常复杂的查找、过滤和映射数据等操作。

一个 Stream 外表上与一个汇合很相似,然而汇合中保留的是数据,而流设置的是 对数据的操作

Stream 流的特点:

  1. Stream 本人不会存储元素
  2. Stream 不会扭转源对象,相同,他们会返回一个持有后果的新的 Stream
  3. Stream 操作是提早执行的,这意味着他们会等到须要后果的时候才去执行
  4. Stream 遵循 做什么,而不是怎么做 的准则,只须要形容做什么,而不必思考程序是怎么实现的
private static void stream() {int[] arr = {4, 1, 2, 5, 0, 8, 6, 5};
        // 获取最大值
        final int max = Arrays.stream(arr).max().getAsInt();
        System.out.println(max);
        // 数组中大于 3 的元素的数量
        final long count = Arrays.stream(arr).filter(e -> e > 3).count();
        System.out.println(count);

        List<Student> list = Arrays.asList(new Student(1, "花木兰", 25, 66.0),
                new Student(2, "李白", 21, 90.0),
                new Student(3, "诸葛亮", 21, 80.0),
                new Student(4, "公孙离", 18, 100d),
                new Student(5, "不知火舞", 21, 90d),
                new Student(5, "不知火舞", 21, 90d)
        );

        list.stream().filter(e -> e.getScore() >= 90)
                .findFirst()
                .ifPresent(System.out::println);
        Console.log("-----------filter--------------");

        list.stream().skip(1).limit(2).forEach(System.out::println);
        Console.log("------------limit-------------");

        list.stream().skip(3).distinct().forEach(System.out::println);
        Console.log("-------------distinct------------");

        list.stream().mapToInt(Student::getAge).min().ifPresent(System.out::println);
        Console.log("-------------map------------");

        final Set<String> collect = list.stream().map(Student::getName)
            .collect(Collectors.toSet());
        System.out.println(collect);
        Console.log("-------------collect------------");

        final List<Student> collect1 = list.stream()
                .sorted(Comparator.comparingDouble(Student::getScore).reversed()
                .thenComparing(Student::getAge)
                .thenComparing(Student::getId))
                .collect(Collectors.toList());
        System.out.println(collect1);
        Console.log("----------sort---------------");

        final boolean b = list.stream().allMatch(e -> e.getAge() < 25);
        Assert.isFalse(b);
        // 正则匹配
        final boolean b1 = list.stream()
            .anyMatch(e -> ReUtil.isMatch("^[1-9]\\d*$", e.getId().toString()));
        Assert.isTrue(b1);
        final boolean b2 = list.stream().noneMatch(e -> e.getScore() == 100d);
        Assert.isFalse(b2);
        Console.log("----------------match------------------");
        list.stream().mapToInt(Student::getAge)
                .reduce(Integer::sum)
                .ifPresent(System.out::println);
        Console.log("----------------reduce------------------");

        final DoubleSummaryStatistics collect2 = list.stream()
            .collect(Collectors.summarizingDouble(Student::getScore));
        Console.log(collect2);

    }

3. 接口默认办法

Java 8 新增了接口的默认办法。

简略说,默认办法就是接口能够有实现办法,而且不须要实现类去实现其办法。

只需在办法名后面加个 default 关键字即可实现默认办法。

为什么要有这个个性?

首先,之前的接口是个双刃剑,益处是面向形象而不是面向具体编程,缺点是,当须要批改接口时候,须要批改全副实现该接口的类,目前的 java 8 之前的汇合框架没有 foreach 办法,通常能想到的解决办法是在 JDK 里给相干的接口增加新的办法及实现。然而,对于曾经公布的版本,是没法在给接口增加新办法的同时不影响已有的实现。所以引进的默认办法。他们的目标是为了解决接口的批改与现有的实现不兼容的问题。

interface Cat {
    /**
     * 接口默认办法
     */
    default void eat() {System.out.println("一只小猫爱吃鱼");
    }

    /**
     * Java 8 的另一个个性是接口能够申明(并且能够提供实现)静态方法。*/
    static void voice() {System.out.println("一只小猫喵喵叫");
    }

}

interface Dog {default void eat() {System.out.println("一只小狗啃骨头");
    }
}

class Animal implements Cat, Dog{

    @Override
    public void eat() {
        // 第一种应用默认实现
        Dog.super.eat();
        // 第二种本人实现
        System.out.println("我是一只新动物");
        // 调用接口的静态方法
        Cat.voice();}
}

4. 日期工夫解决

Java 8 通过公布新的 Date-Time API (JSR 310)来进一步增强对日期与工夫的解决。

在旧版的 Java 中,日期工夫 API 存在诸多问题,其中有:

  • 非线程平安 − java.util.Date 是非线程平安的,所有的日期类都是可变的,这是 Java 日期类最大的问题之一。
  • 设计很差 − Java 的日期 / 工夫类的定义并不统一,在 java.util 和 java.sql 的包中都有日期类,此外用于格式化和解析的类在 java.text 包中定义。java.util.Date 同时蕴含日期和工夫,而 java.sql.Date 仅蕴含日期,将其纳入 java.sql 包并不合理。另外这两个类都有雷同的名字,这自身就是一个十分蹩脚的设计。
  • 时区解决麻烦 − 日期类并不提供国际化,没有时区反对,因而 Java 引入了 java.util.Calendar 和 java.util.TimeZone 类,但他们同样存在上述所有的问题。

新的日期 / 工夫 API 的一些设计准则是:

  • 不变性:新的日期 / 工夫 API 中,所有的类都是不可变的,这对多线程有益处。
  • 关注点拆散:新的 API 将人可读的日期 / 工夫和机器的日期 / 工夫明确拆散,它为日期 Date、工夫 Time、日期工夫 DateTime、工夫戳 unix timestamp 以及时区定义了不同的类。
  • 清晰:在所有的类中,办法都被明确定义 用以实现雷同的行为。举个例子,在所有的类中都定义了 now()办法、format()办法、parse()办法,而不是像以前那样专门有一个独立的类。为了更好的解决问题,所有的类都应用了工厂模式和策略模式,一旦你应用了其中某个类的办法,与其余类协同工作并不艰难。
  • 实用操作:所有新的日期 工夫 API 类都实现了一系列办法用以实现通用的工作,如:加、减、格式化、解析、从日期工夫中提取独自局部,等等。
  • 可扩展性:新的日期 / 工夫 API 是工作在 ISO-801 日历零碎上的,但咱们也能够将其利用在非 ISO 的日历上。

LocalDate、LocalTime、LocalDateTime

private static void dateTime() {LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.now();
        LocalDateTime localDateTime = LocalDateTime.now();
        Console.log("以后日期是{}, 工夫是{}, 日期工夫{}", localDate,
                localTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")),
                localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd                              HH:mm:ss")));
        // 自定义
        LocalDate date = LocalDate.of(2021, Month.AUGUST, 1);
        Console.log("自定义日期{}", date);
        // 设置地区
        LocalDate seoulDate = LocalDate.now(ZoneId.of("Asia/Seoul"));
        LocalTime seoulTime = LocalTime.now(ZoneId.of("Asia/Seoul"));
        LocalDateTime seoul = LocalDateTime.now(ZoneOffset.of("+9"));
        Console.log("首尔日期{}, 首尔工夫{}, 首尔日期工夫{}", seoulDate,
                seoulTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")),
                seoul.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 工夫戳是指格林威治工夫 1970 年 01 月 01 日 00 时 00 分 00 秒 (北京工夫 1970 年 01 月 01 日 08 时 00 分 00 秒) 起至现            在的总秒数。Console.log("以后工夫转毫秒数{}", localDateTime
                    .toInstant(ZoneOffset.of("+8")).toEpochMilli());

        Console.log("格林威治工夫纳秒{},北京工夫{}", Instant.now(), localDateTime);

        /**
         * 工夫差值
         */
        Duration duration = Duration.between(localDateTime, seoul);
        System.out.println(duration);
        /**
         * 计算日期距离
         */
        Period period = Period.between(date, seoulDate);
        System.out.println(period);

    }

5.Optional 类

从 Java 8 引入的一个很乏味的个性是 Optional 类。Optional 类次要解决的问题是臭名远扬的空指针异样(NullPointerException)这个异样就不多说了,必定是每个 Java 程序员都十分理解的异样。Optional 的残缺门路是 java.util.Optional,应用它是为了防止代码中的 if (obj != null) {} 这样范式的代码,能够采纳链式编程的格调。而且通过 Optional 中提供的 filter 办法能够判断对象是否符合条件,在符合条件的状况下才会返回,map 办法能够在返回对象前批改对象中的属性。

Optional 的用途

实质上,Optional 是一个蕴含有可选值的包装类,这意味着 Optional 类既能够含有对象也能够为空。咱们要晓得,Optional 是 Java 实现函数式编程的强劲一步,并且帮忙在范式中实现。然而 Optional 的意义显然不止于此。咱们晓得,任何拜访对象办法或属性的调用都可能导致 NullPointerException,在这里,我举个简略的例子来阐明一下:

String result = test.getName().getTime().getNum().getAnswer();

在下面的这个代码中,如果咱们须要确保不触发异样,就得在拜访每一个值之前对其进行明确地查看,就是应用 if else 对 test 等值进行判断是否为 null,这很容易就变得简短,难以保护。为了简化这个过程,Google 公司驰名的 Guava 我的项目引入了 Optional 类,Guava 通过应用查看空值的形式来避免代码净化,并激励程序员写更洁净的代码。Optional 实际上是个容器:它能够保留类型 T 的值,或者仅仅保留 null。Optional 提供很多有用的办法,这样咱们就不必显式进行空值检测。

Optional 的构造函数

Optional 的三种结构形式:Optional.of(obj),Optional.ofNullable(obj) 和明确的 Optional.empty()

  • Optional.of(obj):它要求传入的 obj 不能是 null 值的, 否则间接报 NullPointerException 异样。
  • Optional.ofNullable(obj):它以一种智能的,宽容的形式来结构一个 Optional 实例。来者不拒,传 null 进到就失去 Optional.empty(),非 null 就调用 Optional.of(obj).
  • Optional.empty():返回一个空的 Optional 对象

Optional 的罕用函数

  • isPresent:如果值存在返回 true,否则返回 false。
  • ifPresent:如果 Optional 实例有值则为其调用 consumer,否则不做解决
  • get:如果 Optional 有值则将其返回,否则抛出 NoSuchElementException。因而也不常常用。
  • orElse:如果有值则将其返回,否则返回指定的其它值。
  • orElseGet:orElseGet 与 orElse 办法相似,区别在于失去的默认值。orElse 办法将传入的字符串作为默认值,orElseGet 办法能够承受 Supplier 接口的实现用来生成默认值
  • orElseThrow:如果有值则将其返回,否则抛出 supplier 接口创立的异样。
  • filter:如果有值并且满足断言条件返回蕴含该值的 Optional,否则返回空 Optional。
  • map:如果有值,则对其执行调用 mapping 函数失去返回值。如果返回值不为 null,则创立蕴含 mapping 返回值的 Optional 作为 map 办法返回值,否则返回空 Optional。
  • flatMap:如果有值,为其执行 mapping 函数返回 Optional 类型返回值,否则返回空 Optional。

Optional 应该怎么用

在应用 Optional 的时候须要思考一些事件,以决定什么时候怎么应用它。重要的一点是 Optional 不是 Serializable。因而,它不应该用作类的字段。Optional 类能够将其与流或其它返回 Optional 的办法联合,以构建晦涩的 API。咱们来看一个示例,咱们不应用 Optional 写代码是这样的:

public String getName(User user) {if (user == null) {return "";}
    return user.getName();}

接着咱们来革新一下下面的代码,应用 Optional 来革新,咱们先来举一个 Optional 滥用,没有达到晦涩的链式 API,反而简单的例子,如下:

public String getName(User user) {Optional<User> u = Optional.ofNullable(user);
    if(!u.isPresent()) {return "";}
    return u.get().getName();
}

这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用 isPresent 办法来代替原先 user==null。这样的改写并不是 Optional 正确的用法,咱们再来改写一次。

public String getName(User user) {return Optional.ofNullable(user)
        .map(u -> u.getName())
        .orElse("");
}

这样才是正确应用 Optional 的姿态。那么依照这种思路,咱们能够安心的进行链式调用,而不是一层层判断了。

6.Base64

在 Java8 中,Base64 编码曾经成为 Java 类库的规范。

Java 8 内置了 Base64 编码的编码器和解码器。

Base64 工具类提供了一套静态方法获取上面三种 BASE64 编解码器:

· 根本:输入被映射到一组字符 A -Za-z0-9+/,编码不增加任何行标,输入的解码仅反对 A -Za-z0-9+/。

· URL:输入映射到一组字符 A -Za-z0-9+_,输入是 URL 和文件。

· MIME:输入隐射到 MIME 敌对格局。输入每行不超过 76 字符,并且应用 ’\r’ 并追随 ’\n’ 作为宰割。编码输入最初没有行宰割。

public static void main(String[] args) {// 编码,加密 getEncoder()
        String str = Base64.getEncoder()
            .encodeToString("java8_Base64?".getBytes(StandardCharsets.UTF_8));
        Console.log("规范加密之后的字符串是 {}", str);
        // 解码,解密 getDecoder()
        byte[] decode = Base64.getDecoder().decode(str);
        Console.log("规范解码 {}", new String(decode, StandardCharsets.UTF_8));

        //URL 编码
        str = Base64.getUrlEncoder()
            .encodeToString("dksiofdo+/d,s;".getBytes(StandardCharsets.UTF_8));
        Console.log("加密后字符串是:{}", str);
        //URL 解码
        byte[] decode1 = Base64.getUrlDecoder().decode(str);
        Console.log("URL 解码后 {}", new String(decode1, StandardCharsets.UTF_8));


        //Mime 编码
        str = Base64.getMimeEncoder()
            .encodeToString("dksiofdo+/d,s;ddd".getBytes(StandardCharsets.UTF_8));
        Console.log("加密后字符串是:{}", str);
        //Mime 解码
        byte[] decode2 = Base64.getMimeDecoder().decode(str);
        Console.log("Mime 解码后 {}", new String(decode2, StandardCharsets.UTF_8));

        /*
        Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator):返回具备给定 lineLength 的已批改 MIME 变体的编码器(向下舍入到最靠近的 4 的倍数 - 输入在 lineLength<= 0 时不分成行)和 lineSeparator。当 lineSeparator 蕴含 RFC 2045 的表 1 中列出的任何 Base64 字母字符时,它会抛出                           java.lang.IllegalArgumentException。相当于用 lineSeparator 隔开加密后的字符串, 每 lineLength(4 的倍数向下取整)隔开
         */
        String s = Base64.getMimeEncoder(6, ".?--".getBytes())
                .encodeToString("jimidssafsaa".getBytes(StandardCharsets.UTF_8));
        Console.log("加密后 {}", s);
        byte[] decode3 = Base64.getMimeDecoder().decode(s);
        Console.log("Mime 解码后 {}", new String(decode3, StandardCharsets.UTF_8));

    }

JDK11 新个性

1. 类型推断

private static void jdk11() {
    var s = "world";
    var list = new ArrayList<String>();
    list.add(s);
    list.add("java");
    list.add("python");
    list.stream().map(e -> "Hello," + e)
        .forEach(System.out::println);
}

局部变量类型推断就是右边的类型间接应用 var 定义,而不必写具体的类型,编译器能依据左边的表达式主动推断类型。

2. 字符串增强

String 新增了 strip()办法,和 trim()相比,strip()能够去掉 Unicode 空格,例如,中文空格:

// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空格
"Javastack".strip(); // "Javastack"
// 去除尾部空格
"Javastack".stripTrailing(); // "Javastack"
// 去除首部空格
"Javastack".stripLeading(); // "Javastack"
// 复制字符串
"Java".repeat(3);// "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3

3. 汇合增强

自 Java 9 开始,Jdk 外面为汇合(List/ Set/ Map)都增加了 of 和 copyOf 办法,它们两个都用来创立不可变的汇合,来看下它们的应用和区别。

var list = List.of("Java", "Python", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); // true
var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // false
static <E> List<E> of(E... elements) {switch (elements.length) { // implicit null check of elements
    case 0:
        return ImmutableCollections.emptyList();
    case 1:
        return new ImmutableCollections.List12<>(elements[0]);
    case 2:
        return new ImmutableCollections.List12<>(elements[0], elements[1]);
    default:
        return new ImmutableCollections.ListN<>(elements);
  }
}
static <E> List<E> copyOf(Collection<? extends E> coll) {return ImmutableCollections.listCopy(coll);
}
static <E> List<E> listCopy(Collection<? extends E> coll) {if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {return (List<E>)coll;
    } else {return (List<E>)List.of(coll.toArray());
    }
}

能够看出 copyOf 办法会先判断起源汇合是不是 AbstractImmutableList 类型的,如果是,就间接返回,如果不是,则调用 of 创立一个新的汇合。

示例 2 因为用的 new 创立的汇合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 办法又创立了一个新的实例,所以为 false.

留神:应用 of 和 copyOf 创立的汇合为不可变汇合,不能进行增加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异样。

下面演示了 List 的 of 和 copyOf 办法,Set 和 Map 接口都有。

4.Stream 流解决增强

private static void jdk11() {
    var s = "world";
    var list = List.of(s, "java", "python", "go");
    list.stream().map(e -> "Hello," + e)
        .forEach(System.out::println);
    /**
         * lambda 表达式体为 true 打印,遇到 false 则不再持续
         */
    list.stream().takeWhile(e -> !StrUtil.startWith(e, "p"))
        .forEach(System.out::println);

    System.out.println("--------------------------------");
    /**
         * lambda 表达式体为 true 不打印,始终到遇到 false 开始打印
         */
    list.stream().dropWhile(e -> !StrUtil.startWith(e, "p"))
        .forEach(System.out::println);
   
}

// 输入
Hello, world
Hello, java
Hello, python
Hello, go
world
java
--------------------------------
python
go

5.Http Client 反对

Http Client 在 Java 9 中就引入了,在 Java 10 中也进行了局部优化批改,然而始终不够成熟,直到 Java 11 对 Http Client 进行了齐全的革新,使其标准化。

public static void get() throws Exception {HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1:8084/hc/getTest"))
                .build();
        // 同步
        HttpResponse<String> response =
                client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("同步" + response.statusCode());
        System.out.println("同步" + response.body());
    }

    public static void get1() throws Exception {HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1:8084/hc/getTest"))
                .build();
        // 异步
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(r -> {System.out.println(r.statusCode());
                    return r;
                }).thenApply(HttpResponse::body)
                .thenAccept(System.out::println)
                .join();}

    // 异步调用 POST
    public static void asyncPost() throws Exception {HttpClient client = HttpClient.newBuilder()
                // http1.1 or http2.0 默认 2.0
                .version(HttpClient.Version.HTTP_2)
                // 是否听从服务器收回的重定向
                .followRedirects(HttpClient.Redirect.NORMAL)
                // 超时工夫
                .connectTimeout(Duration.ofSeconds(20))
                // 代理
                // 身份认证
                // 等等
                .build();

        String jsonBody = JSONUtil.toJsonStr("HttpClient.....post");

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://127.0.0.1:8084/hc/postTest"))
                .header("Content-Type" , "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(r -> {System.out.println(r.statusCode());
                    return r;
                }).thenApply(HttpResponse::body)
                .thenAccept(System.out::println)
                .join();}

6.JShell 工具

Java Shell 工具是 JDK1.9 呈现的工具,Java Shell 工具(JShell)是一个用于学习 Java 编程语言和 Java 代码原型的交互式工具。JShell 是一个 Read-Evaluate-Print 循环(REPL),它在输出时评估申明,语句和表达式,并立刻显示后果。该工具从命令行运行。

为什么要应用 JShell?

应用 JShell,您能够一次输出一个程序元素,立刻查看后果,并依据须要进行调整。
Java 程序开发通常波及以下过程:

  • 写一个残缺的程序。
  • 编译它并修复任何谬误。
  • 运行程序。
  • 弄清楚它有什么问题。
  • 编辑它。
  • 反复这个过程。

JShell 可帮忙您在开发程序时尝试代码并轻松摸索选项。您能够测试单个语句,尝试不同的办法变体,并在 JShell 会话中试验不相熟的 API。JShell 不替换 IDE。在开发程序时,将代码粘贴到 JShell 中进行试用,而后将 JShell 中的工作代码粘贴到程序编辑器或 IDE 中。

参考:https://www.jianshu.com/p/84a…

参考:https://blog.csdn.net/csdnnew…

退出移动版