JAVA8 新特性的一些練習和簡單使用 能快速上手

JAVA8 新特性主要內容部分:

  • 1.Lambda 表達式

  • 2.函數式接口

  • 3.方法引用與構造器引用

  • 4.Stream API

  • 5.接口中的默認方法與靜態方法

  • 6.新時間日期API

  • 7.其他新特性

 

1.速度快、代碼少、強大的stream 、便於並行、最大化減少空指針

 

2.哈希算法 hashmap  數組-鏈表-紅黑樹  加載因子0.75

 

3.ConcurrentHashMap  CAS算法  16

 

4.方法區 換成 MetaSpace 元空間  默認用物理內存 也可配置

      取消永久區

 

# 第一部分 Lambda 表達式

lambda: 匿名函數、可傳遞的代碼鏈式編程

Lambda表達式的基礎語法:java8中引入了一個新的操作符 “->” 箭頭操作符將lambda表達式分爲兩部分:

1.左側:參數列表  (接口中對應的抽象方法參數   函數式接口只有一個抽象方法)

2.右側:表達式中所需執行的功能,即lambda體

 

語法格式1:無餐,無返回值

Runnable  r2 = () -> System.out.println("執行了");

語法格式2:有一個參數,且無返回值;小括號可以不寫

Consumer<String> c = (x) -> System.out.println(x);

//Consumer<String> c = x -> System.out.println(x);

 c.accept("666");

語法格式3:有多個參數,有返回值,多條語句;小括號,大括號都不能省略

Comparator<Integer> t = (x,y) -> {

            System.out.println("函數式接口");

            return Integer.compare(x,y);

        };

語法格式4:若lambda體中只有一條語句時,return ,大括號都可以不寫

                    參數列表數據類型可以不寫,jvm 編譯器可以上下文推斷出數據類型,又稱“類型推斷”

Comparator<Integer> t = (x,y)  ->Integer.compare(x,y);

口訣:左右遇一括號省   左側推斷類型省   能省就省

二、Lambda 表達式 需要“函數式接口”的支持

函數式接口:接口中只有一個抽象方法的接口,稱爲函數式接口。可以使用註解@FunctionInterface 修飾,它可以檢測是不是函數式接口

練習:

///1

List<Emloyee> emps = Arrays.asList(

    new Emloyee(11,"zhangsan","6666"),      
    new Emloyee(22,"李四","4444"),      
    new Emloyee(55,"李四5","54444"),    
    new Emloyee(33,"王五","55555555"),      
    new Emloyee(33,"王五2","2222555"),      
    new Emloyee(44,"zhaoliu","34125")       
    );

    @Test

    public void test() {
        Collections.sort(emps,(e,e2)->
        e.getAge()==e2.getAge() ?
        e.getName().compareTo(e2.getName()) :
        Integer.compare(e.getAge(),e2.getAge())
     );

       for (Emloyee e : emps) {
            System.out.println(e);
        }
    }

///2

public interface StringOpt {
    public String opt(String opt);
}



    // 字符串處理
    @Test
    public void test2() {
        String s = strHandler("\t\t  sfda dfdf  ", x -> x.trim() );
        System.out.println(s);
        String s2 = strHandler("abcde", x->  x.substring(2));
        System.out.println(s2);
    }

    public String strHandler(String  str,StringOpt so) {
        return so.opt(str);
    }

public interface MyFunction<T,R> {
    public R getValue(T t1,T t2);
}

    // 對於兩個Long 型數據處理
    @Test
    public void test3() {
        op(100L,200L,(x,y) -> x + y);
        op(100L,200L,(x,y) -> x * y);
    }
    public  void op(Long l1,Long  l2,MyFunction<Long,Long> mf) {
        System.out.println(mf.getValue(l1, l2));
    }



@FunctionalInterface
public interface TestLambda<T> {
    public boolean test(T t);
}

2.1java 內置四大核心函數式接口
Consumer<T> 消費型接口             void   accept(T t); 
// 方式1
        Consumer<String> cs = new  Consumer<String>() {
            @Override
            public void accept(String t) {
                // TODO Auto-generated method  stub
                System.out.println(t);
            }
        };
        cs.accept("aaaaaaaaaaa");
        // Lambda方式2
        Consumer<String> cs1 = (x)->  System.out.println(x);
        cs1.accept("6666666666");


Supplier<T>    供給型接口                T     get();

    //Supplier<T>    供給型接口                T      get();
    @Test
    public void test4() {
        List<Integer> num = getNumList(10, ()  ->(int)(Math.random()*100));
        System.out.println(num);
    }

    ///需求 產生指定個數的整數,存放到集合中

    public List<Integer> getNumList(int  num,Supplier<Integer> sup){
        List<Integer> list = new  ArrayList<Integer>();
        for(int i = 0;i < num; i++) {
            Integer n = sup.get();
            list.add(n);
        }
        return list;
    }



Function<T,R>   函數型接口               R apply(T t);
//Function<T,R>   函數型接口               R  apply(T t);
    @Test
    public void test5() {
        String i = strHandler1("sssabfjd/t113",  x -> x.substring(0, 1));
        System.out.println(i);
    }

    //需求處理字符串
    public String strHandler1(String  str,Function<String, String>f) {
        return f.apply(str);
    }

Predicate<T>     斷言型接口                boolean test(T t);                      
// Predicate<T>     斷言型接口                 boolean test(T t);                      
    @Test
    public void test6() {
        List<String> s =  Arrays.asList("aa","hh","fhkaf","很好的");
        List<String> strList =  optString(s,x->x.length()>2);
        System.out.println(strList);
    }

    
    /// 將滿足條件的字符串放到集合中
    public List<String> optString(List<String>  list,Predicate<String>pre){
        List<String> strList = new  ArrayList<>();
        for (String s : list) {
            if(pre.test(s)) {
                strList.add(s);
            }
        }
        return strList;
    }

    

 

常用的其他函數式接口:

BiFuntion<T,U,R>       R apply(T t,U u);

UnaryOperatot<T>     T apply(T t);  它是Function 的子接口

BinaryOperator<T>    void accept(T t1,T t2 );  是BiFunction 的子接口

BiConsumer<T,U>       void  accept(T t,U u);

 

 

三、  3.1方法引用

方法引用:若Lambda體中的內容有方法已經實現了,我們可以使用“方法引用”(可以理解爲方法引用是Lambda 表達式的另一種表現形式)

主要有三種語法格式:

對象:: 實例方法名

類ishil :: 靜態方法名

類 :: 實例方法名

 

注意:1. lambda 體中調用方法的參數列表與返回值類型,要與函數式接口這種抽象方法的函數列表與返回值一致

  1. 如果lambda 參數列表第一個是實例方法的調用者,而第二個參數是實例方法的參數時,可以使用 ClassName:: method  如:      

   BiPredicate<String, String> bp = (x,y)  -> x.equals(y);        BiPredicate<String, String> bp2 =  String::equals;

// 對象
    @Test
    public void test7() {
        PrintStream ps = System.out;
        Consumer<String> con = x ->  ps.println(x);
        con.accept("aaa");
        
        // 簡化  左右類型要一致
        Consumer<String> con2  =System.out::println;
        con2.accept("6666");
    }
    
    @Test
    public void test8() {
        Emloyee e = new Emloyee();
        Supplier<String> sup = e::getName;
        String name = sup.get();
        System.out.println(name);
    }
    

    // 類--靜態方法
    @Test
    public void test9() {
        Comparator<Integer> com =   Integer::compareTo;
        int c = com.compare(22, 11);
        System.out.println(c); // 1
    }



// 類--實例方法
    @Test
    public void test91() {
        BiPredicate<String, String> bp = (x,y)  -> x.equals(y);
        BiPredicate<String, String> bp2 =  String::equals;
        System.out.println(bp.test("11","aa"));
    }

 

2.3  構造器引用

ClassName::new

注意;需要調用的構造器參數列表要與函數值接口中參數列表一致;

 @Test
    public void test2() {
        Supplier<Employee> sup = () -> new  Employee();
        // 構造器引用
        Supplier<Employee> sup2 = Employee::new;
        Employee e = sup2.get();
        System.out.println(e); //Employee [id=0,  age=0, name=null, salary=null]
    }

3.2  數組引用

Type::new;

    // 數組引用
    @Test
    public void test3() {
        Function<Integer,String[]> fun = (x) -> new  String[x];  
        String[] strs = fun.apply(4);
        System.out.println(strs.length); //4
        
        Function<Integer,String[]> fun2 =  String[]::new;
        String[] str2 = fun2.apply(6);
        System.out.println(str2.length);//6
    }

四 Stream API 流

問題:什麼是流Sream?

是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。“集合講的是數據,流講的是計算。”

① Stream 自己不會存儲元素。

② Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。

③ Stream 是延遲執行的。 這就意味這他們會等到需要的結果時才執行。

 

三步走:

1.創建stream

    // 1. 可以通過 Collection系列集合框架 提供  xxx.stream() 或者parallelStream()

    // 2. 通過Arrays 中的靜態方法stram() 獲取數組流

    // 3. 通過Stream類中的靜態方法of()

    // 4. 創建無限流(迭代、生成)

       

         /*
         * 創建流對象
         */
        // 1. 可以通過 Collection系列集合框架 提供  xxx.stream() 或者parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();
        // 2. 通過Arrays 中的靜態方法stram() 獲取數組流
        Employee[] emps = new Employee[5];
        Stream<Employee> stream2 =  Arrays.stream(emps);
        // 3. 通過Stream類中的靜態方法of()
        Stream<String> stream3 =  Stream.of("aa","bb","cc");
        // 4. 創建無限流(迭代、生成)
        //迭代
        Stream<Integer> stream4 =  Stream.iterate(0, x-> x+2);
        stream4.limit(4).forEach(System.out::println);
        
        //生成
        Stream.generate(()->Math.random())
        .limit(4)
        .forEach(System.out::println);

2.中間操作(多箇中間操作可以連接起來形成一個流水線,只有流水線上有觸發終止操作的情況,否則中間操作不會執行任何的處理,終止操作時會一次性全部處理,也稱“惰性求值”)

常用的一些中間操作:

篩選與切片

    filter---接收 Lambda,從流中排除某些元素

    limit---截斷流,使其元素不超過某個指定數量

    skip(n) --- 跳過元素,返回一個扔掉前n 個元素的流 ,若元素不足n 個,則返回一個空流,與limit(n)互補

    distinct--- 篩選,通過流所生成元素的hashCode() 和 equals() 去除重複元素

 映射

      map--接收Lambda ,將元素轉換成其他形式或提取信息。接收一個函數作爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素。

      flatMap--接收一個函數作爲參數,將該中的每一個值都換成另一個流,然後把所有流連接成一個流

排序

     sorted() -- 自然排序(Comparable)

     sorted(Comparator com) -- 自定義排序(Comparator)


// filter

//  添加公司員工
    List<Employee> list = Arrays.asList(
        new Employee("zhangsan", 33, 8888),
        new Employee("zhangsan2", 35, 18888),
        new Employee("zhangsan3", 37, 28888),
        new Employee("zhangsan4", 43, 34888),
        new Employee("zhangsan5", 53, 88888)
            );
    
    // 屬於內部迭代  由Stream API 完成
    @Test
    public void test() {
        //中間操作,不會執行
        Stream<Employee> stream = list.stream()
                .filter(e -> {
                    System.out.println("Stream  中間操作");
                    return e.getAge() > 36;
                });
        // 惰性求值
        stream.forEach(System.out::println);
    }
    // 外部迭代
    @Test
    public void test2() {
        Iterator<Employee> it = list.iterator();
        
        while(it.hasNext()) {
            Employee e = it.next();
            if(e.getAge() > 36) {
                System.out.println(e);
            }
        }
    }


// limit

@Test
    public void test3() {
        list.stream()
        .filter(e -> {
            System.out.println("短路"); //&&  ||
            return e.getAge() > 36;
        })
        .limit(2)
        .forEach(System.out::println);
    }
    
//skip

@Test
    public void test4() {
        list.stream()
        .filter(e -> e.getSalary() > 5000)
        .skip(2)
        .forEach(System.out::println);
    }
// distinct 通過流所生成元素的hashCode() 和 equals() 去除重複元素

@Test
    public void test4() {
        list.stream()
        .filter(e -> e.getSalary() > 5000)
        .skip(2)
        .distinct()
        .forEach(System.out::println);
    }
// map

@Test
    public void test5() {
        List<String> list =  Arrays.asList("aa","bb","cc","ddd");
        list.stream()
            .map(str-> str.toUpperCase())
            .forEach(System.out::println);
        
        System.out.println("-----------------");
        
        employee.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
                
    }
////
AA
BB
CC
DDD
-----------------
zhangsan
zhangsan2
zhangsan3
zhangsan4
zhangsan6
zhangsan5
zhangsan5
// flatMap

    // 方式1
        Stream<Stream<Character>> stream =  list.stream()
            .map(LambdaStream::filterCharacter);   //[a,b,c,[d,e]] 相當於集合中add()方法
        
        stream.forEach(sm ->  sm.forEach(System.out::println));
        
        System.out.println("-----------------");
        // 方式2
        Stream<Character> stream2 =  list.stream()
        .flatMap(LambdaStream::filterCharacter);   //[a,b,c,d,e] 相當於集合中addAll()方法
        
        stream2.forEach(System.out::println);
// sorted

/*
     * 排序
     * sorted() -- 自然排序(Comparable)
     * sorted(Comparator com) -- 自定義排序(Comparator)
     */
    @Test
    public void test6() {
        List<String> list =  Arrays.asList("aa","dd","bb","1");
        list.stream()
        .sorted()
        .forEach(System.out::println);
        
        System.out.println("--------------------------");
        
        employee.stream()
        .sorted((e1,e2) ->{
                if(e1.getAge().equals(e2.getAge()))
                    return  e1.getName().compareTo(e2.getName());
                else
                    return  e1.getAge().compareTo(e2.getAge());
            }).forEach(System.out::println);
    }

//////////
1
aa
bb
dd
--------------------------
Employee [name=zhangsan, age=33, salary=8888]
Employee [name=zhangsan2, age=35, salary=18888]
Employee [name=zhangsan3, age=37, salary=28888]
Employee [name=zhangsan4, age=43, salary=34888]
Employee [name=zhangsan5, age=53, salary=88888]
Employee [name=zhangsan5, age=53, salary=88888]
Employee [name=zhangsan6, age=53, salary=88888]

 

3.終止操作  (可以像sql 一樣 優雅編程 )

    查找與匹配

                 *  allMatch --  檢查是否匹配所有元素

                 *  anyMatch --  檢查是否至少匹配一個元素

                 *  noneMatch -- 檢查是否沒有匹配所有元素

                 *  findFrist -- 返回第一個元素

                 *  findAny -- 返回當前流中的任意元素

                 *  count -- 返回流中元素總個數

                 *  max -- 返回流中最大值

                 *  min -- 返回流中最小值

     歸約

             * reduce(T identity,BinaryOpertor) /  reduce(BinaryOperator) -- 可以將流中元素反覆結合起來,得到一個值

                            "map- reduce" 模式  兩個結合使用

    收集 

                collect -- 將流轉換爲其他流。接收一個Collector 接口的實現,用於給Stream 中元素做彙總的方法   

                提取信息聚合 求最大值、最小值、和... ;  

                分組、多級分組、彙總統計、提取信息  連接成字符串



/*
     *  查找與匹配
     *  allMatch --  檢查是否匹配所有元素
     *  anyMatch --  檢查是否至少匹配一個元素
     *  noneMatch -- 檢查是否沒有匹配所有元素
     *  findFrist -- 返回第一個元素
     *  findAny -- 返回當前流中的任意元素
     *  count -- 返回流中元素總個數
     *  max -- 返回流中最大值
     *  min -- 返回流中最小值
     */
    @Test
    public void test2() {
        long count = employee.stream().count();
        System.out.println(count);
        
        Optional<Employee> max =  employee.stream()
                .max((e1,e2) ->  Integer.compare(e1.getSalary(),e1.getSalary()));
        System.out.println(max.get());
        
        Optional<Integer> min =  employee.stream()
                .map(Employee::getSalary)
                .min(Integer::compare);
        System.out.println(min.get());
    }
    @Test
    public void test1() {
        boolean b1 = employee.stream()
                .allMatch((e) ->  e.getStatus().equals(Status.BUSY));
        System.out.println(b1); // false
        
        boolean b2 = employee.stream()
                .anyMatch((e) ->  e.getStatus().equals(Status.FREE));
        System.out.println(b2); // true
        
        boolean b3 = employee.stream()
                .noneMatch((e) ->  e.getStatus().equals(Status.VOCATION));
        System.out.println(b3); // false
        
        Optional<Employee> op =  employee.stream()
                .sorted((e1,e2) ->  -Integer.compare(e1.getSalary(),  e2.getSalary()))
                .findFirst(); // 有可能空指針  所以 java8  封裝到Optional 中
        System.out.println(op.get());
        
        Optional<Employee> op2 =  employee.parallelStream()  // stream() 依次執行   parallelStream  並行同時執行
                .filter(e ->  e.getStatus().equals(Status.FREE)) //  parallelStream 由於並行 所以結果不唯一 隨機
                .findAny();
        System.out.println(op2.get());
    }


collect -- 將流轉換爲其他流。接收一個Collector 接口的實現,用於給Stream 中元素做彙總的方法

/*
     * 收集
     *  collect -- 將流轉換爲其他流。接收一個Collector 接口的實現,用於給Stream 中元素做彙總的方法
     */
    @Test
    public void test4() {
        List<String> list = employee.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("------------------------");
        
        Set<Status> set = employee.stream()
            .map(Employee::getStatus)
            .collect(Collectors.toSet());
        set.forEach(System.out::println);
        
        System.out.println("------------------------");
        
        HashSet<Status> hashset =  employee.stream()
                .map(Employee::getStatus)
                .collect(Collectors.toCollection(HashSet::new));
        
        System.out.println(hashset); //[BUSY,  VOCATION, FREE]
        
        
        System.out.println("------------------------");
        // 總數
        Long count = employee.stream()
                .collect(Collectors.counting());
        System.out.println(count); // 7
        
        // 平均值
         Double avg = employee.stream()
                .collect(Collectors.averagingInt(Employee::getSalary));
        System.out.println(avg);
        
        // 最大值
        Optional<Employee> op =  employee.stream()
                .collect(Collectors.maxBy((e1,e2) ->  Double.compare(e1.getSalary(),  e2.getSalary())));
        System.out.println(op.get());
        
        // 最小值
        Optional<Integer> op2 =  employee.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Integer::compare));
        System.out.println(op2.get());
    }
    


// 多級分組
    @Test
    public void test6() {
        Map<Status, Map<String, List<Employee>>>  map = employee.stream()
                .collect(Collectors.groupingBy(Employee::getStatus,
                        Collectors.groupingBy(e  -> {
                            if(e.getAge() <=35)  return "青年";
                            else if (e.getAge()  <=50 ) return "中年";
                            else return "老年";
                        })));
        System.out.println(map);
        //{VOCATION={中年=[Employee  [name=zhangsan3, age=37, salary=28888,  status=VOCATION]]}, FREE={青年=[Employee  [name=zhangsan, age=33, salary=8888,  status=FREE]], 中年=[Employee [name=zhangsan4,  age=43, salary=34888, status=FREE]]}, BUSY={青年=[Employee [name=zhangsan2, age=35,  salary=18888, status=BUSY]]}}
    }
    // 分組
    @Test
    public void test5() {
        Map<Status, List<Employee>> map =  employee.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
        //{BUSY=[Employee [name=zhangsan2,  age=35, salary=18888, status=BUSY]],  FREE=[Employee [name=zhangsan, age=33,  salary=8888, status=FREE], Employee  [name=zhangsan4, age=43, salary=34888,  status=FREE]], VOCATION=[Employee  [name=zhangsan3, age=37, salary=28888,  status=VOCATION]]}
    }


//  添加公司員工
    List<Employee> employee = Arrays.asList(
        new Employee("zhangsan", 33,  8888,Status.FREE),
        new Employee("zhangsan2", 35,  18888,Status.BUSY),
        new Employee("zhangsan3", 37,  28888,Status.VOCATION),
        new Employee("zhangsan4", 43,  34888,Status.FREE)
    );
    
    /*
     * 收集
     *  collect -- 將流轉換爲其他流。接收一個Collector 接口的實現,用於給Stream 中元素做彙總的方法
     */
    // 提取信息  連接成字符串
    @Test
    public void test9() {
        String c = employee.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",",">>>","<<<"));
        System.out.println(c);
    }
    //彙總  統計
    @Test
    public void test8() {
        IntSummaryStatistics collect =  employee.stream()
                .collect(Collectors.summarizingInt(Employee::getSalary));
        System.out.println(collect.getAverage());
        System.out.println(collect.getMax());
    }
    // 分區
    @Test
    public void test7() {
        Map<Boolean, List<Employee>> map =  employee.stream()
                .collect(Collectors.partitioningBy(e ->  e.getSalary() > 10000));
        System.out.println(map);//{false=[Employee  [name=zhangsan, age=33, salary=8888,  status=FREE]], true=[Employee [name=zhangsan2,  age=35, salary=18888, status=BUSY], Employee  [name=zhangsan3, age=37, salary=28888,  status=VOCATION], Employee [name=zhangsan4,  age=43, salary=34888, status=FREE]]}
    }
    // 多級分組
    @Test
    public void test6() {
        Map<Status, Map<String, List<Employee>>>  map = employee.stream()
                .collect(Collectors.groupingBy(Employee::getStatus,
                        Collectors.groupingBy(e  -> {
                            if(e.getAge() <=35)  return "青年";
                            else if (e.getAge()  <=50 ) return "中年";
                            else return "老年";
                        })));
        System.out.println(map);
        //{VOCATION={中年=[Employee  [name=zhangsan3, age=37, salary=28888,  status=VOCATION]]}, FREE={青年=[Employee  [name=zhangsan, age=33, salary=8888,  status=FREE]], 中年=[Employee [name=zhangsan4,  age=43, salary=34888, status=FREE]]}, BUSY={青年=[Employee [name=zhangsan2, age=35,  salary=18888, status=BUSY]]}}
    }
    // 分組
    @Test
    public void test5() {
        Map<Status, List<Employee>> map =  employee.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
        //{BUSY=[Employee [name=zhangsan2,  age=35, salary=18888, status=BUSY]],  FREE=[Employee [name=zhangsan, age=33,  salary=8888, status=FREE], Employee  [name=zhangsan4, age=43, salary=34888,  status=FREE]], VOCATION=[Employee  [name=zhangsan3, age=37, salary=28888,  status=VOCATION]]}
    }
    @Test
    public void test4() {
        List<String> list = employee.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("------------------------");
        
        Set<Status> set = employee.stream()
            .map(Employee::getStatus)
            .collect(Collectors.toSet());
        set.forEach(System.out::println);
        
        System.out.println("------------------------");
        
        HashSet<Status> hashset =  employee.stream()
                .map(Employee::getStatus)
                .collect(Collectors.toCollection(HashSet::new));
        
        System.out.println(hashset); //[BUSY,  VOCATION, FREE]
        
        
        System.out.println("------------------------");
        // 總數
        Long count = employee.stream()
                .collect(Collectors.counting());
        System.out.println(count); // 7
        
        // 平均值
         Double avg = employee.stream()
                .collect(Collectors.averagingInt(Employee::getSalary));
        System.out.println(avg);
        
        // 最大值
        Optional<Employee> op =  employee.stream()
                .collect(Collectors.maxBy((e1,e2) ->  Double.compare(e1.getSalary(),  e2.getSalary())));
        System.out.println(op.get());
        
        // 最小值
        Optional<Integer> op2 =  employee.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Integer::compare));
        System.out.println(op2.get());
    }
    

 

/////////////////應用場景

1.用 Comparator 來排序

2.用 Runnable 執行代碼塊

3.GUI 事件處理

 

未完待續.....

共同進步,共同學習

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章