Java Retry 重試機制 - 指數退避模版
遠端呼叫爆了,重試一下通常就活了。但如果一直狂轟,server 只會更爆。所以咱們要聰明一點,用指數退避(exponential backoff)的方式慢慢來。 為啥要指數退避直接 retry 沒有 delay 的問題: Server 還沒回過來,馬上再打一次,等於雪上加霜 頻繁重試反而加重服務負擔 看起來像 DDoS 攻擊 XD 指數退避的邏輯: 第 1 次失敗,等 2 秒再試 第 2 次失敗,等 4 秒再試 第 3 次失敗,等 8 秒再試 以此類推… 給 server 喘息的時間,機率就高很多。 通用模版12345678910111213141516171819202122232425262728public boolean retry(int maxAttempts, int initialDelaySeconds, Runnable action) { int attempts = 0; int delay = initialDelaySeconds; while (attempts < maxAttempts) ...
Java Optional 完整用法指南
Optional 是 Java 8 引入的概念,初看很簡單,其實用法超深。一開始很多人覺得 Optional 很煩,後來用對了才發現「真香」。最關鍵是理解它的三段式結構。 Optional 的三段式結構Optional 的用法可以分成三段:頭部(建立)→ 身體(轉換)→ 尾巴(取值) 12建立 轉換(0~多個) 取值Optional → filter/map/flatMap → orElse/ifPresent 頭部:建立 Optionalof() - 確定非 null1234567User user = new User("Alice");// 用 of() 當你確定值一定不為 nullOptional<User> opt = Optional.of(user);// 如果值是 null,直接拋 NullPointerExceptionOptional<User> opt2 = Optional.of(null); // ❌ 拋 exception ofNullable() - 可能 null12345...
Java 實體類物件比較 - equals 與 hashCode 正確實作
比較兩個物件相等這種事,看似簡單但超容易踩坑。特別是涉及 equals 和 hashCode 時,小細節搞錯就會爆。見過太多人只覆寫 equals 不覆寫 hashCode,或用 BigDecimal 的 equals 爆掉的慘案。 核心概念要比較兩個物件相等,**必須同時覆寫 equals() 和 hashCode()**。 為什麼?因為 HashMap、HashSet 等容器依賴 hashCode 和 equals 配合: 123456789// 容器先用 hashCode 定位,再用 equals 驗證Map<User, String> map = new HashMap<>();User u1 = new User(1L, "Alice");map.put(u1, "value1");User u2 = new User(1L, "Alice");// 如果只覆寫 equals 不覆寫 hashCode:// u1 和 u2 hashCode 不同 → 容器認為是不同的鍵// map.ge...
HashMap 轉 JSON 的三種方式
HashMap 轉 JSON 看似簡單,但真正用上去踩坑才多。有三種常見的方式,各有優缺點,選對一個才不會被 JsonProcessingException 折磨。 場景說明先假設有這樣的 HashMap: 1234567891011Map<String, Object> userMap = new HashMap<>();userMap.put("id", 123);userMap.put("name", "Alice");userMap.put("email", "alice@example.com");userMap.put("age", 30);userMap.put("tags", Arrays.asList("admin", "developer"));Map<String, Object> addressMap = new HashMap<>...
Java 8 Stream forEach 與傳統迭代的差異
一開始用 Stream 時,就是 forEach 走天下。後來才發現 Stream forEach 和傳統 Iterator 根本是兩套邏輯,搞混的話效能會爆。 概念差異傳統迭代:外部迭代(External Iteration)你決定怎麼走訪: 1234567891011121314151617List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// for-loop 迴圈for (int i = 0; i < names.size(); i++) { System.out.println(names.get(i));}// Iterator 迴圈Iterator<String> iterator = names.iterator();while (iterator.hasNext()) { System.out.println(iterator.next());}//...
Thread.sleep 的七種替代寫法
每次要延遲執行時,很多人只想到 Thread.sleep()。但你知道嗎?有七種寫法可以選,每種各有優缺點。選對的話,程式真香,選錯的話,維護時會想揍自己。 方法 1:Thread.sleep(ms) - 最基本沒啥好說的,最原始的方式: 1234567try { System.out.println("Before sleep"); Thread.sleep(2000); // 睡 2 秒 System.out.println("After sleep");} catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新設定中斷狀態} 優點: 簡單直接缺點: 毫秒單位,容易打錯;InterruptedException 必須處理;不易讀 方法 2:TimeUnit.SECONDS.sleep(n) - 可讀性最好用 TimeUnit 包裝,讓意圖更清楚: 1234567try {...
Java Synchronized 六種同步模式完整解析
synchronized 這東西從大學開始就在教,但會用和懂用是兩碼子事。踩過不少坑之後,發現這六種同步模式各有眉角,搞不清楚一定會爆。 模式 1:Synchronized Method最簡單的寫法,直接加在方法上: 1234567891011public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; }} 鎖住的對象是什麼? this(呼叫這個方法的物件實例) 12345678910Counter counter1 = new Counter();Counter counter2 = new Counter();Thread t1 = new Thread(counter1::increment);Thread t...
CGLIB 動態代理原理與使用
剛開始用 Spring AOP 時,以為都是用 JDK Proxy。後來發現為啥有些類別代理不了,還是要靠 CGLIB,才開始研究這東西。CGLIB 現在真的無所不在,有必要搞懂它的原理。 為什麼需要 CGLIB?JDK Proxy 有個死穴:必須有 interface。 12345678910111213// JDK Proxy 能代理這個public interface UserService { void save(User user);}public class UserServiceImpl implements UserService { public void save(User user) { }}// JDK Proxy 代理不了這個 - 沒有 interfacepublic class OrderService { public void createOrder(Order order) { }} 現實中一堆類別沒有 interface,難不...
Java Supplier 函式介面用途與延遲執行
一開始看 Supplier 就覺得「幹嘛不直接傳值就好?」,後來用過幾次才明白,這東西在延遲執行的場景真的超香。 什麼是 SupplierSupplier 是 Java 8 引入的函式介面(Functional Interface),定義很簡單: 1234@FunctionalInterfacepublic interface Supplier<T> { T get();} 特點就是: 不接參數 - 呼叫時不需要傳任何東西 回傳一個值 - 回傳型別由泛型 T 決定 可以 lambda - 因為是 @FunctionalInterface,能用 lambda 表達 核心用途:延遲執行Supplier 最強的地方就是「延遲執行」。把一段邏輯包成 Supplier,等到真正需要時才呼叫 .get() 去執行。 實例 1:Logger 的 Lazy Evaluation最經典的例子: 12345// 不好的做法 - 即使 log level 是 INFO,也會執行運算logger.debug("Current value is &qu...
資料庫悲觀鎖與樂觀鎖完整比較
做過庫存扣款、積分更新這種涉及併發的操作沒有?一開始以為很簡單,後來才發現如果不懂鎖機制,資料一定會爆。悲觀鎖和樂觀鎖是兩種完全不同的思路,沒搞懂選錯一個,後期真的有夠慘。 悲觀鎖(Pessimistic Lock)悲觀鎖的思路就是:「我相信一定會有人跟我搶」,所以先佔著位子再說。 SQL 寫法最常見就是 SELECT … FOR UPDATE: 1234567891011@Repositorypublic class InventoryRepository { @Autowired private JdbcTemplate jdbcTemplate; public Integer getInventoryWithLock(Long productId) { String sql = "SELECT quantity FROM inventory WHERE product_id = ? FOR UPDATE"; return jdbcTemplate.queryForObjec...














