[Java筆記]day29

臨潼言承旭發表於2020-11-28

day29

review

  1. 獲取Class例項的三種方式
  • .class
  • getClass()
  • Class.forName(String classPath)
  1. 總結:建立類的方式
  • new + 構造器
  • 可以考慮:Xxx,Xxxs,XxxFactory,XxxBuilder類中檢視是否有靜態方法
  • 反射

動態代理

  1. 複習:靜態代理
public class StaticProxyTest {
    public static void main(String[] args) {
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ProxyClothFactory proxyClothFactory = new 			ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

interface ClothFactory {
    void produceCloth();
}

// 代理類
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;
    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }
    @Override
    public void produceCloth() {
        System.out.println("代理工廠做了一些準備工作");
        factory.produceCloth();
        System.out.println("代理工廠做了一些收尾工作");
    }
}

// 被代理類
class NikeClothFactory implements ClothFactory{
    @Override
    public void produceCloth() {
        System.out.println("Nike工廠生產一批運動服");
    }
}
  1. 動態代理

思考:

  1. 如何根據載入到記憶體中的被代理類,動態地建立一個代理類及物件?
  2. 當通過代理類的物件呼叫方法時,如何動態地呼叫被代理類中的同名方法?
package com.yanchengxu.java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author : yanchengxu
 * @create : 2020/11/26 23:59
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("麻辣燙");
    }
}

interface Human {
    String getBelief();
    void eat(String food);
}

// 被代理類
class SuperMan implements Human {
    @Override
    public String getBelief() {
        return "I believe I can fly~";
    }
    @Override
    public void eat(String food) {
        System.out.println("我喜歡吃" + food);
    }
}


class ProxyFactory {
    /**
     * 返回一個代理類的物件
     * @param obj 被代理類物件
     * @return 代理類的物件
     */
    public static Object getProxyInstance(Object obj) {
        Class<?> cls = obj.getClass();
        ClassLoader loader = cls.getClassLoader();
        Class<?>[] interfaces = cls.getInterfaces();
        MyInvocationHandler handler = new MyInvocationHandler();
        // 繫結
        handler.bind(obj);
        // 動態生成代理類物件
        Object proxyInstance = Proxy.newProxyInstance(loader, interfaces, handler);
        return proxyInstance;
    }
}

class MyInvocationHandler implements InvocationHandler {
    /**
     * 需要使用被代理類進行賦值
     */
    private Object obj; 

    /**
     * 繫結
     * @param obj 被代理類物件
     */
    public void bind(Object obj) {
        this.obj = obj;
    }
    /**
     *
     * @param proxy 代理類的物件
     * @param method 代理類物件呼叫的方法
     * @param args 代理類物件呼叫的方法的引數
     * @return 代理類物件呼叫的方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnVal = method.invoke(obj, args);
        return returnVal;
    }
}

Lambda表示式

  1. 格式:(形參列表) -> 方法體
  • ->:lambda操作符
  • 左邊:lambda形參列表,對應介面中抽象方法的形參列表
  • 右邊:lambda體,對應重寫的抽象方法的方法體
  1. 格式
  • 無參無返回值
  • 有引數無返回值
  • 引數型別都可以省略,型別推斷
  • 若只有一個引數,可以省略()
  • 需要兩個及以上的引數,多條執行語句,並且可以有返回值
  • 只有一條語句,若有return{}則可以省略

總結:

  • 形參列表
    • 形參列表的引數型別可以省略
    • 如果只有一個引數,()也可以省略
  • Lambda體:
    • 多條語句應該用{}包裹
    • 如果只有一條執行語句,則可以省略{}
    • 當僅有一條return語句時,省略{}是則必須省略return
  1. 本質

作為函式式介面的例項

函式式介面

  1. 如果介面中只有一個抽象方法,則稱之為函式式介面

  2. Java內建四大核心函式式介面

在這裡插入圖片描述

應用舉例

public class FunctionalTest {
    @Test
    public void testConsumer() {
        happyTime(999.99, money -> System.out.println("今天花了" + money + "元"));
    }
    public void happyTime(double money, Consumer<Double> con) {
        con.accept(money);
    }
    @Test
    public void testPredicate() {
        List<String> list = Arrays.asList("北京", "天津", "上海", "重慶", "東京");
        List<String> strings = filter(list, s -> s.contains("京"));
        System.out.println(strings);
    }
    public List<String> filter(List<String> list, Predicate<String> pre) {
        ArrayList<String> arrayList = new ArrayList<>();
        for (String s : list) {
            if (pre.test(s)) {
                arrayList.add(s);
            }
        }
        return arrayList;
    }
}
  1. 其他介面

在這裡插入圖片描述

方法引用

  1. 適用情景:當要傳給Lambda體的操作,已經有實現的方法了,此時可以適用方法引用

  2. 本質:方法引用本質上是Lambda表示式

由上文,Lambda表示式本質上是函式式介面的例項,故方法引用也是函式式介面的例項

  1. 使用格式: 類(物件) :: 方法名

  2. 使用要求:

要求介面中的抽象方法的形參列表和返回值與方法引用的方法的形參列表和返回值相同

  1. 分類:
  • 物件::非靜態方法
  • 類::靜態方法
  • 類::非靜態方法
  1. 舉例
public class  MethodRefTest {
	// 情況一:物件 :: 例項方法
	//Consumer中的void accept(T t)
	//PrintStream中的void println(T t)
	@Test
	public void test1() {
		Consumer<String> con1 = str -> System.out.println(str);
		con1.accept("Lambda");
		System.out.println("--------------------------");
		PrintStream ps = System.out;
		Consumer<String> con2 = ps :: println;
		con2.accept("MethodReference");
	}
	//Supplier中的T get()
	//Employee中的String getName()
	@Test
	public void test2() {
		Employee pony = new Employee(1001, "Pony", 23, 9999);
		Supplier<String> sup = () -> pony.getName();
		System.out.println(sup.get());
		System.out.println("-------------------------");
		Supplier<String> sup2 = pony :: getName;
		System.out.println(sup2.get());

	}
	// 情況二:類 :: 靜態方法
	//Comparator中的int compare(T t1,T t2)
	//Integer中的int compare(T t1,T t2)
	@Test
	public void test3() {
		Comparator<Integer> com = (t1, t2) -> Integer.compare(t1, t2);
		System.out.println(com.compare(34, 43));
		System.out.println("-------------");
		Comparator<Integer> com2 = Integer::compare;
		System.out.println(com2.compare(34, 42));
	}
	//Function中的R apply(T t)
	//Math中的Long round(Double d)
	@Test
	public void test4() {
		Function<Double, Long> func1 = d -> Math.round(d);
		System.out.println(func1.apply(13.4));
		System.out.println("---------------------------");
		Function<Double, Long> func2 = Math::round;
		System.out.println(func2.apply(13.5));
	}
	// 情況三:類 :: 例項方法 
	// Comparator中的int comapre(T t1,T t2)
	// String中的int t1.compareTo(t2)
	@Test
	public void test5() {
		Comparator<String> com1 = (t1, t2) -> t1.compareTo(t2);
		System.out.println(com1.compare("abc", "abd"));
		System.out.println("--------------------");
		Comparator<String> com2 = String::compareTo;
		System.out.println(com2.compare("abd", "abc"));
	}
	//BiPredicate中的boolean test(T t1, T t2);
	//String中的boolean t1.equals(t2)
	@Test
	public void test6() {
		BiPredicate<String, String> pre1 = (t1, t2) -> t1.equals(t2);
		System.out.println(pre1.test("abc", "abd"));
		System.out.println("--------------------------");
		BiPredicate<String, String> pre2 = String::equals;
		System.out.println(pre2.test("abc", "abc"));
	}
	// Function中的R apply(T t)
	// Employee中的String getName();
	@Test
	public void test7() {
		Employee pony = new Employee(1001, "Pony", 23, 34935);
		Function<Employee, String> func1 = e -> e.getName();
		System.out.println(func1.apply(pony));
		System.out.println("-----------------");
		Function<Employee, String> func2 = Employee::getName;
		System.out.println(func2.apply(pony));
	}
}

構造器引用和陣列引用

  1. 構造器引用

和方法引用類似,函式式介面的抽象方法的形參列表與構造器的形參列表一致
抽象方法的返回值型別即為構造器所屬的類

  1. 陣列引用

將陣列看作一個特殊的類,則使用方法與構造器引用一致

  1. 舉例
public class ConstructorRefTest {
	//構造器引用
    //Supplier中的T get()
    //Employee中的空參構造器Employee()
    @Test
    public void test1(){
        Supplier<Employee> sup1 = () -> new Employee();
        System.out.println(sup1.get());
        System.out.println("----------------");
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());
    }
	//Function中的R apply(T t)
    @Test
    public void test2(){
        Function<Integer, Employee> func1 = id -> new Employee(id);
        System.out.println(func1.apply(1001));
        System.out.println("----------------");
        Function<Integer, Employee> func2 = Employee::new;
        System.out.println(func2.apply(1002));
	}
	//BiFunction中的R apply(T t,U u)
    @Test
    public void test3(){
        BiFunction<Integer, String, Employee> func1 = (id, name) -> new Employee(id, name);
        System.out.println(func1.apply(1001, "Tom"));
        System.out.println("-------------------------");
        BiFunction<Integer, String, Employee> func2 = Employee::new;
        System.out.println(func2.apply(1002, "Pony"));
    }
	//陣列引用
    //Function中的R apply(T t)
    @Test
    public void test4(){
        Function<Integer, String[]> func1 = length -> new String[length];
        System.out.println(Arrays.toString(func1.apply(5)));
        System.out.println("---------------------");
        Function<Integer, String[]> func2 = String[] :: new;
        System.out.println(Arrays.toString(func2.apply(3)));
    }
}

Stream API

  1. Stream是資料渠道,用於運算元據源(集合、陣列等)所生成的元素序列。

集合講的是資料,Stream講的是計算

①Stream 自己不會儲存元素。
②Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream。
③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。

  1. Stream使用步驟

在這裡插入圖片描述

  • 建立Stream

常用方法

方法原型說明
Collectiondefault Stream stream()返回一個順序流
Collectiondefault Stream parallelStream()返回一個並行流,這裡並行的意思是返回的資料順序可能與輸入的不一致
Arraysstatic Stream stream(T[] array)返回一個流
Streampublic static Stream of(T… values)靜態方法,可以接收任意數量的引數
Streampublic static Stream iterate(final T seed, final UnaryOperator f)靜態方法,建立無限流
Streampublic static Stream generate(Supplier s)靜態方法,建立無限流

使用舉例

@Test
public void testCreateStream() {
    List<Employee> employees = EmployeeData.getEmployees();
    // try1 通過集合建立
    Stream<Employee> stream = employees.stream();  // 順序流
    Stream<Employee> parallelStream = employees.parallelStream();  // 並行流
    // try2 通過陣列建立
    int arr[] = new int[]{1, 3, 4, 5};
    IntStream stream1 = Arrays.stream(arr);  
    // try3 通過Stream.of
    Stream<Integer> stream2 = Stream.of(1, 3, 4, 5);
    // try4 建立無限流
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
    System.out.println("----------------");
    Stream.generate(Math::random).limit(5).forEach(System.out::println);
}
  • 中間操作

常用方法

方法原型說明
Stream filter(Predicate<? super T> predicate)接收Lambda,過濾元素
Stream limit(long maxSize)截斷流,使其元素不超過給定數量maxSize
Stream skip(long n)跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流,與 limit(n) 互補
Stream distinct()通過流所生成元素的 hashCode() 和 equals() 去除重複元素
Stream map(Function<? super T, ? extends R> mapper)接收一個函式作為引數,將元素轉換成其他形式或提取資訊,該函式會被應用到每個元素上,並將其對映成一個新的元素
Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流
Stream sorted(Comparator<? super T> comparator)按comparator定製排序,沒有引數時為自然排序

在這裡插入圖片描述

使用舉例

@Test
public void testIntermediate1() {
    List<Employee> list = EmployeeData.getEmployees();
    Stream<Employee> stream = list.stream();
    // filter
    stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
    System.out.println("----------------------");
    // limit
    list.stream().limit(3).forEach(System.out::println);
    System.out.println("----------------------");
    // skip
    list.stream().skip(3).forEach(System.out::println);
    System.out.println("----------------------");
    // distinct
    list.add(list.get(0));
    list.add(list.get(0));
    list.add(list.get(0));
    list.stream().forEach(System.out::println);
    System.out.println("----------------------");
    list.stream().distinct().forEach(System.out::println);
    System.out.println("----------------------");
}
@Test
public void testIntermediate2() {
    List<String> list = Arrays.asList("aab", "bbdsf", "ccsf");
    // map
    list.stream().map(String::length).filter(len -> len > 3)
        .forEach(System.out::println);
    System.out.println("----------------------");
    Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest::toStream);
    streamStream.forEach(s -> s.forEach(System.out::println));
    System.out.println("----------------------");
    // flatMap
    Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::toStream);
    characterStream.forEach(System.out::println);
    System.out.println("----------------------");
    // sort
    List<Employee> employees = EmployeeData.getEmployees();
    employees.stream().sorted((e1, e2) -> {
        int compareAge = Integer.compare(e1.getAge(), e2.getAge());
        if (compareAge == 0) {
            return Double.compare(e1.getSalary(), e2.getSalary());
        }
        return compareAge;
    }).forEach(System.out::println);
}
public static Stream<Character> toStream(String str) {
    ArrayList<Character> list = new ArrayList<>();
    for (Character c : str.toCharArray()) {
        list.add(c);
    }
    return list.stream();
}

彩蛋

@Test
public void testInterviewFromWWT() {
    // 一行求1到100的和
    System.out.println(IntStream.rangeClosed(1, 100).sum());
}
  • 終止操作

常用方法

方法原型說明
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)內部迭代;使用Collection介面需要使用者去做迭代,稱為外部迭代
T reduce(T identity, BinaryOperator accumulator)可以將流中元素反覆結合起來,得到一個值。返回 T,identity為初始值
Optional reduce(BinaryOperator accumulator)可以將流中元素反覆結合起來,得到一個值。返回 Optional
<R, A> R collect(Collector<? super T, A, R> collector)將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法

應用舉例

public class StreamAPITest2 {
    //1-匹配與查詢
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
//        allMatch(Predicate p)——檢查是否匹配所有元素。
//          練習:是否所有的員工的年齡都大於18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);
//        anyMatch(Predicate p)——檢查是否至少匹配一個元素。
//         練習:是否存在員工的工資大於 10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);
//        noneMatch(Predicate p)——檢查是否沒有匹配的元素。
//          練習:是否存在員工姓“雷”
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);
//        findFirst——返回第一個元素
        Optional<Employee> employee = employees.stream().findFirst();
        System.out.println(employee);
//        findAny——返回當前流中的任意元素
        Optional<Employee> employee1 = employees.parallelStream().findAny();
        System.out.println(employee1);
    }
    @Test
    public void test2(){
        List<Employee> employees = EmployeeData.getEmployees();
        // count——返回流中元素的總個數
        long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);
//        max(Comparator c)——返回流中最大值
//        練習:返回最高的工資:
        Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
        Optional<Double> maxSalary = salaryStream.max(Double::compare);
        System.out.println(maxSalary);
//        min(Comparator c)——返回流中最小值
//        練習:返回最低工資的員工
        Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(employee);
        System.out.println();
//        forEach(Consumer c)——內部迭代
        employees.stream().forEach(System.out::println);
        //使用集合的遍歷操作
        employees.forEach(System.out::println);
    }
    //2-歸約
    @Test
    public void test3(){
//        reduce(T identity, BinaryOperator)——可以將流中元素反覆結合起來,得到一個值。返回 T
//        練習1:計算1-10的自然數的和
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.stream().reduce(0, Integer::sum);
        System.out.println(sum);
//        reduce(BinaryOperator) ——可以將流中元素反覆結合起來,得到一個值。返回 Optional<T>
//        練習2:計算公司所有員工工資的總和
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
//        Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
        Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
        System.out.println(sumMoney.get());
    }
    //3-收集
    @Test
    public void test4(){
//        collect(Collector c)——將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法
//        練習1:查詢工資大於6000的員工,結果返回為一個List或Set
        List<Employee> employees = EmployeeData.getEmployees();
        List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
        employeeList.forEach(System.out::println);
        System.out.println();
        Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
        employeeSet.forEach(System.out::println);
    }
}

補充:Collections

在這裡插入圖片描述
在這裡插入圖片描述

Optional

  1. Optional<T> 類(java.util.Optional) 是一個容器類,它可以儲存型別T的值,代表這個值存在。或者僅僅儲存null,表示這個值不存在。原來用 null表示一個值不存在,現在 Optional可以更好的表達這個概念。並且可以避免空指標異常。

  2. 常用方法
    在這裡插入圖片描述

相關文章