Java一些特性的解析和示例 stream、Optional、lambda、HttpClientApi ... 最佳实践

在这里插入图片描述

本地变量类型推断:关键字 var (@since 10)

这个var跟C++11的auto一样的


//        1、本地类型推断 关键字 var @since 10
//        var 使用的是编译器的能力,通过赋值、变量定义时左值的类型来推断出变量的类型
//        var 定义的变量必须在声明时赋值,不然编译器也猜不透你想用的那个类型
        var var1_integer = 1;
        var var2_double = 2.00;
        var var3_string = "hello java11";
        var var4_list = new LinkedList<String>();
        var var5_complex = new HashMap<String, List<Double>>();

//        看一下所定义的变量类型
        System.out.println("############### var ################");
        System.out.println(var1_integer);
        System.out.println(var2_double);
        System.out.println(var3_string.getClass());
        System.out.println(var4_list.getClass());
        System.out.println(var5_complex.getClass());

//        Java 11 增强了lambda中的var的能力 @since 11
        IntFunction<Integer> addOperator = (var a) ->  a * a;

//        var 定义的变量与普通方式定义的变量是完全一样的
        var4_list.add("oracle");
        var4_list.add("java");
        var4_list.add("11");

        var tempList =  List.of(2.0, 3.0);
        var5_complex.put("keyone", tempList);

//      var 在循环中的使用
        for (var i = 0; i < var4_list.size(); i++) {
            System.out.println(var4_list.get(i));
        }

        for (var s : var4_list) {
            System.out.println(s);
        }

        for (var i = var4_list.iterator(); i.hasNext(); ) {
            System.out.println(i.next());
        }

        System.out.println(addOperator.apply(3));

        System.out.println("############### var ################");

输出
在这里插入图片描述

lambda表达式 (@since 1.8)

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

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

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java
代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

对接口的要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法

jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。

@FunctionalInterface# 修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。

//        无参lambda
        Thread t1 = new Thread(()->{
            System.out.println(Thread.currentThread().getId());
        } , "ExampleLambdaThread");
//      一个参数lambda
        var wordinput = List.of("hello world", "hello apache", "oracle java" , "apache hadoop");
        wordinput.forEach(i->{
            System.out.println(i);
        });
//      两个参数lambda
        BinaryOperator<Integer> biFunction = (Integer a ,Integer b)->{
            return a+b;
        };

        System.out.println(biFunction.apply(2,3));
        

定义使用lambda的接口

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

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     */
    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));
    }
}

stream (@since 1.8 ) java 11 对其有增强

stream() − 为集合创建串行流。

parallelStream() − 为集合创建并行流。

示例中要到的pojo


class Person implements Serializable{
    private String name;
    private String sex;
    private Integer age;

    public Person() {
    }

    public Person(String name, String sex, Integer age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

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

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

使用stream.collect 将List转换为Map,Wordcount

//        2、stream @since 1.8
//        Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
//        Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
//        这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
//        元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
//        java 11 steam 有增强

        System.out.println("############### stream #############");

        //  使用steam将 List 转换为 Map
//        这里使用的 List.of @since 9 , 你可以把它理解为 初始化元素集合
        var personList = List.of(
                new Person("tom", "male", 35)
                , new Person("jary", "male", 34)
                , new Person("linda", "female", 23));

        var personMap = personList
                .stream()
                .collect(Collectors.toMap(Person::getName, i -> i));

//        forEach @since 1.8 这个应该不用多说了
        personMap.forEach((k,v)->{
            System.out.println("entry key : " + k + " value : "+v.toString());
        });

//      java stream wordcout example
//
        System.out.println("########## wordcount #####################");


//      Function 接收一个参数,返回一个参数 ;BiFunction 接收两个参数,返回一个参数;
//      java stream的出现使得我们对各种数据集合的复杂操作变得简单、明了
//      java stream 是java函数式编程支持的体现
//      而stream带来了对集合序列化操作的支持,整体看起来就像是链式模式的操作
//      stream相关的方法就像Apache Sparck,实际上每个方法的具体操作也跟spack差不多,当然肯定没有分布式的那些东西
//      我们来用stream完成一个Wordcount。
//      stream上的方法分为两类:中间操作 和 终端操作
//      对于操作的数据集来说使用中间操作的结果可以传递到下一步操作中
//      相反的终端操作则不会再产生中间结果集
//       我会对stream的比较复杂的方法进行说明,而那些见名知意的方法我想也不需要再次赘述了

//        limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。
//        distint 去重操作,对重复元素去重,底层使用了equals方法。
//        filter 过滤操作,把不想要的数据过滤。
//        peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
//        skip 跳过操作,跳过某些元素。
//
//        collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。
//        count 统计操作,统计最终的数据个数。
//        findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。
//        noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。
//        min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。
//        reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
//        forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。
//        toArray 数组操作,将数据流的元素转换成数组。



        var wordinput = List.of("hello world", "hello apache", "oracle java" , "apache hadoop");

        wordinput.stream()
                .map(word->word.split("\\s"))
//                map 操作是对集合中的每个元素的操作 1对1
                .flatMap(Arrays::stream)
//                flatMap 操作是一种对数据扁平化的操作,是对集合中所有元素的操作
                .sorted()
//                这个就是排序了 , 在这个示例中可有可无
                .reduce(new HashMap<String, Integer>(),
                        new BiFunction<HashMap<String, Integer>, String, HashMap<String, Integer>>() {
                            @Override
                            public HashMap<String, Integer> apply(HashMap<String, Integer> stringIntegerHashMap, String s) {
                                if (stringIntegerHashMap.get(s) != null) {
                                    stringIntegerHashMap.put(s, stringIntegerHashMap.get(s) + 1);
                                    return stringIntegerHashMap;
                                }
                                stringIntegerHashMap.put(s, 1);
                                return stringIntegerHashMap;
                            }
                        },
                        new BinaryOperator<HashMap<String, Integer>>() {
                            @Override
                            public HashMap<String, Integer> apply(HashMap<String, Integer> stringIntegerHashMap, HashMap<String, Integer> stringIntegerHashMap2) {
                                return null;
                            }
                        })
//                reduce 是stream中比较复杂的一个操作,其含义是 合并 ,我们这个实现跟Hadoop MapReduce shuffle操作类似,
//                reduce 有3个重载
                .forEach( (k , v)->{
                    System.out.println("("+k+","+v+")");
                });
//        遍历,输出

        System.out.println("########## wordcount #####################");

输出

在这里插入图片描述

Optional (@since 1.8) java 11 对其有增强

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

    /**
     * 对可能返回null的函数和方法使用Optional返回值类型可大幅减少java.lang.NullPointerException异常的出现
     * @return
     */
    public Optional<Person> getPerson(){
        return Optional.ofNullable(new Person());
    }

      Person person = null;
        Person person1 = new Person("xiaoming" , "male" , 15);
//     Optional.of(obj).get() 这样跟直接用对象差不多,不推荐
        Person temp = Optional.of(person).get();
//        Optional.ofNullable 可以接受一个为null的对象
        if (Optional.ofNullable(person).isEmpty()) {
            System.out.println("object persion is null");
        }

//        Optional.of 接受一个null对象时会报异常:java.lang.NullPointerException
//        也就是说 of 接受的必须是一个非null对象才能执行后面的操作,这跟直接使用null对象几乎没有区别
        if (Optional.of(person).isEmpty()) {
            System.out.println("object persion is null");
        }
//		isEmpty 是判断一个对象是否为null
//		isPresent 判断的是一个对象是否有值
        if (Optional.ofNullable(person).isPresent()) {
            System.out.println("person is no value");

        }

//        Person temp = Optional.of(person).orElse(new Person("xiaohong", "famele", 14));

//        orElse 如果对象是一个null,那么会返回一个指定的同类型对象
        Person temp2 = Optional.ofNullable(person).orElse(new Person("xiaohong","famele",14));

        System.out.println(temp2.toString());

        Optional.ofNullable(person1).orElseGet(()-> new Person());

//        Optional.ofNullable(person).orElseThrow(()->new IllegalArgumentException("this object is null"));


//        Optional.ofNullable(person1).ifPresent();

        ComponentApplicationTests componentApplicationTests = new ComponentApplicationTests();

        Optional personOptional = componentApplicationTests.getPerson();

        if (personOptional.isEmpty()) {
            System.out.println("personOptional is null");
        }

        if (personOptional.isPresent()) {
            System.out.println("personOptional is no value");
        }

        Person personTemp = (Person)personOptional.orElseGet(() -> new Person("default", "male", 0));

        System.out.println(personTemp.toString());

try-with-resources(@since 1.7) 和 InputStream.transferTo (@since 9)

//        try-with-resources 的意义是 一但try-catch语句块中的代码出现异常,try()里的对象将被正确关闭。@since 1.7 更传统的方式是在finally里手动close。
        try(FileInputStream fileInputStream = new FileInputStream("D:/tmp/input.txt")){
//            InputStream.transferTo 方法直接将输入流中的数据传输到OutputStream
            fileInputStream.transferTo(new FileOutputStream("D:/tmp/inputCopy.txt"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

输出

在这里插入图片描述

Http Client api (@since 11)

HttpClientApi是从java9引入,到java11正式集成到标准库里发布package java.net.http
支持http/2、websocket
支持同步、异步

用来取代遗留的java.net.HttpURLConnection。该API用来在Java程序中作为客户端请求HTTP服务。

现有的HttpURLConnection API存在许多问题:

  • 基本的URLConnection被设计为支持多种协议,很多协议已经过时不用了。
  • 该API早于HTTP/1.1发布,而且过于抽象。
  • 使用困难,包含很多没有文档说明的行为。
  • 只支持阻塞模式。
  • 维护困难。

除了HttpURLConnection,还有一些其他的HTTP客户端API和实现类库,比如Apache的HttpComponents(HttpClient)和Jetty等等,但是它们过于重量了,而且没有利用新的Java语言特性如Lambda表达式。

HTTP Client API内部实现上,通过java.util.concurrent.CompletableFuture来支持非阻塞请求和响应,由Java平台的响应式流(Reactive Streams)支持java.util.concurrent.Flow API来实现请求和响应的背压(back-pressure)和流量控制(flow-control)。利用响应式流RX Flow的概念,避免了为支持HTTP/2而创造过多概念。

//        Protocol http/2  POST  nobody
        {
            var client = HttpClient.newBuilder()
                    .version(HttpClient.Version.HTTP_2)
                    .build();


            var request = HttpRequest.newBuilder()
                    .uri(URI.create("https://www.baidu.com"))
                    .POST(HttpRequest.BodyPublishers.noBody())
                    .build();

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

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

            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        {
            var client = HttpClient.newBuilder()
                    .version(HttpClient.Version.HTTP_2)
                    .build();


            var request = HttpRequest.newBuilder()
                    .uri(URI.create("https://www.baidu.com"))
                    .POST(HttpRequest.BodyPublishers.noBody())
                    .build();

            try {
                var response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                        .thenApply(HttpResponse::body)
                        .thenAccept(body -> {
                            System.out.println("java 11 http client api async ");
                            System.out.println(body);
                        });


            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

//      Protocol http1/1 GET with authentication
        var client = HttpClient.newBuilder()
                .authenticator(new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication("postman", "password".toCharArray());
//                        return super.getPasswordAuthentication();
                    }
                })
                .build();

        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://postman-echo.com/basic-auth"))
                .build();

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

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

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
// 另外一些方法的使用示例
// Synchronous Example
    HttpClient client = HttpClient.newBuilder()
        .version(Version.HTTP_1_1)
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(20))
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
        .authenticator(Authenticator.getDefault())
        .build();
   HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
   System.out.println(response.statusCode());
   System.out.println(response.body());  
   
// Asynchronous Example
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://foo.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);  

输出

在这里插入图片描述

发布了58 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/wangxudongx/article/details/104720317