# Java 面試筆記
###### tags: `interview`
[TOC]
---
## Annotation
|註解|功能|適用場景|
|---|---|---|
|@Cacheable|啟用快取,減少重複計算|預先計算推薦結果、查詢快取|
|@RestController|是 @Controller + @ResponseBody 的組合,表示該類別是一個 RESTful API|所有方法預設返回 JSON 格式。|
|@Transactional(readOnly = true)|只讀模式,優化查詢|單純查詢(SELECT)|
|@Transactional(noRollbackFor = Exception.class)|遇到 Exception 不回滾,但 RuntimeException 會回滾|允許部分異常,但關鍵錯誤仍回滾|
|@Transactional(propagation = Propagation.REQUIRES_NEW)|新事務,獨立提交|獨立日誌、積分計算|
|@Transactional(isolation = Isolation.READ_UNCOMMITTED)|允許讀取未提交數據,提升性能|即時報表、低一致性需求的查詢|
```Java
@Cacheable(value = CachePathNaming.CACHE_KEY_BO_RECOMMENDATION, key = "#recommendationType.name() + ':' + #videoId +'_' + #locale.toString()")
@Transactional(readOnly = true, noRollbackFor=Exception.class)
public List<User> getAllUsers() {
return userRepository.findAll();
}
\\ 允許讀取未提交的資料(Dirty Read)。
\\ 並發性最高,但可能讀取到錯誤數據。
\\ 適合 對數據一致性要求不高,但需要高性能的場景。
@Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
public List<Report> getLiveReports() {
return reportRepository.findAll();
}
```
### propagation = Propagation.REQUIRES_NEW
- processOrder(orderId) 開啟 主事務。
- createOrder(orderId) 開啟 新的事務(獨立於 processOrder),成功後 提交。
- sendNotification(orderId) 開啟 新的事務,但執行時發生異常,回滾 sendNotification 事務,但不影響 createOrder 事務。
```Java
@Service
public class OrderService {
@Transactional
public void processOrder(String orderId) {
createOrder(orderId);
sendNotification(orderId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createOrder(String orderId) {
System.out.println("Creating order: " + orderId);
// 插入訂單紀錄
orderRepository.save(new Order(orderId, "NEW"));
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotification(String orderId) {
System.out.println("Sending notification for: " + orderId);
throw new RuntimeException("通知發送失敗!");
}
}
```
### @Cacheable + Redis 快取範例
你可以使用 Spring Cache + Redis 來實作 @Cacheable,讓資料先查詢快取,沒有的話才查詢 MySQL。
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
```
```
spring:
redis:
host: localhost
port: 6379
password: yourpassword # 若無密碼可省略
timeout: 6000
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000ms
```
```java
@Configuration
@EnableCaching // 啟用 Spring Cache
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))) // 設定 TTL 10 分鐘
.build();
}
}
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Cacheable(value = "products", key = "#id") // 先查 Redis,找不到再查 MySQL
public Product getProductById(Long id) {
System.out.println("查詢 MySQL,ID: " + id);
return productRepository.findById(id).orElse(null);
}
}
```
```java
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
System.out.println("查詢 MySQL,ID: " + id);
return productRepository.findById(id).orElse(null);
}
@CacheEvict(value = "products", key = "#id") // 當商品更新時,刪除舊的 Cache
public Product updateProduct(Long id, Product newProductData) {
System.out.println("更新 MySQL,ID: " + id);
Product product = productRepository.findById(id).orElseThrow();
product.setName(newProductData.getName());
product.setPrice(newProductData.getPrice());
return productRepository.save(product);
}
}
@Caching(
evict = { @CacheEvict(value = "products", key = "#id") }, // 刪除舊快取
put = { @CachePut(value = "products", key = "#id") } // 直接寫入新快取
)
public Product updateProduct(Long id, Product newProductData) {
System.out.println("更新 MySQL,ID: " + id);
Product product = productRepository.findById(id).orElseThrow();
product.setName(newProductData.getName());
product.setPrice(newProductData.getPrice());
return productRepository.save(product);
}
```
### Spring security + JWT
1. SecurityConfig
```Java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/static/**", "/js/**", "/css/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/login/authenticate")
.failureUrl("/?param.error=bad_credentials").permitAll()
.usernameParameter("cellPhone")
.and()
.logout()
.deleteCookies("remember-me")
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/").permitAll()
.and()
.authorizeRequests()
.antMatchers("/version", "/oauth/revoke_token", "/favicon.ico").permitAll()
.anyRequest().authenticated()
.and()
.rememberMe();
}
@Override
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
```
2. 創建 JwtAuthenticationFilter(過濾器,用於攔截請求並驗證 JWT)
```Java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String username = jwtUtil.validateToken(token);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = User.withUsername(username).password("").authorities("USER").build();
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}
```
3. 創建 JUtil 解析 token
```Java
@Component
public class JwtUtil {
private static final String SECRET_KEY = "YourSecretKeyYourSecretKeyYourSecretKey!"; // 必須至少 32 bytes
private static final long EXPIRATION_TIME = 1000 * 60 * 60; // 1 小時
private final Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
// 產生 JWT
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
// 驗證 JWT
public String validateToken(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
} catch (JwtException e) {
return null; // 無效的 Token
}
}
}
```
4. 創建 controller 發放 token
```Java
@RestController
@RequestMapping("/auth")
public class AuthController {
private final JwtUtil jwtUtil;
public AuthController(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
// 在這裡應該要檢查帳號密碼是否正確(簡化範例,不做驗證)
return jwtUtil.generateToken(username);
}
}
```
### Spring Cloud Gateway
```yaml=
<!-- 不可以引入spring-boot-starter-web会有冲突 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
```
```yaml=
spring:
cloud:
gateway:
routes:
- id: api-A
uri: http://service-a/api/A
predicates:
- Path=/api/A
filters:
- JwtFilter=ROLE_1,ROLE_2
- id: api-B
uri: http://service-b/api/B
predicates:
- Path=/api/B
filters:
- JwtFilter=ROLE_1
```
```java=
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public ResponseEntity<String> getUserInfo(@RequestHeader("X-User-Name") String username,
@RequestHeader("X-User-Roles") String roles) {
return ResponseEntity.ok("User: " + username + ", Roles: " + roles);
}
}
```
#### 總結:
- Gateway Filter:解析 Bearer Token 並注入用戶信息到 Header。
- 配置路由:通過 Gateway 配置不同的後端服務路徑。
- 後端服務:從 Header 中提取用戶信息並處理請求。
## 常問問題
### 如何一秒處理 10000 個 Request
1. 高效程式碼:
- 優化資料庫查詢,使用索引、批量查詢,減少慢查詢。
2. 快取 (Caching):
- 使用 Redis 或 Memcached 儲存熱點數據,減少數據庫請求。
3. 異步處理 (Asynchronous Processing):
- 將非關鍵部分放入消息隊列,例如 Kafka 或 RabbitMQ。
- 使用 logstash 工具將MySQL 資料放入 ELK
- 後端使用事件驅動模型(例如 Node.js、Vert.x)。
4. 水平擴展 (Horizontal Scaling):
- 增加多台伺服器,將流量分散。
- 使用 負載均衡 (Load Balancer) 例如 Nginx 或 AWS ALB。
5. 壓縮和優化:
- 減少 HTTP 請求量,啟用 gzip 壓縮。
- 使用 CDN 分發靜態資源。
### 如何防 DDoS 攻擊
1. 驗證請求真實性:
- 啟用 Captcha,避免機器人攻擊。
- 檢查請求來源,阻擋異常 IP 段或高風險國家的請求。
2. 網絡層防禦:
- 使用 Web Application Firewall (WAF),例如 AWS WAF、Cloudflare。
- 啟用 Rate Limiting,限制每個 IP 的請求數。
3. Cloudflare:
- 啟用 Bot Fight Mode 或 Super Bot Fight Mode,自動識別和阻擋機器人流量。
4. AWS WAF:
- 通過 AWS Managed Rule - Bot Control 檢測和限制機器人。
5. 分散式部署:
- 使用全球分佈的 CDN,減少單點壓力。
### OOM 的代碼
1. Query 大量資料
- List<Data> dataBatch = queryDatabase(offset, limit);
2. 一直 New 重複 Object
3. 一直 add 東西到物件內
4. 一次讀去過大檔案
- byte[] data = Files.readAllBytes(Paths.get("large_file.dat"));
5. 長時間 cache 沒設 expire
##### 解法
- 使用 stream 可有效避免 OOM
- 如果數據量固定且只需要鍵值對存儲,優先考慮 HashMap。
- 對於只需要順序存儲的數據,使用 ArrayList 比 LinkedList 更省內存。
- 如使用 int[] 而不是 List<Integer>。
### 通常 交易的 log 都如何存儲 能在對帳時較快找出問題
```
CREATE TABLE transaction_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
transaction_id VARCHAR(255) NOT NULL,
user_id BIGINT NOT NULL,
amount DECIMAL(18, 2) NOT NULL,
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
#### Elasticsearch + Logstash + Kibana (ELK):
- 优势:支持实时查询、聚合和可视化。
- 应用:适合需要快速定位问题的对账场景。
- 配置建议:按索引划分日志,例如按日期创建索引 transactions-2025-01-18。
### 高併發時兩個 request 同時要做提款,如何堤防
- 樂觀鎖 (Optimistic Locking)
- 排隊 (Queue)
- 分佈式鎖 (Distributed Lock / Redis Lock):
- 使用 SETNX 命令來設置鎖為每次請求生成唯一 ID,並設置過期時間並存入 Redis
- String result = jedis.set(LOCK_KEY, "locked", "NX", "EX", EXPIRATION_TIME);
- 釋放鎖,使用 DEL 命令刪除鍵
- jedis.del(LOCK_KEY);
## Redis CLI
- NX → 只有當 my_lock(lock key) 不存在 時才設置(確保鎖不會被覆蓋)
- EX 10 → 設置 10 秒後自動過期(防止死鎖)
```
SET my_lock "locked value" NX EX 10
// 成功回傳 OK
// 失敗回傳 (nil)
// 先取回locked value確認 locked value
Get my_lock
// 刪除鎖
DEL my_lock
```
```java
// 用戶提款操作
String key = "withdrawal:user:" + userId;
String value = UUID.randomUUID().toString() + ":timestamp:" + System.currentTimeMillis();
```
```java
public boolean acquireLock(String key, String value, long expireTime) {
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return success != null && success;
}
public boolean releaseLock(String key, String value) {
// 確保只釋放自己設置的鎖
String currentValue = redisTemplate.opsForValue().get(key);
if (value.equals(currentValue)) {
redisTemplate.delete(key);
return true;
}
return false;
}
if (redisLockService.acquireLock(lockKey, lockValue, expireTime)) {
try {
// 執行出金業務邏輯
} finally {
redisLockService.releaseLock(lockKey, lockValue);
}
} else {
System.out.println("請求被鎖定,稍後再試");
}
```
```java
// 用戶提款操作
String key = "withdrawal:user:" + userId;
String value = UUID.randomUUID().toString() + ":timestamp:" + System.currentTimeMillis();
```
```java
public boolean acquireLock(String key, String value, long expireTime) {
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return success != null && success;
}
public boolean releaseLock(String key, String value) {
// 確保只釋放自己設置的鎖
String currentValue = redisTemplate.opsForValue().get(key);
if (value.equals(currentValue)) {
redisTemplate.delete(key);
return true;
}
return false;
}
if (redisLockService.acquireLock(lockKey, lockValue, expireTime)) {
try {
// 執行出金業務邏輯
} finally {
redisLockService.releaseLock(lockKey, lockValue);
}
} else {
System.out.println("請求被鎖定,稍後再試");
}
``` * @param value 鎖的 value (需要驗證是否為當前鎖)
* @return 是否成功釋放鎖
*/
public boolean releaseLock(String key, String value) {
// 確保當前鎖的 value 與傳入的 value 一致
String currentValue = redisTemplate.opsForValue().get(key);
if (value.equals(currentValue)) {
redisTemplate.delete(key); // 釋放鎖
return true;
}
return false;
}
}
```
### HashMap 和 Hashtable 有什麼區別?
- 存儲:HashMap 運行 key 和 value 為 null,而 Hashtable 不允許。
- 線程安全:Hashtable 是線程安全的,而 HashMap 是非線程安全的。因為它的方法(如 put() 和 get())都使用了 synchronized 關鍵字,確保只有一個執行緒可以訪問或修改數據。
- 推薦使用:在 Hashtable 的類注釋可以看到,Hashtable 是保留類不建議使用,推薦在單線程環境下使用 HashMap 替代,如果需要多線程使用則用 ConcurrentHashMap 替代。
- 相比 Hashtable,ConcurrentHashMap 效能更好,因為它使用 分段鎖(Segmented Locking),避免了全局同步。
|類型|是否線程安全?|性能|使用情境|
|---|---|---|---|
|Hashtable| ✅ 是(內建 synchronized)| 慢(鎖住整個 Map) |少量併發讀寫|
|HashMap|❌ 否(非同步)| 快(單執行緒)| 無併發需求|
|SynchronizedMap| ✅ 是(Collections.synchronizedMap())| 一般(方法同步,但遍歷需手動同步)|小規模併發|
|ConcurrentHashMap| ✅ 是(分段鎖)|快(部分同步)|高併發環境|
### synchronized vs Lock 的區別?
- Lock(來自 java.util.concurrent.locks)和 synchronized 都是 Java 中用來確保線程安全的機制,但有以下區別:
|特性|synchronized|Lock(如 ReentrantLock)|
|---|---|---|
|語法|內建語法 (synchronized 關鍵字)|類別 (Lock 介面)|
|可中斷|不可中斷,執行緒必須等待鎖釋放|可中斷(lockInterruptibly())|
|嘗試獲取鎖| 不支持(不能檢查鎖是否被佔用)|支持(tryLock() 可以非阻塞嘗試獲取鎖)|
|公平鎖|不支持(無法設置公平性)|支持(可指定公平鎖 new ReentrantLock(true))|
|條件變數|不支持|支持(使用 newCondition())|
|性能||較低(使用 JVM 內建鎖,可能導致上下文切換)|較高(更靈活,減少上下文切換)|
```Java
public class SynchronizedExample {
private int count = 0;
// 使用 synchronized 關鍵字來保證多執行緒環境下的資料一致性
public synchronized void increment() {count++;}
public synchronized void decrement() { count--;}
public synchronized int getCount() {return count;}
public static void main(String[] args) throws InterruptedException {
SynchronizedExample example = new SynchronizedExample();
// 建立多個執行緒來同時對 count 進行操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.decrement();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + example.getCount());
}
}
```
Lock 範例
```Java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class LockExample {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
```
HashTable 範例
```java
public class HashtableExample {
public static void main(String[] args) {
// 創建一個 Hashtable
Hashtable<String, String> hashtable = new Hashtable<>();
// 設置元素
hashtable.put("A", "Apple");
hashtable.put("B", "Banana");
hashtable.put("C", "Cherry");
// 從 Hashtable 中取出元素
System.out.println("A: " + hashtable.get("A"));
System.out.println("B: " + hashtable.get("B"));
System.out.println("C: " + hashtable.get("C"));
}
}
```
### 在多執行緒環境下,變數可能會被 CPU 緩存暫存,而不是立即寫入主記憶體,導致其他執行緒無法看到最新值。
- 使用 volatile 確保變數的變更立即對所有執行緒可見
```
class VisibilityExample {
private volatile boolean running = true; // 保證可見性
public void stop() {
running = false; // 變更對所有執行緒可見
}
public void run() {
while (running) { // 若無 volatile,可能無法立即看到更新
// do work
}
}
}
```
### 執行緒池(ThreadPoolExecutor)如何運作?
- 核心執行緒 (corePoolSize):最少維持的執行緒數。
- 最大執行緒 (maximumPoolSize):最多可建立的執行緒數。
- 任務佇列 (workQueue):暫存未執行任務。
- 拒絕策略 (RejectedExecutionHandler):當佇列滿時該如何處理新任務。
```
import java.util.concurrent.*;
class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadPoolExecutor.AbortPolicy()
);
executor.execute(() -> System.out.println("Hello from thread pool!"));
executor.shutdown();
}
}
```
### 程序(進程)、執行緒(線程)
> https://oldmo860617.medium.com/%E9%80%B2%E7%A8%8B-%E7%B7%9A%E7%A8%8B-%E5%8D%94%E7%A8%8B-%E5%82%BB%E5%82%BB%E5%88%86%E5%BE%97%E6%B8%85%E6%A5%9A-a09b95bd68dd
線程有哪些狀態?
線程的狀態:
- NEW 尚未啟動
- RUNNABLE 正在執行中
- BLOCKED 阻塞的(被同步鎖或者IO鎖阻塞)
- WAITING 永久等待狀態
- TIMED_WAITING 等待指定的時間重新被喚醒的狀態
- TERMINATED 執行完成
---
## payment 流程圖

## Tools
### Nginx loadbalance

### Websocket
- onOpen
- onClose
- onError
- onMessage
```
## JWT
### JWT 的 payload 通常會放什麼
- user_name , encrypted password
### JWT client 登出後要如何防 token 被拿去再次登入
- 1. 建立黑名單: 當用戶登出或執行某些需要登出的操作時,將 JWT 的唯一識別碼(通常是 JWT 的 jti 聲明)加入到伺服器端的黑名單中。
- 2. 使用 Redis 或資料庫: 將發行過的 JWT 存儲在登出時,從 Redis 或資料庫中刪除
---
## Spring
### java interface abstract
- interface
- interface 用 implements
- interface 每個 function 都需要複寫
- 繼承後可讀寫自己的 function
- interface可以重複 implements
```java
class CCircle implements CShape, BShape
```
- abstrace
- abstrace 用 extends
- 繼承的 function 可覆寫
- 不需要每個 function 都覆寫
- 如父類別有 function 沒覆寫依然可調用
### 如何避免 SQL 注入?
- 使用預處理 PreparedStatement。
- 使用正則表達式過濾掉字符中的特殊字符。
### isBlank 遇到空字串(space)會是 true
```java=
isBlank() vs isEmpty()
public class Main
{
public static void main(String[] args)
{
System.out.println( "ABC".isBlank() ); //false
System.out.println( " ".isBlank() ); //true
System.out.println( "ABC".isEmpty() ); //false
System.out.println( " ".isEmpty() ); //false
}
}
```
### insert update 做 transaction

### finally 是在 return 語句執行之後,return 返回數據之前

### DI(依賴注入)
- 靠Interface介面與依賴注入來實現
- 原則 :
- 高階模組不該依賴於低階模組,兩者都該依賴抽象
- 抽象不依賴具體實作方式,具體實作方式則依賴抽象
### IOC(控制反轉)
- 為一種思想,函式主要功能之外的其他功能應該由第三方完成
原本的關係圖

控制反轉後

使用控制反轉後的關係圖
- 目的都是為了解偶,使程式好維護,且更容易測試
### AOP


### AOP 範例一
```java
@Aspect
@Component
public class SecurityAspect {
@Before("execution(* com.example.controller.*.*(..))")
public void checkAuthorization(JoinPoint joinPoint) {
// 模擬權限檢查
if (!SecurityContextHolder.getContext().isAuthorized()) {
throw new SecurityException("Unauthorized access");
}
}
}
```
### AOP 範例二
```java
@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void handleException(JoinPoint joinPoint, Throwable ex) {
System.out.println("Exception in method: " + joinPoint.getSignature().getName());
System.out.println("Exception message: " + ex.getMessage());
}
}
```
### Bean 生命週期

> https://www.youtube.com/watch?v=atyRpDzfDPs&ab_channel=%E5%B0%9A%E7%A1%85%E8%B0%B7IT%E5%9F%B9%E8%AE%AD%E5%AD%A6%E6%A0%A1
Bean 生命週期
- 1. 創建 class public class persion
- 2. 依賴注入 set value
- 3. 初始化 init-method 前後 加一個步驟 (AOP)
- 4. 使用 excute function
- 5. 銷毀 destroy method
---
## Data Structure
### 編碼
- 1 byte = 8 bit
- 1 bit = 0, 1
- 1 byte = 01100001 (存取資料最小單位)
#### ASCII (char) [1B 7位] <font color=" #B0AAA8">American Standard Code for information Interchange</font>
- 2^7=128 0-127 佔 1 byte (8位 僅用7位) A 是 65, a是 97
#### ISO-Latin-1 [1B 8位]
- 2^8=128 0-255 佔 1 byte (8位) 前 128 跟 ASCII 一致
#### Unicode [2B 16位]
- 處理英文佔 2 byte 非英文 2 byte
#### UTF-8 (Unicode Transformation Format)
- 處理英文佔 1 byte 非英文 3 byte
- 宣告 char 時就是 UTF-8
- windows 慣用 Unicode
- linux 慣用 UTF-8
### 8 種排序法
```
1. 氣泡排序(Bubble sorting)
2. 選擇排序(Selection sorting)
3. 插入排序(Insertion sorting)
4. 快速排序(Quick sorting)
5. 堆積排序(Heap sorting)
6. 薛爾排序(Shell sorting)
7. 合併排序(Merge sorting)
8. 基數排序(Radix sorting)
```
> http://spaces.isu.edu.tw/upload/18833/3/web/sorting.htm
### Big-O

### 加解密
#### 編碼(Encode, Decode)
```
1. EncodeUrl()
2. Base64
3. Huffman Coding
```
#### 加密(Encrypt Decrypted)
```
1. AES 加密
2. RSA 加密
3. md5 密碼加密 (無法反解密)
```
#### 雜湊(Hashing)
```
1. 查看檔案是否相同
```
> https://medium.com/starbugs/what-are-encoding-encrypt-and-hashing-4b03d40e7b0c
### ArrayList 和 LinkedList 的區別是什麼?
數據結構實現:ArrayList 是動態數組的數據結構實現,而 LinkedList 是雙向鍊表的數據結構實現。
隨機訪問效率:ArrayList 比 LinkedList 在隨機訪問的時候效率要高,因為 LinkedList 是線性的數據存儲方式,所以需要移動指針從前往後依次查找。
增加和刪除效率:在非首尾的增加和刪除操作,LinkedList 要比 ArrayList 效率要高,
綜合來說,在需要頻繁讀取集合中的元素時,更推薦使用 ArrayList,而在插入和刪除操作較多時,更推薦使用 LinkedList。
|操作|ArrayList|LinkedList|
|---|---|---|
|查詢|快,O(1):根據索引直接定位|慢,O(n):需竹節點遍歷|
|新增|慢,O(n):插入需移動後續所有元素|快,O(1):只需指針操作|
|刪除|慢,O(n):刪除後需要移動後續所有元素|快,O(1):只需指針操作|
|適用場景|頻繁查詢,較少新增刪除|頻繁新增刪除,查詢次數較少|
### char 和 varchar 的區別是什麼?
- char(n) :固定長度類型,比如訂閱 char(10),當你輸入"abc"三個字符的時候,它們占的空間還是 10 個字節,其他 7 個是空字節。
char 優點:效率高;缺點:占用空間;適用場景:存儲密碼的 md5 值,固定長度的,使用 char 非常合適。
- varchar(n) :可變長度,存儲的值是每個值占用的字節再加上一個用來記錄其長度的字節的長度。
所以,從空間上考慮 varcahr 比較合適;從效率上考慮 char 比較合適,二者使用需要權衡。
### float 和 double 的區別是什麼?
- float 最多可以存儲 8 位的十進位數,並在內存中占 4 字節。
- double 最可可以存儲 16 位的十進位數,並在內存中占 8 字節。
### 如何決定使用 HashMap 還是 TreeMap?
- HashMap:適合快速插入、刪除和查找元素,因為它基於 哈希表 (Hash Table),具有 O(1) 的平均時間複雜度(在哈希函數理想情況下)。
適用於需要快速查找、插入和刪除,而不關心鍵的有序性
- TreeMap, TreeSet:適合需要對鍵進行有序遍歷的情況,因為它基於 紅黑樹 (Red-Black Tree),提供按自然順序或自定義順序排序的功能,操作的時間複雜度是 O(log n)。新增刪除時須進行平衡操作(如旋轉和重染色)
```
左旋
x y
\ / \
y ==> x T3
/ \ / \
T2 T3 T1 T2
右旋
y x
/ \ / \
x T3 ==> T1 y
/ \ / \
T1 T2 T2 T3
重新著色
黑 紅
/ \ / \
紅 紅 ==> 黑 黑
/ /
```

### HashMap 的實現原理?
HashMap 基於 Hash 算法實現的,我們通過 put(key,value)存儲,get(key)來獲取。當傳入 key 時,HashMap 會根據 key. hashCode() 計算出 hash 值,根據 hash 值將 value 保存在 bucket 里。當計算出的 hash 值相同時,我們稱之為 hash 衝突,HashMap 的做法是用鍊表和紅黑樹存儲相同 hash 值的 value。當 hash 衝突的個數比較少時,使用鍊表否則使用紅黑樹。
### 創建線呈方法
#### 繼承 Thread 類並覆寫 run 方法
```java
// 創建線程類,繼承 Thread
class MyThread extends Thread {
@Override
public void run() {
// 線程執行的代碼
for (int i = 1; i <= 5; i++) {
System.out.println("Thread A: " + i);
try {
Thread.sleep(500); // 模擬工作延遲
} catch (InterruptedException e) {
System.err.println("Thread A interrupted");
}
}
}
}
// 主方法測試
public class ThreadExampleA {
public static void main(String[] args) {
// 創建線程對象
MyThread threadA = new MyThread();
// 啟動線程
threadA.start();
}
}
```
#### 實現 Runnable 接口並傳入 Thread
```java
// 創建線程類,實現 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
// 線程執行的代碼
for (int i = 1; i <= 5; i++) {
System.out.println("Thread B: " + i);
try {
Thread.sleep(500); // 模擬工作延遲
} catch (InterruptedException e) {
System.err.println("Thread B interrupted");
}
}
}
}
// 主方法測試
public class ThreadExampleB {
public static void main(String[] args) {
// 創建 Runnable 對象
MyRunnable myRunnable = new MyRunnable();
// 將 Runnable 對象傳入 Thread
Thread threadB = new Thread(myRunnable);
// 啟動線程
threadB.start();
}
}
```
### Thread.start() VS Thread.run()
|Thread.start()|Thread.run()|
|---|---|
|Creates a new thread and the run() method is excuted on the newly created thread.|No new thread is created and the run() method is executed on the calling thread itself.|
|Can't be invoked more than one time otherwise throws java.lang.illegalStateException|Multiple invocation is possible|
|Defined in java.lang.Thread class.|Defined in java.lang.Runnable interface and must be overriden in the implementing class.|
### RPC RMI REST
> https://kknews.cc/zh-tw/code/oq6ll4o.html
---
## Other
### Unit Test JPA
> https://kknews.cc/code/4jrzkzq.html
### design pattern
> https://ithelp.ithome.com.tw/articles/10202075
#### Factory method pattern
```java=
// 產品接口
interface Payment {
void processPayment(double amount);
}
// 具體產品
class CreditCardPayment implements Payment {
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
class PayPalPayment implements Payment {
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
// 工廠方法
abstract class PaymentFactory {
public abstract Payment createPayment();
}
// 具體工廠
class CreditCardPaymentFactory extends PaymentFactory {
public Payment createPayment() {
return new CreditCardPayment();
}
}
class PayPalPaymentFactory extends PaymentFactory {
public Payment createPayment() {
return new PayPalPayment();
}
}
// 用戶端代碼
public class FactoryMethodExample {
public static void main(String[] args) {
PaymentFactory factory = new CreditCardPaymentFactory();
Payment payment = factory.createPayment();
payment.processPayment(100.0);
factory = new PayPalPaymentFactory();
payment = factory.createPayment();
payment.processPayment(200.0);
}
}
```
```java=
public class DatabaseConnectionFactory {
public static DatabaseConnection getConnection(String type) {
switch (type) {
case "MySQL":
return MySQLConnection.getInstance();
case "PostgreSQL":
return PostgreSQLConnection.getInstance();
case "Oracle":
return OracleConnection.getInstance();
default:
throw new IllegalArgumentException("Unsupported database type: " + type);
}
}
}
```
### Singleton Pattern
- 需要唯一的共享實例:
例如,應用程序的配置管理器、數據庫連接池、日誌系統等。
```java=
class Logger {
private static Logger instance;
private Logger() {
// 私有構造方法,防止外部創建
}
public static Logger getInstance() {
if (instance == null) {
synchronized (Logger.class) {
if (instance == null) {
instance = new Logger();
}
}
}
return instance;
}
public void log(String message) {
System.out.println("Log: " + message);
}
}
```
```java=
Logger logger1 = Logger.getInstance();
Logger logger2 = Logger.getInstance();
logger1.log("This is a log message."); // Output: Log: This is a log message.
System.out.println(logger1 == logger2); // Output: true
```
#### Stretegy Pattern
```java=
// 策略接口
interface DiscountStrategy {
double applyDiscount(double price);
}
// 具體策略
class ChristmasDiscount implements DiscountStrategy {
public double applyDiscount(double price) {
return price * 0.8; // 20% 折扣
}
}
class BlackFridayDiscount implements DiscountStrategy {
public double applyDiscount(double price) {
return price * 0.5; // 50% 折扣
}
}
class NoDiscount implements DiscountStrategy {
public double applyDiscount(double price) {
return price; // 沒有折扣
}
}
// 上下文
class ShoppingCart {
private DiscountStrategy discountStrategy;
public ShoppingCart(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double calculateTotal(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 用戶端代碼
public class StrategyPatternExample {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart(new NoDiscount());
System.out.println("Total with no discount: $" + cart.calculateTotal(100.0));
cart.setDiscountStrategy(new ChristmasDiscount());
System.out.println("Total with Christmas discount: $" + cart.calculateTotal(100.0));
cart.setDiscountStrategy(new BlackFridayDiscount());
System.out.println("Total with Black Friday discount: $" + cart.calculateTotal(100.0));
}
}
```
### Java spring boot 如何做到 cache
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
```java
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class UserService {
// 緩存名稱為 "users",key 為 userId
@Cacheable(value = "users", key = "#userId")
public String getUserById(String userId) {
// 模擬慢查詢
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "User-" + userId;
}
// 清除緩存
@CacheEvict(value = "users", key = "#userId")
public void evictCache(String userId) {
System.out.println("Cleared cache for userId: " + userId);
}
}
```
如何讓多個機器訂閱一個 TOPIC 並且清 cache
如何不重複處理資料 例如重複發傭金給大家
Concurant HashMap 如何使用
Oracle 如何做到簡易的 sql lock
Store procedure
### Heap Stack
在系統中,Heap 和 Stack 是兩種內存分配的區域
#### Stack(堆疊)
- 用途
- 存儲方法的局部變量(例如基本數據類型:int、float 或對象的引用)。
- 用於方法的調用與返回,包括函數調用過程中的參數和返回地址。
- 特點
- 結構:基於 LIFO(後進先出)的結構。
- 大小:由操作系統分配,大小固定(一般較小)。
- 分配和釋放速度:快,由系統自動管理內存的分配和釋放。
- 生命週期:變量的生命週期與方法或作用域一致,方法執行結束後,內存會自動釋放。
- 常見問題
- 若遞歸太深或分配太多內存,可能導致 Stack Overflow(堆疊溢出)。
- 使用範例
```
public void example() {
int x = 10; // x 是局部變量,存放在 Stack 中
}
```
#### Heap(堆)
- 用途
- 存儲動態分配的對象和數據。
- 主要用於全局變量和需要在多個方法間共享的對象。
- 特點
- 結構:無固定結構,內存按需動態分配。
- 大小:一般比 Stack 大,受系統物理內存限制。
- 分配和釋放速度:較慢,由程序員顯式分配與釋放內存(如 C++),或依靠垃圾回收機制(如 Java)。
- 生命週期:可以比方法或作用域的生命週期更長,直到顯式釋放或垃圾回收。
- 常見問題
- 若未正確釋放內存,可能導致 Memory Leak(內存洩漏)。
- 使用範例
```
public void example() {
String s = new String("Heap Example"); // s 指向的對象分配在 Heap 中
}
```
|特性|Stack(堆疊)|Heap(堆)|
|---|---|---|
|用途|局部變量、方法調用|動態分配的對象和全局變量|
|分配方式|自動管理|需要手動分配或垃圾回收|
|分配速度|快|慢|
|內存大小|固定且較小|動態調整且通常較大|
|生命週期|與作用域一致|持久,直到釋放或垃圾回收|
```
Java 的heap堆是一個運行時數據區,類的(對象從中分配空間。
這些對象通過new、newarray、anewarray和multianewarray等指令建立,
它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,
生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,
Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,
由於要在運行時動態分配內存,存取速度較慢。
「stack棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。
但缺點是,存在棧中的數據大小與生存期必須是確定的,
缺乏靈活性。棧中主要存放一些基本類型的變量(,
int, short, long, byte, float, double, boolean, char
)和對象句柄(引用)。 」
```
### Java 中垃圾回收如何處理 Heap 內存
- Java 的垃圾回收(Garbage Collection, GC)是一個自動化的內存管理機制,專門負責清理 Heap 中無法再被訪問的對象,以釋放內存。以下是其工作原理與詳細說明:
### 同一個 class 如果 A B function 都掛 Transational 兩這互摳會失效
#### Spring AOP 基於代理的機制
- @Transactional 是通過 Spring AOP 實現的,默認使用代理對象(proxy object)來管理事務。
當調用方法時,只有通過代理對象進行的方法調用才能觸發 AOP 攔截,進而啟動或管理事務。
如果 function A 在同一個類中直接調用 function B,這是一個普通的內部方法調用,不會通過代理對象,導致 function B 的 @Transactional 不生效。
代理範圍限制
- Spring 代理僅作用於 Spring 容器管理的對象之間的調用,內部調用無法通過代理攔截。
解法 1: 把被調用的方法提取到另一個類中
將 function B 提取到另一個 Spring 管理的 Bean 中,並通過注入的方式調用。
```java
@Component
public class ServiceB {
@Transactional
public void functionB() {
// 方法 B 的邏輯
}
}
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void functionA() {
// 方法 A 的邏輯
serviceB.functionB(); // 這裡通過代理調用
}
}
```
JVM
Process thread
thread.local
JDK21
服務註冊 Nokose
用戶網站 代理往 管理後臺
websocket
快速引擎 定時累計
|概念|定義|與 JVM 關係|
|---|---|---|
|JVM|Java Virtual Machine,運行 Java 程式的環境|以 Process 形式 存在|
|Process|執行中的應用程式實例|JVM 是一個 Process|
|Thread|Process 中的執行單元|JVM 內部會有 多個執行緒(Thread)|
JVM、Process、Thread 關係圖
```lua
+-----------------------------------+
| JVM Process |
| +---------------------------+ |
| | Main Thread (執行 main) | |
| +---------------------------+ |
| | Garbage Collection Thread | |
| +---------------------------+ |
| | JIT Compilation Thread | |
| +---------------------------+ |
| | Finalizer Thread | |
| +---------------------------+ |
+-----------------------------------+
```
## Thread Safety
1️⃣ String(字串)
String 在 Java 中是不可變 (immutable) 的,因此天生執行緒安全,無需額外處理。
但是,如果你要在多執行緒環境下拼接或修改字串,應該考慮使用:
✅ StringBuffer(執行緒安全,方法有 synchronized)
✅ StringBuilder(非執行緒安全,如果需要執行緒安全,外部需同步)
```
StringBuffer sb = new StringBuffer(); // 執行緒安全
sb.append("Hello");
StringBuilder sb2 = new StringBuilder(); // 需額外同步
synchronized (sb2) {
sb2.append("World");
}
```
2️⃣ ArrayList(動態陣列)
ArrayList 不是執行緒安全的,如果多個執行緒同時修改它,可能導致 ConcurrentModificationException。
📌 解決方案:
✅ Collections.synchronizedList(new ArrayList<>())(透過內部 synchronized 保證安全)
```
List<String> list1 = Collections.synchronizedList(new ArrayList<>());
// 使用時仍需手動同步迭代
synchronized (list1) {
for (String s : list1) {
System.out.println(s);
}
}
```
3️⃣ int(基本型別)
int 不是執行緒安全的,因為 ++ 和 -- 這類操作不是原子操作。
📌 解決方案:
✅ AtomicInteger(提供原子操作)
✅ synchronized(確保同一時間只有一個執行緒修改)
```
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet(); // 原子遞增
atomicInt.decrementAndGet(); // 原子遞減
// 傳統 synchronized 保護 int
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
```
4️⃣ HashMap(雜湊表)
HashMap 不是執行緒安全的,在多執行緒環境下可能發生「死循環」等問題。
📌 解決方案:
✅ Collections.synchronizedMap(new HashMap<>())(內部加 synchronized)
✅ ConcurrentHashMap(高效執行緒安全的 Map)
```
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
// 使用時仍需手動同步迭代
synchronized (syncMap) {
for (Map.Entry<String, String> entry : syncMap.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("A", "Apple");
```
|資料型態|是否執行緒安全|建議替代方案|
|---|---|---|
|String|✅(不可變)|StringBuffer / StringBuilder(需同步)|
|ArrayList|❌(非執行緒安全)|Collections.synchronizedList / CopyOnWriteArrayList|
|int|❌(非執行緒安全)|AtomicInteger / synchronized 方法
|HashMap|❌(非執行緒安全)|ConcurrentHashMap / Collections.synchronizedMap|
```java
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class ConcurrentHashMapStreamExample {
public static void main(String[] args) {
// 建立 ConcurrentHashMap
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("A", "Apple");
map.put("B", "Banana");
map.put("C", "Cherry");
map.put("D", "Date");
map.put("E", "Elderberry");
// 使用 Stream 操作 ConcurrentHashMap
ConcurrentHashMap<String, String> result = map.entrySet().stream()
.filter(entry -> entry.getValue().length() > 5) // 過濾 value 長度 > 5
.collect(Collectors.toConcurrentMap(
Map.Entry::getKey,
entry -> entry.getValue().toUpperCase(), // 轉換為大寫
(e1, e2) -> e1, // 處理 key 衝突(這裡不會發生)
ConcurrentHashMap::new
));
// 使用 forEach (避免 for 迴圈)
result.forEach((key, value) -> System.out.println(key + " -> " + value));
}
}
```
```java
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class SynchronizedListStreamExample {
public static void main(String[] args) {
// 建立執行緒安全的 List
List<String> list = Collections.synchronizedList(new ArrayList<>());
list.add("Apple");
list.add("Banana");
list.add("Cherry");
list.add("Date");
list.add("Elderberry");
// 使用 Stream 來處理(無需 synchronized)
List<String> result = list.stream()
.filter(s -> s.length() > 5) // 過濾長度 > 5 的字串
.map(String::toUpperCase) // 轉換為大寫
.collect(Collectors.toList()); // 收集成新的 List
// 輸出結果
result.forEach(System.out::println);
}
}
```
```java
import java.sql.*;
import java.util.*;
import java.util.concurrent.*;
public class InventorySync {
private static final int BATCH_SIZE = 100;
private static final int THREAD_POOL_SIZE = 5; // 可根據 CPU 調整
private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
private static final String DB_USER = "your_user";
private static final String DB_PASSWORD = "your_password";
public static void main(String[] args) throws SQLException {
List<Map<String, Object>> products = fetchProductsFromATable();
syncToBTableWithThreads(products);
}
// 1️⃣ **從 A Table 撈取所有商品**
private static List<Map<String, Object>> fetchProductsFromATable() throws SQLException {
List<Map<String, Object>> productList = new ArrayList<>();
String query = "SELECT id, name, stock, price FROM A_Table";
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Map<String, Object> product = new HashMap<>();
product.put("id", rs.getInt("id"));
product.put("name", rs.getString("name"));
product.put("stock", rs.getInt("stock"));
product.put("price", rs.getDouble("price"));
productList.add(product);
}
}
return productList;
}
// 2️⃣ **使用多執行緒同步商品至 B Table**
private static void syncToBTableWithThreads(List<Map<String, Object>> products) {
ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
// 依照 BATCH_SIZE 分批處理
int totalSize = products.size();
int batchCount = (int) Math.ceil((double) totalSize / BATCH_SIZE);
for (int i = 0; i < batchCount; i++) {
int fromIndex = i * BATCH_SIZE;
int toIndex = Math.min(fromIndex + BATCH_SIZE, totalSize);
List<Map<String, Object>> batch = products.subList(fromIndex, toIndex);
// 提交執行緒
executor.submit(() -> insertBatchIntoBTable(batch));
}
// 3️⃣ **關閉執行緒池**
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES); // 最長等 10 分鐘
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 4️⃣ **批次插入 B Table**
private static void insertBatchIntoBTable(List<Map<String, Object>> batch) {
String insertSQL = "INSERT INTO B_Table (id, name, stock, price) VALUES (?, ?, ?, ?) "
+ "ON DUPLICATE KEY UPDATE stock = VALUES(stock), price = VALUES(price)";
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
PreparedStatement stmt = conn.prepareStatement(insertSQL)) {
for (Map<String, Object> product : batch) {
stmt.setInt(1, (int) product.get("id"));
stmt.setString(2, (String) product.get("name"));
stmt.setInt(3, (int) product.get("stock"));
stmt.setDouble(4, (double) product.get("price"));
stmt.addBatch();
}
stmt.executeBatch(); // 一次插入 100 筆
System.out.println("✅ 插入 " + batch.size() + " 筆資料至 B Table");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
```
### 透過 applicationContext.getBean(conditionName) 會根據 condition_name 來獲取對應的 Spring Bean
```
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ConditionExecutor {
@Autowired
private CampaignRepository campaignRepository;
@Autowired
private ApplicationContext applicationContext;
public void executeConditions(String campaign, String status) {
List<CampaignCondition> conditions = campaignRepository.findByCampaignAndStatus(campaign, status);
for (CampaignCondition condition : conditions) {
executeCondition(condition.getConditionName());
}
}
private void executeCondition(String conditionName) {
try {
ConditionInterface condition = (ConditionInterface) applicationContext.getBean(conditionName);
boolean result = condition.Condition();
System.out.println("執行 " + conditionName + " 結果: " + result);
} catch (Exception e) {
System.out.println("找不到對應的 Bean:" + conditionName);
}
}
}
```
## GC
### Serial GC(串行回收器)
- 單執行緒執行垃圾回收(STW,Stop-The-World)。
- 適用於單核心 CPU 和 小型 Java 應用(如 IoT、桌面應用)。
- 優勢:記憶體佔用低,適合單執行緒環境。
- 劣勢:當 GC 執行時,應用程式會完全停止,影響效能。
```
java -XX:+UseSerialGC -jar myapp.jar
```
```java
public class SerialGCDemo {
public static void main(String[] args) {
System.out.println("Using Serial GC");
for (int i = 0; i < 100000; i++) {
String[] data = new String[1000];
}
System.gc();
}
}
```
### Parallel GC(並行回收器)
- 多執行緒並行回收(提升吞吐量,適合多核心 CPU)。
- 預設 GC,在 Java 8~Java 17 中 預設使用 Parallel GC。
- 優勢:適合批量處理,GC 速度快,吞吐量高。
- 劣勢:STW 時間較長(可能影響響應時間)。
```
java -XX:+UseParallelGC -XX:ParallelGCThreads=4 -jar myapp.jar
```
```
public class ParallelGCDemo {
public static void main(String[] args) {
System.out.println("Using Parallel GC");
for (int i = 0; i < 500000; i++) {
int[] data = new int[1000];
}
System.gc();
}
}
```
### G1 GC(Garbage First)
區塊式管理記憶體,避免長時間 STW。
預測性低延遲 GC,適合 大型 Java 服務(記憶體 >=4GB)。
優勢:平衡吞吐量與低延遲。
劣勢:高記憶體使用量,且回收時 CPU 負載較高。
適用場景: ✅ 大型 Web 應用(如 Spring Boot + MySQL)
✅ 微服務架構
✅ 記憶體 4GB 以上的應用程式
✅ 需要低延遲(RT < 100ms)的大型系統
```
java -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -jar myapp.jar
```
### ZGC(低延遲回收器) / Shenandoah GC
ZGC(JDK 11+) 和 Shenandoah GC(JDK 12+) 都是 低延遲 GC。
最大 STW 低於 10ms,適合高併發、即時回應系統。
優勢:STW 超低(適合高併發 Web 服務)。
劣勢:記憶體佔用較高,ZGC 需要 JDK 11+。
適用場景: ✅ 高併發、高可用 Web 應用(如 API 服務、交易系統)
✅ 即時性要求高的應用(金融系統、遊戲伺服器)
✅ 記憶體 8GB 以上的大型系統
```
java -XX:+UseZGC -Xmx8G -jar myapp.jar
```

## Java 使用 JMC(JDK Mission Control) 監控 CPU Memory
```
jmc
```
1. 在 JMC 中選擇 File → Open File,然後選擇 app.jfr 進行分析。
2.你可以看到:
- CPU 使用情況
- Memory 佔用
- GC 活動
- 執行緒狀態
- 方法調用時間(適用於 profile 設定)



```java
String[] words = inputText.split(" ");
String string = "123";
int[] array1 = {1,2,3,4,5};
int faces = 6; // 每顆骰子有六面
List<List<Integer>> combinations = new ArrayList<>();
Map<String, String> map = new HashMap<>();
HashSet<Character> set = new HashSet<>();
for (int right = 0; right < s.length(); right++) {
while (set.contains(s.charAt(right))) {
set.remove(s.charAt(left));
left++;
}
set.add(s.charAt(right));
maxLength = Math.max(maxLength, right-left+1);
}
public class Main {
public static void main(String[] args) {
public static int[] function(){
}
}
}
```
## Spring cloud 架構
- API Gateway (Spring Cloud Gateway):負責流量轉發與 API 路由
- Service Discovery (Eureka / Consul / Nacos):提供動態服務註冊與發現(動態擴展)
- Load Balancer (Ribbon / Spring Cloud LoadBalancer):負責請求負載均衡
- Microservices (Spring Boot Services):提供業務邏輯
- Database (MySQL / PostgreSQL / MongoDB / Redis):儲存應用資料
- Message Queue (Kafka / RabbitMQ):解耦系統、處理異步通訊
- Logging & Monitoring (ELK / Prometheus / Grafana):監控與日誌分析
- Configuration Management (Spring Cloud Config):集中管理配置
- Kubernetes / Docker (可選):作為部署與運行環境
```
┌─────────────────────────────┐
│ Clients (Web / App) │
└───────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ API Gateway (Zuul / SCG)│
│ (Load Balancer & Security) │
└───────────┬────────────────┘
│
┌────────────────────────────┴──────────────────────────┐
│ │
▼ ▼
┌──────────────┐ ┌───────────────┐
│ Service Reg. │ │ Config Server │
│ (Eureka) │ <──────────────┐ │ (Spring Cloud)│
└──────────────┘ │ └───────────────┘
▲ │
│ ▼
┌──────────────┐ ┌──────────────────┐
│ LoadBalancer │ │ Message Bus │ (Kafka / RabbitMQ)
│ (Ribbon/LB) │ └──────────────────┘
└──────────────┘
│
▼
┌───────────────────────┬───────────────────────┬───────────────────────┐
│ Service A │ Service B │ Service C │
│ (Order Service) │ (User Service) │ (Payment Service) │
│ Spring Boot App │ Spring Boot App │ Spring Boot App │
└───────────────────────┴───────────────────────┴───────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ MySQL (Order DB) │ │ PostgreSQL (User │ │ Redis (Session) │
│ │ │ Data) │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```
## AWS 動態擴展 EC2 Auto Scaling Group (ASG)
```
Clients (Web / App)
│
▼
┌────────────────────┐
│ AWS Application │
│ Load Balancer│
└────────▲───────────┘
│
▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ EC2 (App 1) │ │ EC2 (App 2) │ ... │ EC2 (App n) │
└───────────────┘ └───────────────┘ └───────────────┘
▲
│
┌──────────────────┐
│ Auto Scaling Group│ (根據 CPU/Memory/Network 負載動態擴展)
└──────────────────┘
```
## JAVA 注入方式
### 1️⃣ 構造函數注入 (Constructor Injection)
```
@Service
public class UserService {
public String getUserName() {
return "Alice";
}
}
@Controller
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public String getUser() {
return userService.getUserName();
}
}
```
### 2️⃣ Setter 方法注入 (Setter Injection)
```
@Service
public class OrderService {
public String getOrder() {
return "Order#123";
}
}
@Controller
public class OrderController {
private OrderService orderService;
@Autowired
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
public String getOrderDetails() {
return orderService.getOrder();
}
}
```
### 3️⃣ 字段 (屬性) 注入 (Field Injection)
直接在字段上使用 @Autowired 來注入依賴。
```
@Service
public class PaymentService {
public String processPayment() {
return "Payment Successful";
}
}
@Controller
public class PaymentController {
@Autowired
private PaymentService paymentService;
public String makePayment() {
return paymentService.processPayment();
}
}
```
## Unit Test
|Mock 套件|特色|適用情境|
|---|---|---|
|Mockito|最流行、易學習|一般單元測試|
|PowerMock|可以 Mock static 方法、final 類別|需要 Mock 靜態方法|
|JMockit|強大,支援 Mock private 方法、構造函數|需要 Mock 私有/構造函數|
|EasyMock|內建驗證功能,類似 Mockito|需要驗證 Mock 行為|
### Mockito
```
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
class UserServiceTest {
@Test
void testGetUser() {
// 1️⃣ 建立 Mock 物件
UserRepository mockRepo = mock(UserRepository.class);
// 2️⃣ 定義 Mock 行為
when(mockRepo.getUserById(1)).thenReturn(new User(1, "John Doe"));
// 3️⃣ 測試目標
UserService userService = new UserService(mockRepo);
User user = userService.getUserById(1);
// 4️⃣ 驗證結果
assertEquals("John Doe", user.getName());
// 5️⃣ 驗證方法是否被呼叫
verify(mockRepo).getUserById(1);
}
}
```
### JMockit
```
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import mockit.Deencapsulation;
import mockit.Expectations;
import mockit.Mocked;
class UserServiceTest {
@Test
void testPrivateMethod(@Mocked UserService userService) {
// 1️⃣ 模擬私有方法
new Expectations() {{
Deencapsulation.invoke(userService, "getSecretKey");
result = "MockedKey";
}};
// 2️⃣ 測試方法
String key = Deencapsulation.invoke(userService, "getSecretKey");
// 3️⃣ 驗證結果
assertEquals("MockedKey", key);
}
}
```