Java 8 (2014 年發布) 是自 Java 5 以來最大版本升級,涵蓋語言、編譯器、類庫、工具、JVM 五大維度十餘項新特性,確立了現代 Java 開發範式:函數式程式設計、聲明式資料處理、不可變時間模型、模組化 JVM。
Lambda 讓函數成為一等公民,可作為參數傳遞、賦值給變數。語法:(參數) -> 運算式 或 (參數) -> { 語句塊 }。
// 基礎用法
List<String> list = Arrays.asList("a", "b", "d");
list.forEach(e -> System.out.println(e));
// 類型推論與顯式聲明
list.forEach((String e) -> System.out.println(e));
// 複雜語句塊
list.forEach(e -> { System.out.print(e); System.out.print(e); });
// 閉包捕獲:區域變數隱式 final
String separator = ",";
list.forEach(e -> System.out.print(e + separator));
// 返回值類型推論
list.sort((e1, e2) -> e1.compareTo(e2));
@FunctionalInterface僅含單一抽象方法的介面,可隱式轉換為 Lambda。預設方法與靜態方法不破壞函數式介面契約。
@FunctionalInterface
public interface Functional {
void method();
default void defaultMethod() {} // 允許
static void staticMethod() {} // 允許
}
default 關鍵字讓介面提供預設實作,實作類可選擇繼承或覆寫,實現「不破壞二進制相容性」的介面演進。
interface Defaulable {
default String notRequired() { return "Default implementation"; }
}
class DefaultableImpl implements Defaulable {} // 繼承預設
class OverridableImpl implements Defaulable {
@Override public String notRequired() { return "Overridden"; } // 覆寫
}
介面靜態方法作為輔助工具:
interface DefaulableFactory {
static Defaulable create(Supplier<Defaulable> s) { return s.get(); }
}
// 使用方法引用
Defaulable d = DefaulableFactory.create(DefaultableImpl::new);
四種形式:
| 類型 | 語法 | 範例 |
|---|---|---|
| 建構子引用 | Class::new | Car::new |
| 靜態方法引用 | Class::staticMethod | Car::collide |
| 實例方法引用(特定物件) | instance::method | police::follow |
| 實例方法引用(任意物件) | Class::method | Car::repair |
@Repeatable@Repeatable(Filters.class)
@interface Filter { String value(); }
@interface Filters { Filter[] value(); }
@Filter("filter1") @Filter("filter2")
interface Filterable {}
// 反射獲取:Filterable.class.getAnnotationsByType(Filter.class)
編譯器能推導泛型參數,減少顯式聲明:
Value<String> value = new Value<>(); // 鑽石操作符
value.getOrDefault("22", Value.defaultValue()); // 參數類型自動推導
新增 ElementType.TYPE_USE、ElementType.TYPE_PARAMETER,註解可用於區域變數、型別參數、例外聲明等任意型別位置。
啟用 javac -parameters 或 Maven 配置 maven-compiler-plugin <compilerArgument>-parameters</compilerArgument>,執行期可透過 Parameter.getName() 獲取真實參數名(預設為 arg0)。
Optional<String> name = Optional.ofNullable(null);
name.isPresent(); // false
name.orElseGet(() -> "[none]"); // "[none]"
name.map(s -> "Hey " + s + "!").orElse("Hey Stranger!"); // "Hey Stranger!"
Optional<String> present = Optional.of("Tom");
present.map(s -> "Hey " + s + "!").orElse("Hey Stranger!"); // "Hey Tom!"
核心方法:isPresent()、orElse()、orElseGet()、map()、flatMap()、filter()、ifPresent()。
// Task: status(OPEN/CLOSED), points List<Task> tasks = Arrays.asList( new Task(OPEN, 5), new Task(OPEN, 13), new Task(CLOSED, 8)); // 篩選+映射+聚合:OPEN 任務總分 long total = tasks.stream() .filter(t -> t.getStatus() == OPEN) .mapToInt(Task::getPoints) .sum(); // 18 // 並行處理 double totalAll = tasks.parallelStream() .mapToInt(Task::getPoints) .reduce(0, Integer::sum); // 26.0 // 分組 Map<Status, List<Task>> grouped = tasks.stream() .collect(Collectors.groupingBy(Task::getStatus)); // 權重百分比計算 (鏈式流轉換) List<String> weights = tasks.stream() .mapToInt(Task::getPoints).asLongStream() .mapToDouble(p -> p / totalAll).boxed() .mapToLong(w -> (long)(w * 100)).mapToObj(p -> p + "%") .collect(Collectors.toList()); // [19%, 50%, 30%]
filter/map/flatMap/distinct/sorted/peek/limit/skip — 惰性、返回新 StreamforEach/collect/reduce/count/min/max/findFirst/anyMatch/allMatch/noneMatch — 觸發計算、消費 StreamparallelStream() 或 .parallel(),ForkJoinPool 執行Files.lines(path) 返回 Stream<String>java.time)解決 Date/Calendar 可變、非執行緒安全、API 反直覺等歷史包袱,深受 Joda-Time 影響。
| 類別 | 用途 | 關鍵特性 |
|---|---|---|
Instant | 時間戳 (納秒精度) | 不可變、機器時間 |
LocalDate | 日期 (無時間、無時區) | ISO-8601 日曆 |
LocalTime | 時間 (無日期、無時區) | 納秒精度 |
LocalDateTime | 日期時間 (無時區) | 常用業務模型 |
ZonedDateTime | 帶時區日期時間 | 處理夏令時、時區轉換 |
Duration | 時間間隔 (秒/納秒) | 適合機器時間差 |
Period | 日期間隔 (年/月/日) | 適合人類時間差 |
Clock | 時鐘抽象 | 可替換 System.currentTimeMillis(),利於測試 |
// 常用操作
LocalDate.now(); // 今日
LocalDateTime.now(Clock.systemUTC()); // 指定時鐘
ZonedDateTime.now(ZoneId.of("America/Los_Angeles")); // 指定時區
// 期間計算
Duration.between(
LocalDateTime.of(2014, 4, 16, 0, 0),
LocalDateTime.of(2015, 4, 16, 23, 59, 59)
).toDays(); // 365
// 格式化/解析
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime.parse("2024-01-01 12:00:00", fmt);
javax.script.ScriptEngine 實作,支援 Java/JS 互呼:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
engine.eval("function f() { return 1; }; f() + 1;"); // 2
Base64.getEncoder().encodeToString("text".getBytes(StandardCharsets.UTF_8));
// 支援 URL/MIME 變體:getUrlEncoder()/getMimeEncoder()
long[] arr = new long[20000]; Arrays.parallelSetAll(arr, i -> ThreadLocalRandom.current().nextInt(1000000)); Arrays.parallelSort(arr); // 多核排序顯著加速
ConcurrentHashMap 新增聚合操作:reduceValues、forEach、searchStampedLock:容量型鎖,三模式 (樂觀讀/悲觀讀/寫),替代 ReadWriteLockjava.util.concurrent.atomic 新增:LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator (高併發計數器)CompletableFuture (Java 8 核心非同步編程組件,雖非原文提及但屬同期重大特性)jjs:Nashorn 命令列執行 JS 檔jdeps:類別依賴分析器,輸出包/類依賴圖,支援 JAR/目錄/Class 檔輸入-XX:MetaSpaceSize/-XX:MaxMetaspaceSize 取代 -XX:PermSize/-XX:MaxPermSizeOutOfMemoryError: PermGen spacestream(),邏輯更清晰、易並行Date/Calendar,新代碼僅用 LocalDateTime/ZonedDateTimeMaxMetaspaceSizeJava 8 以 Lambda/Stream 確立函數式風格,以 java.time 修正時間模型,以 Optional 消除空指針困擾,以 Metaspace 解決類載入記憶體痛點。這些特性相互配合,構成了現代 Java 生態系統的基石。掌握核心 API 與遷移策略,是每位 Java 開發者邁向高效開發的必經之路。