16.Java 8 的新特性


一、Lambda表达式

  • 举例:(o1,o2) -> Interger.compare(o1,o2);
  • 格式:
    • -> : lambda操作符 或 箭头表达式
    • ->左边 :lambda形参列表(其实就是接口中的抽象方法的形参列表)
    • ->右边 : lambda体(其实就是重写的抽象方法的方法体)
  • lambda表达式的本质:作为函数式接口的实例
  • 总结:
    • ->左边:
      • lambda形参列表的参数类型可以省略(类型推断);
      • 如果lambda形参列表只有一个参数,其一对()也可以省略
    • ->右边:
      • lambda应该使用一对{}包裹;
      • 如果lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字
import java.util.Comparator;
import java.util.function.Consumer;

public class LambdaTest {
    public static void main(String[] args) {

        //语法格式一:无参,无返回值
        Runnable r1 = () -> {
            System.out.println("我爱中华!");
        };
        r1.run();

        //语法格式二:Lambda需要一个参数,但是没有返回值
        Consumer consumer1 = (String str) -> {
            System.out.println(str);
        };
        consumer1.accept("谎言和誓言的区别是什么呢?");

        //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
        Consumer consumer2 = (str) -> System.out.println(str);
        consumer2.accept("一个是听的人当真了,一个是说的人当真了。");

        //语法格式四:Lambda若只需要一个参数,参数的小括号可以省略
        Consumer consumer3 = str -> System.out.println(str);
        consumer3.accept("花开花落几时回?");

        //语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
        Comparator comparator1 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(comparator1.compare(12, 21));

        //语法格式六:当Lambda体只有一条执行语句时,return 与大括号若有,都可以省略
        Comparator comparator2 = (o1,o2) -> o1.compareTo(o2);
        System.out.println(comparator2.compare(12, 6));

    }
}

二、函数式(Functional)接口

??如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。可以通过Lambda表达式来创建该接口的对象。若Lambda表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明。可以通过在一个接口上使用@FunctionalInterface注解,这样可以检查它是否是一个函数时接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。匿名实现类表示都可以用Lambda表达式来写。

2.1、Java内置四大核心函数式接口

函数式接口 参数类型 返回类型 用途
Consumer消费型接口 T void 对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier 供给型接口 T 返回类型为T的对象,包含方法:T get()
Function 函数型接口 T R 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate 断定型接口 T boolean 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class FunctionalInterface {
    public static void main(String[] args) {
        FunctionalInterface test = new FunctionalInterface();
        List list = Arrays.asList("北京","天津","南京","西京","东京","开封");

        test.happyTime(400,money -> System.out.println("今天共消费" + money));
        List string =  test.filterString(list,str -> str.contains("京"));
        System.out.println(string);
    }

    public void happyTime(double money,Consumer consumer){
        consumer.accept(money);
    }

    //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
    public List filterString(List list,Predicate predicate){
        ArrayList filterList = new ArrayList<>();

        for (String str : list) {
            if(predicate.test(str)){
                filterList.add(str);
            }
        }

        return filterList;
    }
}

2.2、其它接口

函数式接口 参数类型 返回类型 用途
BiFunction T,U R 对类型为T,U参数应用操作,返回R类型的结果。,包含方法:R apply(T t,U u)
UnaryOperator(Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为:T apply(T t)
BinaryOperator(BiFunction子接口) T,T T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法:T apply(T t1,T t2)
BiConsumer T,U void 对类型为T,U参数应用操作。包含方法为:void accept(T t,U u)
BiPredicate T,U boolean 包含方法:boolean test(T t,U u)
ToIntFunction T int 计算int值的函数
ToLongFunction T long 计算long值的函数
ToDoubleFunction T double 计算double值的函数
IntFunction int R 参数为int类型的函数
LongFunction long R 参数为long类型的函数
DoubleFunction double R 参数为double类型的函数

三、方法引用与构造器引用

3.1、方法引用

??当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。方法引用可以看做是Lambda表达式的深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法。方法引用要求实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!(针对情况一和情况二)格式:使用操作符“::”将类(或对象)与方法名分隔开来。当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时,(针对情况三)格式:ClassName::methodName

如下三种主要使用情况

  • 情况一:对象::实例方法名
  • 情况二:类::静态方法名
  • 情况三:类::实例方法名
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class MethodReference {
    public static void main(String[] args) {

        //情况一:对象::实例方法
        //Consumer中的 void accept(T t)
        //PrintStream中的 void println(T t)
        PrintStream printStream = System.out;
        Consumer consumer = printStream::println;
        consumer.accept("Sakura");

        //情况二:类::静态方法
        //Supplier中的 T get()
        //Person中的 String gerName() 
        Person person = new Person("Sakura",10);
        Supplier supplier = person::getAge;
        System.out.println(supplier.get());

        //情况三:类::非静态方法
        //Comparator中的 int compare(T t1,T t2)
        //Integer中的 int compare(T t1,T t2)
        Comparator comparator = Integer::compare;
        System.out.println(comparator.compare(10, 13));

        System.out.println();

        //Function中的 R apply(T t)
        //Math中的 Long round(Double d)
        Function fun1 = new Function() {
            @Override
            public Long apply(Double d) {
                return Math.round(d);
            }
        }; 
        System.out.println(fun1.apply(12.3));

        Function fun2 = d -> Math.round(d);
        System.out.println(fun2.apply(21.5));

        Function fun3 = Math::round;
        System.out.println(fun3.apply(33.7));

        System.out.println();

        //Comparator中的 int comapre(T t1,T t2)
        //String中的 int t1.comparaTo(t2)
        Comparator comparator2 = String::compareTo;
        System.out.println(comparator2.compare("intel", "amd"));

        //Function中的 R apply(T t)
        //Person中的 String getName()
        Function fun4 = Person::getName;
        System.out.println(fun4.apply(person));
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

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

    public String toString(){
        return "Person{ name:" + name + ",age:" + age + "}";
    }
}

3.2、构造器引用

??构造器引用与方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。抽象方法的返回值类型即为构造器所属的类的类型。

import java.util.function.Function;
import java.util.function.Supplier;

public class ConstructorReference {
    public static void main(String[] args) {

        //Supplier中的 T get()
        Supplier supplier = Person::new;
        System.out.println(supplier.get());

        //Function中的 R apply(T t)
        Function fun = Person::new;
        System.out.println(fun.apply("Sakura"));

        //BiFunction中 R apply(T t,U u)
        BiFunction biFunction = Person::new;
        System.out.println(biFunction.apply("Sakura", 9));
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

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

    public String toString(){
        return "Person{ name:" + name + ",age:" + age + "}";
    }
}

3.3、数组引用

??可以把数组看做是一个特殊的类,则写法与构造器引用一致;

import java.util.Arrays;
import java.util.function.Function;

public class ArrayReferences {
    public static void main(String[] args) {

        Function fun1 = length -> new String[length];
        String[] array1 = fun1.apply(5);
        System.out.println(Arrays.toString(array1));

        Function fun2 = String[]::new;
        String[] array2 = fun2.apply(10);
        System.out.println(Arrays.toString(array2));
    }
}

四、强大的Stream API

4.1 Stream概述

??Stream API(java.util.stream)把真正的函数式接口编程风格引入到Java中。Stream是Java 8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式。

  • Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
  • Stream自己不会存储元素
  • Stream不会改变源对象。相反,它会返回一个持有结果的新Stream
  • Stream操作是延迟执行的。这意味着它会等到需要结果的时候才执行

创建Stream的操作三个对象

  1. 创建Stream:一个数据源(如:集合、数组),获取一个流
  2. 中间操作:一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作):一旦执行终止操作,就会执行中间操作链,并产生结果。之后,不会再被使用

4.2、创建Stream

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest {
    public static void main(String[] args) {

        //创建Stream方式一:通过集合
        List list = Arrays.asList(1,2,3,4,5);

        //default Stream stream():返回一个顺序流
        Stream stream = list.stream();
        //default Stream parallelStream():返回一个并行流
        Stream parallelStream = list.parallelStream();

        //创建Stream方式二:通过数组
        Object[] object = list.toArray();
        //调用Arrays类的static  Stream stream(T[] array):返回一个流
        Stream stream1 = Arrays.stream(object);

        //创建Stream方式三:通过Stream的of()
        Stream stream2 = Stream.of(1,2,3,4,5);

        //创建Stream方式四:创建无限流
        //public static Stream iterate(final T seed,final UnaryOperator f) //迭代
        Stream.iterate(0, t->t+2).limit(10).forEach(System.out::println);
        //public static Stream generate(Supplier s)                        //生成
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
}

4.3、中间操作

??多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次从全部处理,称为“惰性求值”。

筛选与切片

Stream filter(Predicate<? super T> predicate)  //接收Lambda,从流中排除某些元素         
Stream distinct()                              //筛选,通过流所生成元素的hashCode()和equals()去除重复元素
Stream limit(long maxSize)                     //截断流,使其元素不超过给定数量 
Stream skip(long n)                            //跳过元素,返回一个扔掉前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(long maxSize)互补
import java.util.ArrayList;
import java.util.List;

public class StreamTest {
    public static void main(String[] args) {
        
        List list = new ArrayList<>();
        list.add(new Person("Sakura",9));
        list.add(new Person("Akame",17));
        list.add(new Person("Miloto",13));
        list.add(new Person("Shana",15));
        list.add(new Person("Nanoha",11));

        //filter(Predicate<? super T> predicate)   //接收Lambda,从流中排除某些元素
        list.stream().filter(person -> person.getAge()<=13).forEach(System.out::println);
        System.out.println();
        //limit(long maxSize)                      //截断流,使其元素不超过给定数量
        list.stream().limit(3).forEach(System.out::println);
        System.out.println();
        //skip(long n)                             //跳过元素,返回一个扔掉前n个元素的流。若流中元素不足n个,则返回一个空流。
        list.stream().skip(3).forEach(System.out::println);
        System.out.println();
        //distinct()                               //筛选,通过流所生成元素的hashCode()和equals()去除重复元素
        list.add(new Person("Rimuru",10));
        list.add(new Person("Rimuru",10));
        list.add(new Person("Rimuru",11));
        list.add(new Person("Rimuru",11));
        list.add(new Person("Rimuru",11));
        list.stream().distinct().forEach(System.out::println);
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

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

    public String toString(){
        return "Person{ name:" + name + ",age:" + age + "}";
    }

    public boolean equals(Object obj) {
        if(obj instanceof Person){
            Person person = (Person)obj;
            if(name.equals(person.name)){
                if(age == person.age){
                    return true;
                } 
            }
        }
        return false;
    }

    public int hashCode() {
        return name.hashCode()+age;
    }
}

映射

 Stream map(Function<? super T,? extends R> mapper)                       //接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)                    //接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream    
IntStream mapToInt(ToIntFunction<? super T> mapper)                             //接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream         
LongStream mapToLong(ToLongFunction<? super T> mapper)                          //接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream  
 Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) //接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流   
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest {
    public static void main(String[] args) {

        List list = Arrays.asList("aa","bb","cc","dd");
        Stream> streamStrean = list.stream().map(StreamTest::fromStringToStream);
        streamStrean.forEach(s -> s.forEach(System.out::println));
        System.out.println();
        Stream characterStream =  list.stream().flatMap(StreamTest::fromStringToStream);
        characterStream.forEach(System.out::println);
    }

    //将字符串中的多个字符构成的集合转换为对应的Stream的实例
    public static Stream fromStringToStream(String str){
        ArrayList list = new ArrayList<>();
        for (Character character : str.toCharArray()) {
            list.add(character);
        }
        return list.stream();
    }
}

排序

Stream sorted()                                      //产生一个新流,其中按自然顺序排序
Stream sorted(Comparator<? super T> comparator)      //产生一个新流,其中按比较器顺序排序
import java.util.ArrayList;
import java.util.List;

public class StreamTest {
    public static void main(String[] args) {

        List list = new ArrayList<>();
        list.add(new Person("Sakura",9));
        list.add(new Person("Akame",17));
        list.add(new Person("Miloto",13));
        list.add(new Person("Shana",15));
        list.add(new Person("Nanoha",11));

        list.stream().sorted().forEach(System.out::println);
        System.out.println();
        list.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
    }
}
public class Person implements Comparable{
    private String name;
    private int age;

    public Person() {
    }

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

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

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

    public String toString(){
        return "Person{ name:" + name + ",age:" + age + "}";
    }

    @Override
    public int compareTo(Object o) {
        if(o instanceof Person){
            Person person = (Person)o;
            int compare = this.name .compareTo(person.name);
            if(compare != 0){
                return compare;
            } else {
                return Integer.compare(this.age, person.age);
            }
        }
        throw new RuntimeException("传入的数据类型不一致!");
    }
}

4.4、终止操作

??终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。流进行终止操作后,不能再次使用。

匹配与查找

boolean allMatch(Predicate<? super T> predicate)    //检查是否匹配所有元素
boolean anyMatch(Predicate<? super T> predicate)    //检查是否至少匹配一个元素
boolean noneMatch(Predicate<? super T> predicate)   //检查是否没有匹配所有元素
Optional findFirst()                             //返回第一个元素
Optional findAny()                               //返回当前流中的任意元素
long count()                                        //返回流中元素总数
Optional max(Comparator<? super T> comparator)   //返回流中最大值
Optional min(Comparator<? super T> comparator)   //返回流中最小值
void forEach(Consumer<? super T> action)            //内部迭代
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamTest {
    public static void main(String[] args) {

        List list = Arrays.asList(7,17,10,9,11,13,15);

        boolean allMath = list.stream().allMatch(e -> e<13);
        System.out.println(allMath);
        boolean anyMath = list.stream().anyMatch(e -> e<=10);
        System.out.println(anyMath);
        boolean noneMath = list.stream().noneMatch(e -> e==7);
        System.out.println(noneMath);

        Optional first = list.stream().findFirst();
        System.out.println(first);
        Optional any = list.parallelStream().findAny();
        System.out.println(any);

        long count = list.stream().count();
        System.out.println(count);
        Optional max = list.stream().max(Integer::compare);
        System.out.println(max);
        Optional min = list.stream().min((e1,e2) -> Integer.compare(e1, e2));
        System.out.println(min);

        list.stream().forEach(System.out::println);
    }
}

规约

Optional reduce(BinaryOperator accumulator)   //可以将流中元素反复结合起来,得到一个值,返回Optional
T reduce(T identity, BinaryOperator accumulator) //可以将流中元素反复结合起来,得到一个值,返回T
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamTest {
    public static void main(String[] args) {

        List number = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        int sum = number.stream().reduce(10,Integer::sum);
        System.out.println(sum);
        Optional sum1= number.stream().reduce(Integer::sum);
        System.out.println(sum1);
    }
}

收集

 R collect(Collector<? super T,A,R> collector) //将流转换成其它形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
public class StreamTest {
    public static void main(String[] args) {

        List list1 = Arrays.asList(7,17,10,9,11,13,15);
        list1.stream().filter(e -> e<=13).collect(Collectors.toList()).forEach(System.out::println);
        System.out.println();
        list1.stream().filter(e -> e<=10).collect(Collectors.toList()).forEach(System.out::println);
    }
}

五、Optional类

??Optional类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在;或者仅仅保存null值,表示这个值不存在;原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()会返回true,调用get()会返回该对象。

public static  Optional of(T value)           //创建一个Optional实例,t必须非空
public static  Optional empty()               //创建一个空的Optional实例
public static  Optional ofNullable(T value)   //t可以为null
public boolean isPresent()                          //判断是否包含对象
public void ifPresent(Consumer<? super T> action)   //如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它
public T get()                                      //如果调用对象包含值,返回该值,否则抛异常
public T orElse(T other)                            //如果有值则将其返回,否则返回指定的other对象
public T orElseGet(Supplier<? extends T> supplier)  //如果有值则将其返回,否则返回由Supplier接口实现提供的对象
public T orElseThrow()                              //如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
public class OptionalTest {
    public static void main(String[] args) {

        Boy boy = new Boy();
        String grilName = Boy.getGrilName(boy);
        System.out.println(grilName);

        boy = null;
        grilName = Boy.getGrilName(boy);
        System.out.println(grilName);

        boy = new Boy("Akame", 13, new Gril("Mikoto Misaka", 13));
        grilName = Boy.getGrilName(boy);
        System.out.println(grilName);
    }
}
public class Person{
    private String name;
    private int age;

    public Person() {
    }

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

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

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

    public String toString(){
        return "Person{ name:" + name + ",age:" + age + "}";
    }
}
import java.util.Optional;

public class Gril extends Person{
    private Boy boy;
    
    public Gril(){

    }

    public Gril(String name){
        super(name);
    }

    public Gril(String name,int age){
        super(name, age);
    }

    public Gril(String name,int age,Boy boy){
        super(name, age);
        this.boy = boy;
    }

    public void setBoy(Boy boy){
        this.boy = boy;
    }

    public Boy getBoy(){
        return boy;
    }

    public String toString(){
        return "Person{ name:" + getName() + ",age:" + getAge() + "boy:" + boy + "}";
    }

    public static String getBoyName(Gril gril){
        Optional grilOptional = Optional.ofNullable(gril);
        //此时gril1一定非空
        Gril gril1 = grilOptional.orElse(new Gril("Sakura",9,new Boy("Kikyou",9)));
        Boy boy = gril1.getBoy();
        Optional griloOptional = Optional.ofNullable(boy);
        //此时boy1一定非空
        Boy boy1 = griloOptional.orElse(new Boy("Kagome",9));
        return boy1.getName();
    }
}
import java.util.Optional;

public class Boy extends Person{
    private Gril gril;

    public Boy(){

    }

    public Boy(String name){
        super(name);
    }

    public Boy(String name,int age){
        super(name, age);
    }

    public Boy(String name,int age,Gril gril){
        super(name, age);
        this.gril = gril;
    }

    public void setGril(Gril gril){
        this.gril = gril;
    }

    public Gril getGril(){
        return gril;
    }

    public String toString(){
        return "Person{ name:" + getName() + ",age:" + getAge() + "gril:" + gril + "}";
    }

    public static String getGrilName(Boy boy){
        Optional boyOptional = Optional.ofNullable(boy);
        //此时boy1一定非空
        Boy boy1 = boyOptional.orElse(new Boy("Rimuru",10,new Gril("Mikoto",10)));
        Gril gril = boy1.getGril();
        Optional griloOptional = Optional.ofNullable(gril);
        //此时gril1一定非空
        Gril gril1 = griloOptional.orElse(new Gril("Misaka",10));
        return gril1.getName();
    }
}

六、语法改进

6.1、try语句升级

import java.io.IOException;
import java.io.InputStreamReader;

public class Template {
    public static void main(String[] args) {

        //Java 8 中资源关闭操作,可实现资源的自动关闭
        //要求自动关闭的资源的初始化必须放在try的一堆小括号中
        try(InputStreamReader reader = new InputStreamReader(System.in)){
            char[] cbuf = new char[20];
            int len;
            if((len = reader.read(cbuf)) != -1){
                String str = new String(cbuf,0,len);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}