Java之所以被開發,是要達到以下五個目的:
- 應當使用物件導向程式設計方法學
- 應當允許同一程式在不同的電腦平台執行
- 應當包括內建的對電腦網路的支援
- 應當被設計成安全地執行遠端程式碼
- 應當易於使用,並借鑑以前那些物件導向語言(如C++)的長處。
Java 在各個版本的演進
1.0-1.4 中的 java.lang.Thread
5.0 中的 java.util.concurrent
6.0 中的 Phasers 等
7.0 中的 Fork/Join 框架
8.0 中的 Lambda、Stream
9.0 中的 Jigsaw 模組化
CHAPTER 1 Basic
Anonymous inner - > Lambda
傳統匿名語法
new Thread(new Runnable() { @Override public void run() { System.out.println("inside runnable using an anonymous inner class"); } }).start();
lambda 於建構子中
new Thread(() -> System.out.println("inside Thread constructor using lambda")).start();
lambda 賦與變數
Runnable r = () -> System.out.println("lambda expression implementing the run method");
lambda 實做 FilenameFilter
String[] names = directory.list((dir, name) -> name.endsWith(".java")); System.out.println(Arrays.asList(names));
lambda 區塊
String[] names = directory.list((File dir, String name) -> { return name.endsWith(".java"); });
使用 方法參考(method reference) 存取 print
// Consumer<Integer> functional interface Consumer<Integer> printer = System.out::println; Stream.of(3, 1, 4, 1, 5, 9).forEach(printer);
語句
object::instanceMethod
物件實例方法 ex: System.out::println
Class::staticMethod
類別靜態方法 ex: Math::max
Class::instanceMethod
類別實例方法 ex: String::length
@FunctionalInterface
註釋該介面目標為 lambda expression or method reference.
只能有一個抽象成員
Default Methods
在介面中提供實做
public interface Collection<E> extends Iterable<E> { boolean isEmpty(); default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; } } // usage boolean removed = Arrays.asList(3, 1, 4, 1, 5, 9).removeIf(n -> n <= 0);
CHAPTER 2 The java.util.function Package
p.s. ReferencePipeline內的 downstream 為 Consumer 類
Consumer
將欲使用方法傳入 Consumer 內
// implement public interface Iterable<T> { Iterator<T> iterator(); default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } } // usage Arrays.asList("this", "is", "a", "list", "of", "strings").forEach(System.out::println);
Suppliers
將產生的值載入 supplier 內 並透過 get 方法取得值
Supplier<T> sippiler = () -> T; DoubleSupplier randomSupplier = Math::random; // 此時才呼叫 Math.random() System.out.println(randomSupplier.getDouble());
附加 Suppliers 介面
Interface | abstract method |
---|---|
Supplier | T get() |
IntSupplier | int getAsInt() |
DoubleSupplier | double getAsDouble() |
LongSupplier | long getAsLong() |
BooleanSupplier | boolean getAsBoolean() |
Predicates
透過 Predicates 判斷是否有符合 filter
// implement public static boolean predicateTest(int value, Predicate<Integer> predicate) { return predicate.test(value); } // usage predicateTest(3, (x) -> x == 3); // Stream implement Stream<T> filter(Predicate<? super T> predicate); abstract class IntPipeline<E_IN> extends AbstractPipeline<E_IN, Integer, IntStream> implements IntStream { @Override public final IntStream filter(IntPredicate predicate) { Objects.requireNonNull(predicate); return new StatelessOp<Integer>(this, StreamShape.INT_VALUE, StreamOpFlag.NOT_SIZED) { @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { return new Sink.ChainedInt<Integer>(sink) { @Override public void begin(long size) { downstream.begin(-1); } @Override public void accept(int t) { // 檢查是否通過測試 if (predicate.test(t)) downstream.accept(t); } }; } }; } } // usage Arrays.asList("this", "is", "a", "list", "of", "strings") .stream() .filter(s -> s.length() == 2) .collect(Collectors.joining(", "));
Functions
呼叫特定方法,並回傳所需內容
// implement static int modifyTheValue(int valueToBeOperated, Function<Integer, Integer> function) { return function.apply(valueToBeOperated); } // usage modifyTheValue(10, (x)-> x + 20); // Stream implement abstract class ReferencePipeline<P_IN, P_OUT> extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>> implements Stream<P_OUT> { @Override @SuppressWarnings("unchecked") public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) { Objects.requireNonNull(mapper); return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) { return new Sink.ChainedReference<P_OUT, R>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); } }; } }; } } // usage Arrays.asList("this", "is", "a", "list", "of", "strings") .stream() .map(String::length) .collect(Collectors.toList());
附加 Functions 介面
Interface | abstract method |
---|---|
Function | R apply(t value) |
IntFunction | R apply(int value) |
DoubleFunction | R apply(double value) |
LongFunction | R apply(long value) |
ToIntFunction | int applyAsInt(T value) |
ToDoubleFunction | double applyAsDouble(T value) |
ToLongFunction | long applyAsLong(T value) |
DoubleToIntFunction | int applyAsInt(double value) |
DoubleToLongFunction | long applyAsLong(double value) |
IntToDoubleFunction | double applyAsDouble(int value) |
IntToLongFunction | long applyAsLong(int value) |
LongToDoubleFunction | double applyAsDouble(long value) |
LongToIntFunction | int applyAsInt(long value) |
BiFunction | void accept(T t, U u) |
ToIntBiFunction | int applyAsInt(T t, U u) |
ToDoubleBiFunction | double applyAsDouble(T t, U u) |
ToLongBiFunction | long applyAsLong(T t, U u) |
CHAPTER 3 Streams
java 8 新串流 API 支援 functional programming,串流只能被使用一次,如需要再次處理數據必須再重新建立。
Stream 運行原理
// 呼叫流程 Arrays.stream -> StreamSupport.stream -> return ReferencePipeline.Head -> filter (or the method you need)
CHAPTER 4 Comparators and Collectors
java7 使用 collections
public List<String> defaultSort() { Collections.sort(list); return sampleStrings; }
java8 透過 streams 使用 collections
public List<String> defaultSortUsingStreams() { return list.stream() .sorted() .collect(Collectors.toList()); }
collect 方法 兩種實做
<R, A> R collect(Collector<? super T, A, R> collector); <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
CHAPTER 5 Issues with Streams, Lambdas, and Method References
使用靜態方法確認空值與比較
// 去除空值資料 List<String> nonNullStrings = Arrays.asList( "this", null, "is", "a", null, "list", "of", "strings", null) .stream().filter(Objects::nonNull).collect(Collectors.toList());
在 lambdas 內存取在外 local 變數
int total = 0; // 錯誤 lambdas 無法存取 local var Arrays.asList(3, 1, 4, 1, 5, 9).forEach(n -> total += n); // 使用 function 傳入屬性進行存取 Arrays.asList(3, 1, 4, 1, 5, 9) .stream().mapToInt(Integer::valueOf).sum()
使用 forEach 來進行迭代
List<Integer> integers = Arrays.asList(3, 1, 4, 1, 5, 9); // 匿名方法 integers.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }); // 完整敘述式 lambda integers.forEach((Integer n) -> {System.out.println(n);}); // 表達式 lambda integers.forEach(n -> System.out.println(n)); // 方法參照 integers.forEach(System.out::println);
檢查 lambda 錯誤 exception
// 使用 encode 必須處理 UnsupportedEncodingException Arrays.stream(values).map(s -> URLEncoder.encode(s, "UTF-8"))).collect(Collectors.toList()); // solution Arrays.stream(values) .map(s -> { try { return URLEncoder.encode(s, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return ""; } }) .collect(Collectors.toList());
透過 @FunctionalInterface 改寫
@FunctionalInterface public interface FunctionWithException<T, R, E extends Exception> { R apply(T t) throws E; } private static <T, R, E extends Exception> Function<T, R> wrapper(FunctionWithException<T, R, E> fe) { return arg -> { try { return fe.apply(arg); } catch (Exception e) { throw new RuntimeException(e); } }; } Arrays.stream(values).map(wrapper(s -> URLEncoder.encode(s, "UTF-8"))).collect(Collectors.toList());
CHAPTER 6 The Optional Type
Java 8 新的 java.util.Optional<T>
類,目的是解決許多開發者困擾的 NullPointerExceptions,他被設計以溝通的方式告知回傳值為不合法的空值。
Optional 結合 AtomicInteger 範例
AtomicInteger counter = new AtomicInteger(); Optional<AtomicInteger> opt = Optional.ofNullable(counter); System.out.println(optional); // Optional[0] counter.incrementAndGet(); System.out.println(optional); // Optional[1] optional.get().incrementAndGet(); // 重新指定內容 optional = Optional.ofNullable(new AtomicInteger());
Optional 例外範例
Optional<String> firstOdd = Stream.of("five", "even", "length", "string", "values") .filter(s -> s.length() % 2 != 0) .findFirst(); // throws NoSuchElementException System.out.println(firstOdd.get()); // 使用 isPresent 檢查空值 System.out.println( firstOdd.isPresent() ? firstOdd.get() : "No even length strings"); // 如值為空回傳預設值 firstOdd.orElse("No even length strings"); // 如值為空執行Methods並回傳值 firstOdd.orElseGet(() -> new String());
CHAPTER 7 File I/O
使用 stream 處理檔案內文
找出檔案內最長的單字前10名
try (Stream<String> lines = Files.lines(Paths.get("/usr/share/dict/web2")) { lines.filter(s -> s.length() > 20) .sorted(Comparator.comparingInt(String::length).reversed()).limit(10) .forEach(w -> System.out.printf("%s (%d)%n", w, w.length())); } catch (IOException e) { e.printStackTrace(); }
列出目錄檔案清單
try (Stream<Path> paths = Files.walk(Paths.get("src/main/java"))) { paths.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); }
找出含有特定目錄的檔案
try (Stream<Path> paths = Files.find(Paths.get("src/main/java"), Integer.MAX_VALUE, (path, attributes) -> !attributes.isDirectory() && path.toString().contains("fileio"))) { paths.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); }
CHAPTER 8 The java.time Package
從 JDK1.0 開始就包含的程式庫,java.util.Date , 大多數 java.util.Date 方法都被棄用並使用 java.util.Calendar 來取代,但也並不是那麼方便取用。
終於在 Java 8 後導入了 Joda-Time library 並建議之後的使用者使用它做為開發。
基本使用
// Instant.now(): 2017-06-20T17:27:08.184Z System.out.println("Instant.now(): " + Instant.now()); // LocalDate.now(): 2017-06-20 System.out.println("LocalDate.now(): " + LocalDate.now()); // LocalTime.now(): 13:27:08.318 System.out.println("LocalTime.now(): " + LocalTime.now()); // LocalDateTime.now(): 2017-06-20T13:27:08.319 System.out.println("LocalDateTime.now(): " + LocalDateTime.now()); // ZonedDateTime.now(): 2017-06-20T13:27:08.319-04:00[America/New_York] System.out.println("ZonedDateTime.now(): " + ZonedDateTime.now());
util.Date, util.Calendar, java.sql.Date 與 util.time 互換
package datetime; import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; public class ConvertDate { public LocalDate convertFromSqlDatetoLD(java.sql.Date sqlDate) { return sqlDate.toLocalDate(); } public java.sql.Date convertToSqlDateFromLD(LocalDate localDate) { return java.sql.Date.valueOf(localDate); } public LocalDateTime convertFromTimestampToLDT(Timestamp timestamp) { return timestamp.toLocalDateTime(); } public Timestamp convertToTimestampFromLDT(LocalDateTime localDateTime) { return Timestamp.valueOf(localDateTime); } }
CHAPTER 9 Parallelism and Concurrency
parallelStream 是透過 ForkJoinPool Thread 來進行並發任務
並行範例
// 下列陣列將不會按順訊印出 Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9) .parallelStream().forEach(out::println); // 使用 forEachOrdered 強制以順序印出 Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9) .parallelStream().forEachOrdered(out::println);
Parallelism 與其他方式比較
class Person { int id; String name; String sex; float height; } // 傳統迴圈 public List<Person> constructPersons() { List<Person> persons = new ArrayList<Person>(); for (int i = 0; i < 5; i++) { Person p = new Person(i, "name" + i, "sex" + i, i); persons.add(p); } return persons; } // 迴圈 public void doFor(List<Person> persons) { long start = System.currentTimeMillis(); for (Person p : persons) { System.out.println(p.name); } long end = System.currentTimeMillis(); System.out.println("doFor cost:" + (end - start)); } // 串流 public void doStream(List<Person> persons) { long start = System.currentTimeMillis(); persons.stream().forEach(x -> System.out.println(x.name)); long end = System.currentTimeMillis(); System.out.println("doStream cost:" + (end - start)); } // 並行 public void doParallelStream(List<Person> persons) { long start = System.currentTimeMillis(); persons.parallelStream().forEach(x -> System.out.println(x.name)); long end = System.currentTimeMillis(); System.out.println("doParallelStream cost:" + (end - start)); }
使用 openjdk (Java Micro-benchmark Harness) 計算效能
import org.openjdk.jmh.annotations.*; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Thread) @Fork(value = 2, jvmArgs = {"-Xms4G", "-Xmx4G"}) public class DoublingDemo { public int doubleIt(int n) { try { Thread.sleep(100); } catch (InterruptedException ignored) { } return n * 2; } @Benchmark public int doubleAndSumSequential() { return IntStream.of(3, 1, 4, 1, 5, 9).map(this::doubleIt).sum(); } @Benchmark public int doubleAndSumParallel() { return IntStream.of(3, 1, 4, 1, 5, 9) .parallel().map(this::doubleIt).sum(); } }
ForkJoinPool
改變 ForkJoinPool 參數
// 改變 pool 大小 System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20"); // 使用自定的 ForkJoinPool 執行 parallel ForkJoinPool pool = new ForkJoinPool(15); ForkJoinTask<Long> task = pool.submit(() -> LongStream.rangeClosed(1, 3_000_000).parallel().sum());
Future
等待未來的結果
public void getIfNotCancelled(Future<String> future) { if (!future.isCancelled()) { System.out.println(future.get()); } else { System.out.println("Cancelled"); } } ExecutorService service = Executors.newCachedThreadPool(); Future<String> future = service.submit(new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(100); return "Hello, World!"; } }); // 取消 service submit future.cancel(true); System.out.println("Processing..."); getIfNotCancelled(future);
CHAPTER 10 Java 9 Additions
Java 9 新功能
Jigsaw
Suppliers Module
package com.oreilly.suppliers; public class NamesSupplier implements Supplier<Stream<String>> { private Path namesPath = Paths.get("server/src/main/resources/names.txt"); @Override public Stream<String> get() { try { return Files.lines(namesPath); } catch (IOException e) { e.printStackTrace(); return null; } } } // module-info.java module com.oreilly.suppliers { exports com.oreilly.suppliers; }
package com.kousenit.clients; import com.oreilly.suppliers.NamesSupplier; public class Main { public static void main(String[] args) throws IOException { NamesSupplier supplier = new NamesSupplier(); try (Stream<String> lines = supplier.get()) { lines.forEach(line -> System.out.printf("Hello, %s!%n", line)); } } } // module-info.java module com.kousenit.clients { requires com.oreilly.suppliers; }
介面私有成員
public interface SumNumbers { default int addEvens(int... nums) { return add(n -> n % 2 == 0, nums); } default int addOdds(int... nums) { return add(n -> n % 2 != 0, nums); } private int add(IntPredicate predicate, int... nums) { return IntStream.of(nums) .filter(predicate) .sum(); } } class PrivateDemo implements SumNumbers {} public class SumNumbersTest { private SumNumbers demo = new PrivateDemo(); @Test public void addEvens() throws Exception { assertEquals(2 + 4 + 6, demo.addEvens(1, 2, 3, 4, 5, 6)); } @Test public void addOdds() throws Exception { assertEquals(1 + 3 + 5, demo.addOdds(1, 2, 3, 4, 5, 6)); } }
不可變動集合
靜態工廠
不可變動集合 使用 add, addAll, clear, remove, removeAll, replaceAll, set 將會拋出 UnsupportedOperationException.
@Test(expected = UnsupportedOperationException.class) public void showImmutabilityAdd() throws Exception { List<Integer> intList = List.of(1, 2, 3); intList.add(99); } @Test(expected = UnsupportedOperationException.class) public void showImmutabilityClear() throws Exception { List<Integer> intList = List.of(1, 2, 3); intList.clear(); } @Test(expected = UnsupportedOperationException.class) public void showImmutabilityRemove() throws Exception { List<Integer> intList = List.of(1, 2, 3); intList.remove(0); } @Test(expected = UnsupportedOperationException.class) public void showImmutabilityReplace() throws Exception { List<Integer> intList = List.of(1, 2, 3); intList.replaceAll(n -> -n); } @Test(expected = UnsupportedOperationException.class) public void showImmutabilitySet() throws Exception { List<Integer> intList = List.of(1, 2, 3); intList.set(0, 99); } // 如果將不可變動集合塞入物件,將可改變物件內容 @Test public void areWeImmutableOrArentWe() throws Exception { List<Holder> holders = List.of(new Holder(1), new Holder(2)); assertEquals(1, holders.get(0).getX()); holders.get(0).setX(4); assertEquals(4, holders.get(0).getX()); }
迭代使用Predicate
// Java8 方法 List<BigDecimal> bigDecimals = Stream.iterate(BigDecimal.ZERO, bd -> bd.add(BigDecimal.ONE)).limit(10).collect(Collectors.toList()); // Java9 方法 bigDecimals = Stream.iterate(BigDecimal.ZERO, bd -> bd.longValue() < 10L, bd -> bd.add(BigDecimal.ONE)) .collect(Collectors.toList());
日期計算
// Java8 用法 public List<LocalDate> getDays_java8(LocalDate start, LocalDate end) { Period period = start.until(end); return LongStream.range(0, ChronoUnit.DAYS.between(start, end)) .mapToObj(start:plusDays) .collect(Collectors.toList()); } LocalDate start = LocalDate.of(2017, Month.JUNE, 10); LocalDate end = LocalDate.of(2017, Month.JUNE, 17); System.out.println(dateRange.getDays_java8(start, end)); // Java9 用法 public List<LocalDate> getDays_java9(LocalDate start, LocalDate end) { return start.datesUntil(end).collect(Collectors.toList()); } public List<LocalDate> getMonths_java9(LocalDate start, LocalDate end) { return start.datesUntil(end, Period.ofMonths(1)).collect(Collectors.toList()); }
Extra Information
Try-With-Resources
Try with Resources 會自動幫開發者關閉 try(closeable; ... ) 內實做 java.lang.AutoCloseable 的物件
// Java8 用法 // inputstream try(InputStream stream1 = new InputStream(....); InputStream stream2 = new InputStream(....)) { ... } // connection try (Connection con = DriverManager.getConnection(myConnectionURL); PreparedStatement ps = createPreparedStatement(con, userId); ResultSet rs = ps.executeQuery()) { // process the resultset here, all resources will be cleaned up } catch (Exception e) { e.printStackTrace(); } // Java9 用法 InputStream stream1 = new InputStream(....); InputStream stream2 = new InputStream(....); .... try(stream1;stream2){ .... }
APPENDIX A Generics and Java 8
關於泛型的能力大多數的 Java 開發者都只認識到他們所需用於工作上的範圍,跟著 Java 8 的來到 javadoc 文件已經充斥著像是以下的代碼 :
// java.util.Map.Entry static <K extends Comparable<? super K>,V> Comparator<Map.Entry<K,V>> comparingByKey() // or this one from java.util.Comparator: static <T,U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T,? extends U> keyExtractor) // or even this monster from java.util.stream.Collectors: static <T,K,D,A,M extends Map<K, D>> Collector<T,?,M> groupingBy( Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
理解基礎的泛型已不再夠用。
簡易泛型代碼
List<String> strings = new ArrayList<>(); strings.add("Hello"); strings.add("World"); // strings.add(new Date()); // Integer i = strings.get(0); 編譯不過 for (String s : strings) { System.out.printf("%s has length %d%n", s, s.length()); }
List 所定義的方法
boolean add(E e) boolean addAll(Collection<? extends E> c) void clear() boolean contains(Object o) boolean containsAll(Collection<?> c) E get(int index)
有些開發者所不理解的
許多開發者對於 ArrayList<String>
跟 ArrayList<Object>
並沒有關連
List<String> strings = new ArrayList<>(); String s = "abc"; String s = "abc"; Object o = s; // 這樣的代碼是不被允許的 strings.add(o);
未只定 List 的通配符
List<?> stuff = new ArrayList<>(); // 未指定泛型的 list 不能被寫入 stuff.add("abc"); stuff.add(new Object()); stuff.add(3); // 但是能被讀取 int numElements = stuff.size();
Upper Bounded Wildcards
使用向上通配符一樣不能被寫入
List<? extends Number> numbers = new ArrayList<>(); // numbers.add(3); // numbers.add(3.14159); // numbers.add(new BigDecimal("3"));
使用 Upper Bounded
private static double sumList(List<? extends Number> list) { return list.stream().mapToDouble(Number::doubleValue).sum(); } List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0); List<BigDecimal> bigDecimals = Arrays.asList( new BigDecimal("1.0"),new BigDecimal("2.0"),new BigDecimal("3.0"),new BigDecimal("4.0"),new BigDecimal("5.0")); System.out.printf("ints sum is %s%n", sumList(ints)); System.out.printf("doubles sum is %s%n", sumList(doubles)); System.out.printf("big decimals sum is %s%n", sumList(bigDecimals));
使用 Lower Bounded
向下通配符一樣不能被寫入
public void numsUpTo(Integer num, List<? super Integer> output) { IntStream.rangeClosed(1, num).forEach(output::add); } ArrayList<Integer> integerList = new ArrayList<>(); ArrayList<Number> numberList = new ArrayList<>(); ArrayList<Object> objectList = new ArrayList<>(); numsUpTo(5, integerList); numsUpTo(5, numberList); numsUpTo(5, objectList);
多重分配符
T extends Runnable & AutoCloseable
Top comments (0)