<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://xcv11xcv22.github.io/bardlee.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xcv11xcv22.github.io/bardlee.github.io/" rel="alternate" type="text/html" /><updated>2025-12-02T18:47:37+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/feed.xml</id><title type="html">Bard的黃金屋</title><subtitle>你需要的</subtitle><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><entry><title type="html">⭐AI聊天室專案-EchoMind介紹</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/AI%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%B0%88%E6%A1%88-EchoMind%E4%BB%8B%E7%B4%B9/" rel="alternate" type="text/html" title="⭐AI聊天室專案-EchoMind介紹" /><published>2025-10-15T00:00:00+00:00</published><updated>2025-10-15T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/%E2%AD%90AI%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%B0%88%E6%A1%88-EchoMind%E4%BB%8B%E7%B4%B9</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/AI%E8%81%8A%E5%A4%A9%E5%AE%A4%E5%B0%88%E6%A1%88-EchoMind%E4%BB%8B%E7%B4%B9/"><![CDATA[<h2 id="echomind我獨立打造的-ai-實驗聊天室想為聊天重新點火">EchoMind：我獨立打造的 AI 實驗聊天室，想為聊天重新點火</h2>

<p>你還記得「聊天室」最熱鬧的時代嗎？<br />
曾經的它，是交朋友、聊天、分享生活最溫暖的地方。如今，它幾乎被演算法社群取代。</p>

<p>我一直在想：<br />
<strong>如果把最新的 AI 技術放進聊天室，會不會重新變得好玩？</strong></p>

<p>帶著這個念頭，我用半年時間，一個人從零打造了 <strong>EchoMind</strong> ——<br />
一個能和 AI 一起聊天、一起探索、一起創造的實驗性聊天室。</p>

<hr />

<h2 id="為什麼做-echomind">為什麼做 EchoMind？</h2>

<p>我想用 AI，為快消失的聊天室文化點一把新火</p>

<p>大多數 AI 都停留在一問一答、工具性、缺乏人味。<br />
而聊天室，曾經是最有溫度的互動場域。</p>

<p>我開始想像：</p>

<p>🤖 如果 AI 是聊天室中的一個角色，而不是客服？<br />
🔥 如果讓人類與 AI 一起聊天，而不是輪流輸入？<br />
🎭 如果進聊天室就能擁有 AI 為你生成的獨特身份？</p>

<p>我想用 AI 重新賦予「聊天」新的形式。<br />
EchoMind 就是這個念頭的實驗。</p>

<hr />

<h2 id="我如何讓-ai像在聊天">我如何讓 AI「像在聊天」</h2>

<p>我想讓 EchoMind 的 AI 更像一個會聊天的角色，因此做了三件事：</p>

<ul>
  <li>
    <p><strong>Token 串流回覆（WebSocket）</strong><br />
  AI 不是等想完才回，而是像人在打字，一句一句出來，節奏更自然</p>
  </li>
  <li>
    <p><strong>Redis 短期記憶（依使用者）</strong><br />
  AI 會記住最近聊過的內容，不會每句都像第一次見面</p>
  </li>
  <li>
    <p><strong>RAG（Postgres + pgvector + BGE-M3）</strong><br />
  用向量搜尋補強知識，不只陪聊，也能講得準、有內容</p>
  </li>
</ul>

<p>這 3 件事的目標很簡單：<br />
<strong>AI 不只是回你，而是能接你話、記得你、講得更貼近你。</strong></p>

<hr />

<h2 id="技術架構亮點">技術架構亮點</h2>

<p>EchoMind 的架構不是堆技術，而是為了能長大。</p>

<ul>
  <li>
    <p><strong>前端：React + WebSocket</strong><br />
  多人聊天室、即時互動、AI 串流都在這層體現</p>
  </li>
  <li>
    <p><strong>後端：Spring Boot</strong><br />
  管理房間、消息轉發、使用者狀態，並直接在 Java 生態執行 <strong>ONNX Runtime</strong><br />
  GAN 生圖放這層，是為了更順的進場體驗與更低延遲，並使用cpu推論onnx</p>
  </li>
  <li>
    <p><strong>AI 層：FastAPI（LLM + RAG）</strong><br />
  與聊天服務解耦，模型可替換，可擴展不同 AI 角色</p>
  </li>
</ul>

<hr />

<h2 id="我為什麼用-onnx-在-spring-boot-做生成">我為什麼用 ONNX 在 Spring Boot 做生成？</h2>

<p>我把 <strong>GAN 模型從 PyTorch 轉為 ONNX</strong>，並在 Spring Boot 直接推理，而不是放 AI 層，原因有兩個：</p>

<ul>
  <li>CPU推論我訓練的Gan速度非常快</li>
  <li>讓「進入聊天室就拿到角色」這件事更即時、更順暢。</li>
</ul>

<p>這是一個 <strong>體驗優先 × 架構思考</strong> 的取捨：</p>

<ul>
  <li>
    <p>不用跨服務請求 → 延遲更低</p>
  </li>
  <li>
    <p>角色生成與登入體驗緊密結合</p>
  </li>
  <li>
    <p>AI 層維持乾淨，專注對話與 RAG</p>
  </li>
</ul>

<hr />

<h3 id="部署與對外體驗cloudflare-pages--minikube--cloudflare-tunnel">部署與對外體驗：Cloudflare Pages × minikube × Cloudflare Tunnel</h3>

<p>我希望 EchoMind 不只是「我自己能跑」，而是任何人都能直接體驗。<br />
因此我採用了前後端分離且具產品化思維的部署方式：</p>

<ul>
  <li>
    <p><strong>前端（React）</strong>：部署於 <strong>Cloudflare Pages</strong>，由全球 CDN 加速提供 HTTPS 入口，同時具備 SEO 友善特性，讓使用者第一眼就像在使用正式產品</p>
  </li>
  <li>
    <p><strong>後端（Spring Boot + ONNX、生圖、FastAPI、Postgres、Redis、Rabbitmq）</strong>：運行在本機 <strong>minikube</strong>，保持低成本、高掌控度</p>
  </li>
  <li>
    <p><strong>對外連線</strong>：透過 <strong>Cloudflare Tunnel</strong> 將後端 API 安全公開成自有子網域 <strong>api.bardcloud.online</strong>，不需公網 IP，也不用自己處理 SSL</p>
  </li>
</ul>

<hr />

<h2 id="如果你願意我希望你能親自進來玩玩">如果你願意，我希望你能親自進來玩玩</h2>

<p>🔗 Demo 入口：<a href="https://chat.bardcloud.online" target="_blank">AI聊天室專案-EchoMind</a></p>

<p>💻  GitHub：<a href="https://github.com/lipeijia/chat-room" target="_blank">前後端原碼</a>, <a href="https://github.com/xcv11xcv22/chat-room-bot" target="_blank">AI層原碼</a></p>

<p>如果你想聊聊這個專案、AI、產品開發，或你覺得我們可以激出什麼火花，歡迎找我。</p>

<hr />

<h2 id="使用教學">使用教學</h2>

<p><strong>要使用ai功能需選擇Ai為說話對象</strong></p>
<h3 id="手機版">手機版</h3>

<p><img src="https://img.bardcloud.online/ai_project/Screenshot-20251016-074031-Firefox.jpg" alt="Screenshot-20251016-074031-Firefox" border="0" /></p>

<p><img src="https://img.bardcloud.online/ai_project/Screenshot-20251016-074059-Firefox.jpg" alt="Screenshot-20251016-074059-Firefox" border="0" /></p>

<p><img src="https://img.bardcloud.online/ai_project/Screenshot-20251016-074136-Firefox.jpg" alt="Screenshot-20251016-074136-Firefox" border="0" /></p>

<h3 id="電腦版">電腦版</h3>

<p><a href="https://img.bardcloud.online/ai_project/1.png"><img src="https://img.bardcloud.online/ai_project/1.png" alt="1" border="0" /></a></p>

<p><a href="https://img.bardcloud.online/ai_project/2.png"><img src="https://img.bardcloud.online/ai_project/2.png" alt="2" border="0" /></a></p>

<h4 id="畫柴犬教學">畫柴犬教學</h4>

<p>1.輸入「畫柴犬」至輸入框後送出</p>

<p><a href="https://img.bardcloud.online/ai_project/draw1.png"><img src="https://img.bardcloud.online/ai_project/draw1.png" alt="draw1" border="0" /></a></p>

<p>2.可點擊圖放大檢視</p>

<p><a href="https://img.bardcloud.online/ai_project/draw2.png"><img src="https://img.bardcloud.online/ai_project/draw2.png" alt="draw2" border="0" /></a></p>

<p>3.位於圖片右上角的X可關閉圖片</p>

<p><a href="https://img.bardcloud.online/ai_project/draw3.png"><img src="https://img.bardcloud.online/ai_project/draw3.png" alt="draw3" border="0" height="927" loading="lazy" decoding="async" fetchpriority="low" /></a></p>

<h2 id="demo">👍Demo</h2>

<h3 id="streaming回覆">Streaming回覆</h3>

<p><a href="https://img.bardcloud.online/ai_project/streaming.gif"><img src="https://img.bardcloud.online/ai_project/streaming.gif" alt="streaming.gif" border="0" /></a></p>

<h3 id="onnx柴犬成像圖片">Onnx柴犬成像圖片</h3>

<p><img src="https://img.bardcloud.online/ai_project/dog1.jpg" alt="柴犬1" loading="lazy" decoding="async" height="832" /></p>

<p><img src="https://img.bardcloud.online/ai_project/dog2.png" alt="柴犬2" loading="lazy" decoding="async" height="832" /></p>

<h3 id="上下文記憶">上下文記憶</h3>

<p><a href="https://img.bardcloud.online/ai_project/memory.png?n=1"><img src="https://img.bardcloud.online/ai_project/memory.png?n=1" alt="上下文記憶" border="0" height="952" loading="lazy" decoding="async" fetchpriority="low" /></a></p>

<h3 id="iphone17-rag回覆">IPhone17 rag回覆</h3>

<p><a href="https://img.bardcloud.online/ai_project/iphone1.png"><img src="https://img.bardcloud.online/ai_project/iphone1.png" alt="iphone1" border="0" /></a></p>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="作品" /><summary type="html"><![CDATA[EchoMind：我獨立打造的 AI 實驗聊天室，想為聊天重新點火]]></summary></entry><entry><title type="html">⭐近期專案介紹</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/%E8%BF%91%E6%9C%9F%E5%B0%88%E6%A1%88%E4%BB%8B%E7%B4%B9/" rel="alternate" type="text/html" title="⭐近期專案介紹" /><published>2025-10-14T00:00:00+00:00</published><updated>2025-10-14T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/%E2%AD%90%E8%BF%91%E6%9C%9F%E5%B0%88%E6%A1%88%E4%BB%8B%E7%B4%B9</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/%E8%BF%91%E6%9C%9F%E5%B0%88%E6%A1%88%E4%BB%8B%E7%B4%B9/"><![CDATA[<h1 id="專案經歷">專案經歷</h1>

<p>前期進入遊戲產業以C# Unity為主, pixi無輔的開發了約接近4年的遊戲</p>

<p>後來接續開發過python為主 爬蟲, tkinder DeviceTree產生器, pyside  電動起子控制器</p>

<p>C#為主的  WPF, Asp.net MVC, Azure, WebAPi ,python為輔助的專案
智能包廂,智能咖啡機, Beacon, 行動電源, appium 爬蟲等等</p>

<h1 id="智能包廂系統">智能包廂系統</h1>

<div id="pdfv-projects-e6-99-ba-e8-83-bd-e5-8c-85-e5-bb-82-e7-b3-bb-e7-b5-b1-pdf" class="pdfv-wrap" data-src="https://img.bardcloud.online/assets/pdfjs/web/viewer.html?v=2&amp;file=/projects/%E6%99%BA%E8%83%BD%E5%8C%85%E5%BB%82%E7%B3%BB%E7%B5%B1.pdf#zoom=page-width&amp;pagemode=thumbs&amp;disableAutoFetch=true&amp;disableStream=true" data-height="780">
    <div class="pdfv-placeholder" style="border:1px solid #e5e7eb; background:#f8f8f8; height: 780px; display:flex; align-items:center; justify-content:center; flex-direction:column;">
      <p style="margin:0 0 .5rem; color:#555;">PDF 尚未載入</p>
      <button type="button" class="pdfv-load-btn" style="padding:.5rem 1rem; border:0; border-radius:.5rem; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,0.1);">
        點我載入
      </button>
    </div>
  </div>

<p style="margin:.5rem 0 1rem;">
    <a href="https://img.bardcloud.online/projects/智能包廂系統.pdf" download="">下載原始 PDF</a>
  </p>

<script>
  (function(){
    if (window.__pdfvInit) return; window.__pdfvInit = true;

    function mountIframe(wrapper){
      if (!wrapper || wrapper.dataset.mounted) return;
      var src = wrapper.getAttribute('data-src');
      var h   = wrapper.getAttribute('data-height') || '780';
      var iframe = document.createElement('iframe');
      iframe.width = '100%';
      iframe.height = h;
      iframe.style.border = '0';
      iframe.src = src;
      wrapper.innerHTML = '';
      wrapper.appendChild(iframe);
      wrapper.dataset.mounted = '1';
    }

    // 點擊「點我載入」按鈕
    document.addEventListener('click', function(ev){
      var btn = ev.target.closest('.pdfv-load-btn');
      if (!btn) return;
      var wrap = btn.closest('.pdfv-wrap');
      mountIframe(wrap);
    });

    // 捲到可視範圍附近自動載入
    function setupIO(){
      if (!('IntersectionObserver' in window)) {
        document.querySelectorAll('.pdfv-wrap').forEach(mountIframe);
        return;
      }
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting) { mountIframe(e.target); io.unobserve(e.target); }
        });
      }, { rootMargin: '200px' });
      document.querySelectorAll('.pdfv-wrap').forEach(function(w){ io.observe(w); });
    }

    // 延後到 DOM ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', setupIO);
    } else {
      setupIO();
    }
  })();
  </script>

<h1 id="beacon廣播系統">Beacon廣播系統</h1>

<div id="pdfv-projects-beacon-e5-bb-a3-e6-92-ad-e7-b3-bb-e7-b5-b1-pdf" class="pdfv-wrap" data-src="https://img.bardcloud.online/assets/pdfjs/web/viewer.html?v=2&amp;file=/projects/Beacon%E5%BB%A3%E6%92%AD%E7%B3%BB%E7%B5%B1.pdf#zoom=page-width&amp;pagemode=thumbs&amp;disableAutoFetch=true&amp;disableStream=true" data-height="780">
    <div class="pdfv-placeholder" style="border:1px solid #e5e7eb; background:#f8f8f8; height: 780px; display:flex; align-items:center; justify-content:center; flex-direction:column;">
      <p style="margin:0 0 .5rem; color:#555;">PDF 尚未載入</p>
      <button type="button" class="pdfv-load-btn" style="padding:.5rem 1rem; border:0; border-radius:.5rem; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,0.1);">
        點我載入
      </button>
    </div>
  </div>

<p style="margin:.5rem 0 1rem;">
    <a href="https://img.bardcloud.online/projects/Beacon廣播系統.pdf" download="">下載原始 PDF</a>
  </p>

<script>
  (function(){
    if (window.__pdfvInit) return; window.__pdfvInit = true;

    function mountIframe(wrapper){
      if (!wrapper || wrapper.dataset.mounted) return;
      var src = wrapper.getAttribute('data-src');
      var h   = wrapper.getAttribute('data-height') || '780';
      var iframe = document.createElement('iframe');
      iframe.width = '100%';
      iframe.height = h;
      iframe.style.border = '0';
      iframe.src = src;
      wrapper.innerHTML = '';
      wrapper.appendChild(iframe);
      wrapper.dataset.mounted = '1';
    }

    // 點擊「點我載入」按鈕
    document.addEventListener('click', function(ev){
      var btn = ev.target.closest('.pdfv-load-btn');
      if (!btn) return;
      var wrap = btn.closest('.pdfv-wrap');
      mountIframe(wrap);
    });

    // 捲到可視範圍附近自動載入
    function setupIO(){
      if (!('IntersectionObserver' in window)) {
        document.querySelectorAll('.pdfv-wrap').forEach(mountIframe);
        return;
      }
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting) { mountIframe(e.target); io.unobserve(e.target); }
        });
      }, { rootMargin: '200px' });
      document.querySelectorAll('.pdfv-wrap').forEach(function(w){ io.observe(w); });
    }

    // 延後到 DOM ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', setupIO);
    } else {
      setupIO();
    }
  })();
  </script>

<h1 id="app爬蟲系統">app爬蟲系統</h1>

<div id="pdfv-projects-app-e7-88-ac-e8-9f-b2-e7-b3-bb-e7-b5-b1-pdf" class="pdfv-wrap" data-src="https://img.bardcloud.online/assets/pdfjs/web/viewer.html?v=2&amp;file=/projects/app%E7%88%AC%E8%9F%B2%E7%B3%BB%E7%B5%B1.pdf#zoom=page-width&amp;pagemode=thumbs&amp;disableAutoFetch=true&amp;disableStream=true" data-height="780">
    <div class="pdfv-placeholder" style="border:1px solid #e5e7eb; background:#f8f8f8; height: 780px; display:flex; align-items:center; justify-content:center; flex-direction:column;">
      <p style="margin:0 0 .5rem; color:#555;">PDF 尚未載入</p>
      <button type="button" class="pdfv-load-btn" style="padding:.5rem 1rem; border:0; border-radius:.5rem; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,0.1);">
        點我載入
      </button>
    </div>
  </div>

<p style="margin:.5rem 0 1rem;">
    <a href="https://img.bardcloud.online/projects/app爬蟲系統.pdf" download="">下載原始 PDF</a>
  </p>

<script>
  (function(){
    if (window.__pdfvInit) return; window.__pdfvInit = true;

    function mountIframe(wrapper){
      if (!wrapper || wrapper.dataset.mounted) return;
      var src = wrapper.getAttribute('data-src');
      var h   = wrapper.getAttribute('data-height') || '780';
      var iframe = document.createElement('iframe');
      iframe.width = '100%';
      iframe.height = h;
      iframe.style.border = '0';
      iframe.src = src;
      wrapper.innerHTML = '';
      wrapper.appendChild(iframe);
      wrapper.dataset.mounted = '1';
    }

    // 點擊「點我載入」按鈕
    document.addEventListener('click', function(ev){
      var btn = ev.target.closest('.pdfv-load-btn');
      if (!btn) return;
      var wrap = btn.closest('.pdfv-wrap');
      mountIframe(wrap);
    });

    // 捲到可視範圍附近自動載入
    function setupIO(){
      if (!('IntersectionObserver' in window)) {
        document.querySelectorAll('.pdfv-wrap').forEach(mountIframe);
        return;
      }
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting) { mountIframe(e.target); io.unobserve(e.target); }
        });
      }, { rootMargin: '200px' });
      document.querySelectorAll('.pdfv-wrap').forEach(function(w){ io.observe(w); });
    }

    // 延後到 DOM ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', setupIO);
    } else {
      setupIO();
    }
  })();
  </script>

<h1 id="行動電源維運系統">行動電源維運系統</h1>

<div id="pdfv-projects-e8-a1-8c-e5-8b-95-e9-9b-bb-e6-ba-90-e7-b6-ad-e9-81-8b-e7-b3-bb-e7-b5-b1-pdf" class="pdfv-wrap" data-src="https://img.bardcloud.online/assets/pdfjs/web/viewer.html?v=2&amp;file=/projects/%E8%A1%8C%E5%8B%95%E9%9B%BB%E6%BA%90%E7%B6%AD%E9%81%8B%E7%B3%BB%E7%B5%B1.pdf#zoom=page-width&amp;pagemode=thumbs&amp;disableAutoFetch=true&amp;disableStream=true" data-height="780">
    <div class="pdfv-placeholder" style="border:1px solid #e5e7eb; background:#f8f8f8; height: 780px; display:flex; align-items:center; justify-content:center; flex-direction:column;">
      <p style="margin:0 0 .5rem; color:#555;">PDF 尚未載入</p>
      <button type="button" class="pdfv-load-btn" style="padding:.5rem 1rem; border:0; border-radius:.5rem; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,0.1);">
        點我載入
      </button>
    </div>
  </div>

<p style="margin:.5rem 0 1rem;">
    <a href="https://img.bardcloud.online/projects/行動電源維運系統.pdf" download="">下載原始 PDF</a>
  </p>

<script>
  (function(){
    if (window.__pdfvInit) return; window.__pdfvInit = true;

    function mountIframe(wrapper){
      if (!wrapper || wrapper.dataset.mounted) return;
      var src = wrapper.getAttribute('data-src');
      var h   = wrapper.getAttribute('data-height') || '780';
      var iframe = document.createElement('iframe');
      iframe.width = '100%';
      iframe.height = h;
      iframe.style.border = '0';
      iframe.src = src;
      wrapper.innerHTML = '';
      wrapper.appendChild(iframe);
      wrapper.dataset.mounted = '1';
    }

    // 點擊「點我載入」按鈕
    document.addEventListener('click', function(ev){
      var btn = ev.target.closest('.pdfv-load-btn');
      if (!btn) return;
      var wrap = btn.closest('.pdfv-wrap');
      mountIframe(wrap);
    });

    // 捲到可視範圍附近自動載入
    function setupIO(){
      if (!('IntersectionObserver' in window)) {
        document.querySelectorAll('.pdfv-wrap').forEach(mountIframe);
        return;
      }
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting) { mountIframe(e.target); io.unobserve(e.target); }
        });
      }, { rootMargin: '200px' });
      document.querySelectorAll('.pdfv-wrap').forEach(function(w){ io.observe(w); });
    }

    // 延後到 DOM ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', setupIO);
    } else {
      setupIO();
    }
  })();
  </script>

<h1 id="咖啡機設備控制程式">咖啡機設備控制程式</h1>

<div id="pdfv-projects-e5-92-96-e5-95-a1-e6-a9-9f-e8-a8-ad-e5-82-99-e6-8e-a7-e5-88-b6-e7-a8-8b-e5-bc-8f-pdf" class="pdfv-wrap" data-src="https://img.bardcloud.online/assets/pdfjs/web/viewer.html?v=2&amp;file=/projects/%E5%92%96%E5%95%A1%E6%A9%9F%E8%A8%AD%E5%82%99%E6%8E%A7%E5%88%B6%E7%A8%8B%E5%BC%8F.pdf#zoom=page-width&amp;pagemode=thumbs&amp;disableAutoFetch=true&amp;disableStream=true" data-height="780">
    <div class="pdfv-placeholder" style="border:1px solid #e5e7eb; background:#f8f8f8; height: 780px; display:flex; align-items:center; justify-content:center; flex-direction:column;">
      <p style="margin:0 0 .5rem; color:#555;">PDF 尚未載入</p>
      <button type="button" class="pdfv-load-btn" style="padding:.5rem 1rem; border:0; border-radius:.5rem; cursor:pointer; box-shadow:0 2px 6px rgba(0,0,0,0.1);">
        點我載入
      </button>
    </div>
  </div>

<p style="margin:.5rem 0 1rem;">
    <a href="https://img.bardcloud.online/projects/咖啡機設備控制程式.pdf" download="">下載原始 PDF</a>
  </p>

<script>
  (function(){
    if (window.__pdfvInit) return; window.__pdfvInit = true;

    function mountIframe(wrapper){
      if (!wrapper || wrapper.dataset.mounted) return;
      var src = wrapper.getAttribute('data-src');
      var h   = wrapper.getAttribute('data-height') || '780';
      var iframe = document.createElement('iframe');
      iframe.width = '100%';
      iframe.height = h;
      iframe.style.border = '0';
      iframe.src = src;
      wrapper.innerHTML = '';
      wrapper.appendChild(iframe);
      wrapper.dataset.mounted = '1';
    }

    // 點擊「點我載入」按鈕
    document.addEventListener('click', function(ev){
      var btn = ev.target.closest('.pdfv-load-btn');
      if (!btn) return;
      var wrap = btn.closest('.pdfv-wrap');
      mountIframe(wrap);
    });

    // 捲到可視範圍附近自動載入
    function setupIO(){
      if (!('IntersectionObserver' in window)) {
        document.querySelectorAll('.pdfv-wrap').forEach(mountIframe);
        return;
      }
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting) { mountIframe(e.target); io.unobserve(e.target); }
        });
      }, { rootMargin: '200px' });
      document.querySelectorAll('.pdfv-wrap').forEach(function(w){ io.observe(w); });
    }

    // 延後到 DOM ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', setupIO);
    } else {
      setupIO();
    }
  })();
  </script>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="作品" /><summary type="html"><![CDATA[專案經歷]]></summary></entry><entry><title type="html">Fedora安裝Sql</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Fedora%E5%AE%89%E8%A3%9DSql/" rel="alternate" type="text/html" title="Fedora安裝Sql" /><published>2025-07-28T00:00:00+00:00</published><updated>2025-07-28T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Fedora%E5%AE%89%E8%A3%9DSql</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Fedora%E5%AE%89%E8%A3%9DSql/"><![CDATA[<h1 id="安裝mysql">安裝MySql</h1>

<p><code class="language-plaintext highlighter-rouge">$(rpm -E %fedora)</code> 會自動替換成你的 Fedora 版本。</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>https://dev.mysql.com/get/mysql80-community-release-fc<span class="si">$(</span>rpm <span class="nt">-E</span> %fedora<span class="si">)</span><span class="nt">-1</span>.noarch.rpm
</code></pre></div></div>

<p>後來發現Mysql沒有Fedora42版本</p>

<h2 id="可以安裝上一版41">可以安裝上一版41</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>https://dev.mysql.com/get/mysql80-community-release-fc41-1.noarch.rpm
<span class="nb">sudo </span>dnf <span class="nb">install </span>mysql-community-server
</code></pre></div></div>
<p>但dnf update</p>
<ul>
  <li>會升級你目前 repo 綁定的版本（例如 <code class="language-plaintext highlighter-rouge">8.0.36 → 8.0.37</code>）。</li>
  <li><strong>不會升級到 <code class="language-plaintext highlighter-rouge">8.1</code> 或 <code class="language-plaintext highlighter-rouge">9.0</code></strong>，除非你手動更換 repo。</li>
  <li>官方會維持 <code class="language-plaintext highlighter-rouge">mysql80-community.repo</code>、<code class="language-plaintext highlighter-rouge">mysql81-community.repo</code>、<code class="language-plaintext highlighter-rouge">mysql57-community.repo</code> 等彼此獨立。</li>
</ul>

<h2 id="安裝sql各版本更新規則">安裝SQL各版本更新規則</h2>

<table>
  <thead>
    <tr>
      <th>安裝來源</th>
      <th><code class="language-plaintext highlighter-rouge">dnf update</code> 是否會自動升級版本</th>
      <th>備註</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Fedora 官方套件庫（MariaDB）</td>
      <td>✅ <strong>會自動升級次版本（如 10.5 → 10.6）</strong></td>
      <td>依據 Fedora 當前支援版本進行滾動升級</td>
    </tr>
    <tr>
      <td>MySQL 官方 repo (<code class="language-plaintext highlighter-rouge">mysql80-community-release</code>)</td>
      <td>⚠️ <strong>只升小版本（如 8.0.36 → 8.0.37）</strong>，不會升級大版本（如 8.0 → 8.1）除非 repo 換成 <code class="language-plaintext highlighter-rouge">mysql81-community-release</code></td>
      <td>Oracle 的 repo 預設綁定某個主版本線</td>
    </tr>
    <tr>
      <td>手動安裝 <code class="language-plaintext highlighter-rouge">.rpm</code></td>
      <td>❌ 不會自動升級</td>
      <td>沒 repo metadata</td>
    </tr>
    <tr>
      <td>Docker container</td>
      <td>❌ 主機升級不影響容器</td>
      <td>要你自己 <code class="language-plaintext highlighter-rouge">docker pull</code> 升級</td>
    </tr>
  </tbody>
</table>

<h1 id="為什麼-fedora-官方只支援-mariadb">為什麼 Fedora 官方只支援 MariaDB？</h1>

<ul>
  <li>Fedora 是 Red Hat 支系，Red Hat 決定全面轉向使用 MariaDB（避免 Oracle 授權問題）。</li>
  <li><code class="language-plaintext highlighter-rouge">dnf install mysql-server</code> 時實際上裝的是 <code class="language-plaintext highlighter-rouge">mariadb-server</code>。</li>
  <li>如果安裝 <code class="language-plaintext highlighter-rouge">mysql-community-server</code>，那就是來自 Oracle 的 repo，需要手動維護。</li>
</ul>

<h1 id="mariadb-vs-mysql-差異一覽">MariaDB vs MySQL 差異一覽</h1>

<table>
  <thead>
    <tr>
      <th>面向</th>
      <th>MySQL (Oracle)</th>
      <th>MariaDB (社群版)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>開發主導</td>
      <td>Oracle</td>
      <td>MariaDB Foundation</td>
    </tr>
    <tr>
      <td>功能相容</td>
      <td>完全相容（前期）</td>
      <td>基本相容，但功能愈來愈多樣</td>
    </tr>
    <tr>
      <td>授權</td>
      <td>開源 + 專有部分</td>
      <td>完全 GPL</td>
    </tr>
    <tr>
      <td>JSON 支援</td>
      <td>✅ (原生 JSON type)</td>
      <td>✅（但實作不同）</td>
    </tr>
    <tr>
      <td>複製功能</td>
      <td>InnoDB Replication</td>
      <td>多種複製（包括 Galera）</td>
    </tr>
    <tr>
      <td>新功能更新頻率</td>
      <td>較保守（穩定）</td>
      <td>積極開發（但有可能不穩）</td>
    </tr>
    <tr>
      <td>最終趨勢</td>
      <td>商業導向（企業支持）</td>
      <td>社群導向（Fedora、Debian 都改用）</td>
    </tr>
  </tbody>
</table>

<h1 id="安裝mariadb">安裝MariaDB</h1>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>mariadb-server
<span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> mariadb
<span class="nb">sudo </span>mysql_secure_installation
</code></pre></div></div>

<h2 id="mariadb-server官方社群版">MariaDB Server（官方社群版）</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">mariadb-server</code>（資料庫核心）</li>
  <li>所需的 systemd 服務、socket 等</li>
</ul>

<h2 id="啟動並設為開機自動啟動">啟動並設為開機自動啟動</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> mariadb
</code></pre></div></div>

<h2 id="進行安全性初始化">進行安全性初始化</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mysql_secure_installation
</code></pre></div></div>

<ul>
  <li>Switch to unix_socket authentication [Y/n]
    <ul>
      <li><code class="language-plaintext highlighter-rouge">unix_socket</code> 認證方式就是<strong>透過 Linux 系統帳號來驗證 MariaDB 使用者身份</strong>，不需要密碼。</li>
      <li>讓 <strong><code class="language-plaintext highlighter-rouge">mysql -u root</code> 不需要輸入密碼</strong>，只要你是用系統的 <code class="language-plaintext highlighter-rouge">root</code> 或 <code class="language-plaintext highlighter-rouge">sudo</code> 使用者登入，系統就會自動讓你以 root 權限登入 MariaDB</li>
      <li>y 可以輸入sudo mysql，直接登入</li>
    </ul>
  </li>
  <li>Change the root password? [Y/n]
    <ul>
      <li>unix_socket 為 y 時 不能change</li>
    </ul>
  </li>
  <li>查帳號用的是哪種驗證
    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="k">user</span><span class="p">,</span> <span class="k">host</span><span class="p">,</span> <span class="n">plugin</span> <span class="k">FROM</span> <span class="n">mysql</span><span class="p">.</span><span class="k">user</span> <span class="k">WHERE</span> <span class="k">user</span><span class="o">=</span><span class="s1">'root'</span><span class="p">;</span>
</code></pre></div>    </div>
  </li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#系統帳號登入</span>
root | localhost | unix_socket
<span class="c">#傳統輸入密碼</span>
root | localhost | mysql_native_password
</code></pre></div></div>

<h1 id="建立新的帳號給程式用">建立新的帳號給程式用</h1>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'appuser'</span><span class="o">@</span><span class="s1">'localhost'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'yourpassword'</span><span class="p">;</span>
<span class="k">GRANT</span> <span class="k">ALL</span> <span class="k">PRIVILEGES</span> <span class="k">ON</span> <span class="n">your_database</span><span class="p">.</span><span class="o">*</span> <span class="k">TO</span> <span class="s1">'appuser'</span><span class="o">@</span><span class="s1">'localhost'</span><span class="p">;</span>
<span class="n">FLUSH</span> <span class="k">PRIVILEGES</span><span class="p">;</span>

</code></pre></div></div>

<h1 id="sql的gui">SQL的GUI</h1>

<p>用flatpak安裝</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#install</span>
flatpak <span class="nb">install </span>flathub io.dbeaver.DBeaverCommunity
<span class="c">#run</span>
flatpak run io.dbeaver.DBeaverCommunity

</code></pre></div></div>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="SQL" /><category term="Linux" /><summary type="html"><![CDATA[安裝MySql]]></summary></entry><entry><title type="html">Java-Funcional Interface</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Java-Funcional-Interface/" rel="alternate" type="text/html" title="Java-Funcional Interface" /><published>2025-07-28T00:00:00+00:00</published><updated>2025-07-28T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Java-Funcional%20Interface</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Java-Funcional-Interface/"><![CDATA[<h1 id="介紹">介紹</h1>
<h1 id="介紹-1">介紹</h1>

<p>引入 <strong>Functional Interface（函數式介面）</strong> 的目的，主要是為了引進 <strong>函數式編程（Functional Programming）</strong> 的特性，並解決 Java 傳統物件導向程式設計的一些限制。這個改變是從 Java 8 開始的一個重大語言演進</p>

<h2 id="1-支援-lambda-表達式">1. 支援 Lambda 表達式</h2>
<ul>
  <li>
    <p>Java 長期以來缺乏函數作為一等公民（first-class function），不能把行為當作參數傳遞。</p>
  </li>
  <li>
    <p>為了支援 <strong>Lambda 表達式（匿名函數）</strong>，Java 必須有一種語法結構來接收「一段邏輯」作為參數 → 這就是函數式介面。</p>
  </li>
</ul>

<p>Functional Interface 是 Lambda 的語法依附對象。</p>

<p>請先參考 <a href="/bardlee.github.io/Java-Lambda介紹/">Lambda 表達式介紹</a>。</p>
<h2 id="2-簡化匿名類別的繁瑣寫法">2. 簡化匿名類別的繁瑣寫法</h2>
<p>在 Java 8 以前，若要傳遞一段邏輯（例如事件處理、callback），只能寫匿名類別</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nc">ActionListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">actionPerformed</span><span class="o">(</span><span class="nc">ActionEvent</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Clicked"</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">};</span>
<span class="c1">//透過函數式介面 + Lambda</span>
<span class="n">e</span> <span class="o">-&gt;</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Clicked"</span><span class="o">);</span>

</code></pre></div></div>

<ul>
  <li>更簡潔</li>
  <li>可讀性更高</li>
  <li>少寫很多 boilerplate code</li>
</ul>

<h2 id="3-強化-api-的可組合性與彈性如-stream">3. 強化 API 的可組合性與彈性（如 Stream）</h2>
<p>Java 8 新增的 <code class="language-plaintext highlighter-rouge">Stream API</code>、<code class="language-plaintext highlighter-rouge">Optional</code>、<code class="language-plaintext highlighter-rouge">CompletableFuture</code> 等都使用函數式介面來設計。</p>

<p>這讓邏輯可以像資料一樣「<strong>流動與組合</strong>」，提升 API 的表達力與擴展性</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">list</span><span class="o">.</span><span class="na">stream</span><span class="o">()</span>
    <span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="o">)</span>
    <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">x</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="o">)</span>
    <span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">);</span>
</code></pre></div></div>

<h2 id="4-兼容物件導向與函數式風格">4. 兼容物件導向與函數式風格</h2>
<p>Java 沒有放棄 OOP，而是「<strong>用 Functional Interface 作為橋梁</strong>」，將兩種編程風格融合在一起</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 接口仍是 class-based 的物件導向結構</span>
<span class="nd">@FunctionalInterface</span>
<span class="kd">interface</span> <span class="nc">MyFunction</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="nf">apply</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">);</span>
    
<span class="c1">// int doMore(int y); // ❌ 編譯會錯，因為違反 Functional Interface 要求</span>
<span class="c1">//只能有一個抽象方法</span>
	<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="5-讓行為函數變成參數或變數">5. 讓行為（函數）變成參數或變數</h2>
<p>邏輯（方法）當作值傳遞，這實現了「行為即資料」的概念</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">doOperation</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">,</span> <span class="nc">Function</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">operation</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">operation</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">x</span><span class="o">));</span>
<span class="o">}</span>

<span class="n">doOperation</span><span class="o">(</span><span class="mi">5</span><span class="o">,</span> <span class="n">y</span> <span class="o">-&gt;</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">10</span><span class="o">);</span>  <span class="c1">// 輸出 15</span>
</code></pre></div></div>

<h1 id="functional-interface的實做">Functional Interface的實做</h1>

<p><strong>是一種只定義一個抽象方法”的介面</strong>，它可以：</p>

<ul>
  <li>由「具名類別」實作</li>
  <li>由「匿名類別」實作</li>
  <li>由「Lambda 表達式」實作 （Java 8+ 最簡潔的做法）</li>
</ul>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@FunctionalInterface</span>
<span class="kd">interface</span> <span class="nc">MyFunction</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="nf">apply</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>特點：</p>

<ul>
  <li>只有<strong>一個抽象方法</strong>（比如 <code class="language-plaintext highlighter-rouge">apply()</code>）</li>
  <li>可加 <code class="language-plaintext highlighter-rouge">@FunctionalInterface</code> 註解（非必要，但建議）</li>
  <li>代表「可以被當作 Lambda 的目標」</li>
</ul>

<h2 id="實作-functional-interface-的-3-種方式對比">實作 Functional Interface 的 3 種方式對比</h2>

<ol>
  <li>使用具名類別
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Square</span> <span class="kd">implements</span> <span class="nc">MyFunction</span> <span class="o">{</span>
 <span class="nd">@Override</span>
 <span class="kd">public</span> <span class="kt">int</span> <span class="nf">apply</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
     <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="o">;</span>
 <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>    </div>
  </li>
  <li>使用匿名類別（Anonymous Class）
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@FunctionalInterface</span>
<span class="nc">MyFunction</span> <span class="n">f</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">MyFunction</span><span class="o">()</span> <span class="o">{</span>
 <span class="nd">@Override</span>
 <span class="kd">public</span> <span class="kt">int</span> <span class="nf">apply</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
     <span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="o">;</span>
 <span class="o">}</span>
<span class="o">};</span>
</code></pre></div>    </div>
  </li>
  <li>使用 Lambda（Java 8+ 最推薦）
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">MyFunction</span> <span class="n">f</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-&gt;</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span><span class="o">;</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>三種寫法對應 Java 中實作 Functional Interface <code class="language-plaintext highlighter-rouge">Consumer&lt;T&gt;</code></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Consumer</code> 是「接受一個值但<strong>不回傳</strong>」的功能介面。</li>
  <li>適用場景：列印、儲存、觸發副作用等。</li>
</ul>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">test1</span> <span class="kd">implements</span> <span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;{</span>
	<span class="kd">public</span> <span class="kt">void</span> <span class="nf">accept</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">v</span><span class="o">){</span>
	
	<span class="o">}</span>
<span class="o">}</span>

<span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">_test1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;()</span> <span class="o">{</span>

	<span class="kd">public</span> <span class="kt">void</span> <span class="nf">accept</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">v</span><span class="o">){</span>
	<span class="o">}</span>

<span class="o">};</span>

<span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">_test1_1</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Integer</span> <span class="n">v</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{};</span>
</code></pre></div></div>

<h2 id="對照表">對照表</h2>

<table>
  <thead>
    <tr>
      <th>寫法類型</th>
      <th>程式碼範例</th>
      <th>優點</th>
      <th>缺點</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>具名類別</td>
      <td><code class="language-plaintext highlighter-rouge">class Square ...</code></td>
      <td>清楚明確，便於除錯</td>
      <td>程式碼冗長</td>
    </tr>
    <tr>
      <td>匿名類別</td>
      <td><code class="language-plaintext highlighter-rouge">new MyFunction()...</code></td>
      <td>不用額外類別名稱</td>
      <td>結構仍冗長</td>
    </tr>
    <tr>
      <td>Lambda 寫法</td>
      <td><code class="language-plaintext highlighter-rouge">x -&gt; x * x</code></td>
      <td>最簡潔，可讀性高</td>
      <td>不能處理複雜邏輯、無法捕捉 checked exception</td>
    </tr>
  </tbody>
</table>

<h1 id="策略模式的應用">策略模式的應用</h1>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 定義 Functional Interface</span>
<span class="nd">@FunctionalInterface</span>
<span class="kd">interface</span> <span class="nc">Strategy</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="nf">apply</span><span class="o">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">,</span> <span class="kt">int</span> <span class="n">b</span><span class="o">);</span>
<span class="o">}</span>
<span class="c1">// 建立策略表</span>

<span class="kn">import</span> <span class="nn">java.util.Map</span><span class="o">;</span>

<span class="c1">//可換成 `new HashMap&lt;&gt;()` + `put()` 若需動態新增策略</span>
<span class="c1">// Map.of後若更動，將會報錯</span>

<span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Strategy</span><span class="o">&gt;</span> <span class="n">strategyMap</span> <span class="o">=</span> <span class="nc">Map</span><span class="o">.</span><span class="na">of</span><span class="o">(</span>
    <span class="s">"add"</span><span class="o">,</span> <span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="o">,</span>
    <span class="s">"sub"</span><span class="o">,</span> <span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">-</span> <span class="n">b</span><span class="o">,</span>
    <span class="s">"mul"</span><span class="o">,</span> <span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">*</span> <span class="n">b</span><span class="o">,</span>
    <span class="s">"div"</span><span class="o">,</span> <span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">b</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span> <span class="o">:</span> <span class="mi">0</span>
<span class="o">);</span>

<span class="c1">// 執行</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">execute</span><span class="o">(</span><span class="nc">String</span> <span class="n">type</span><span class="o">,</span> <span class="kt">int</span> <span class="n">a</span><span class="o">,</span> <span class="kt">int</span> <span class="n">b</span><span class="o">,</span> <span class="nc">Map</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Strategy</span><span class="o">&gt;</span> <span class="n">map</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Strategy</span> <span class="n">strategy</span> <span class="o">=</span> <span class="n">map</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">type</span><span class="o">);</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">strategy</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">IllegalArgumentException</span><span class="o">(</span><span class="s">"未知策略："</span> <span class="o">+</span> <span class="n">type</span><span class="o">);</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="n">strategy</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">a</span><span class="o">,</span> <span class="n">b</span><span class="o">);</span>
<span class="o">}</span>

<span class="c1">//測試</span>

<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">execute</span><span class="o">(</span><span class="s">"add"</span><span class="o">,</span> <span class="mi">10</span><span class="o">,</span> <span class="mi">5</span><span class="o">,</span> <span class="n">strategyMap</span><span class="o">));</span> <span class="c1">// 15</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">execute</span><span class="o">(</span><span class="s">"mul"</span><span class="o">,</span> <span class="mi">10</span><span class="o">,</span> <span class="mi">5</span><span class="o">,</span> <span class="n">strategyMap</span><span class="o">));</span> <span class="c1">// 50</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">execute</span><span class="o">(</span><span class="s">"div"</span><span class="o">,</span> <span class="mi">10</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">strategyMap</span><span class="o">));</span> <span class="c1">// 0</span>

</code></pre></div></div>

<p>注意
	可換成 <code class="language-plaintext highlighter-rouge">new HashMap&lt;&gt;()</code> + <code class="language-plaintext highlighter-rouge">put()</code> 若需動態新增策略
	Map.of後若更動，將會報錯</p>

<h1 id="常見-functional-interface--函式式介面對照表">常見 Functional Interface  函式式介面對照表</h1>

<table>
  <thead>
    <tr>
      <th>類型</th>
      <th>用途</th>
      <th>介面</th>
      <th>方法名</th>
      <th>Lambda 範例</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>無參無回傳</td>
      <td>執行動作</td>
      <td><code class="language-plaintext highlighter-rouge">Runnable</code></td>
      <td><code class="language-plaintext highlighter-rouge">void run()</code></td>
      <td><code class="language-plaintext highlighter-rouge">() -&gt; {...}</code></td>
    </tr>
    <tr>
      <td>有參無回傳</td>
      <td>處理一個值</td>
      <td><code class="language-plaintext highlighter-rouge">Consumer&lt;T&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">void accept(T)</code></td>
      <td><code class="language-plaintext highlighter-rouge">x -&gt; {...}</code></td>
    </tr>
    <tr>
      <td>雙參無回傳</td>
      <td>處理兩個值</td>
      <td><code class="language-plaintext highlighter-rouge">BiConsumer&lt;T, U&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">void accept(T,U)</code></td>
      <td><code class="language-plaintext highlighter-rouge">(a, b) -&gt; {...}</code></td>
    </tr>
    <tr>
      <td>有參有回傳</td>
      <td>單值轉換</td>
      <td><code class="language-plaintext highlighter-rouge">Function&lt;T, R&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">R apply(T)</code></td>
      <td><code class="language-plaintext highlighter-rouge">x -&gt; result</code></td>
    </tr>
    <tr>
      <td>雙參有回傳</td>
      <td>二元運算</td>
      <td><code class="language-plaintext highlighter-rouge">BiFunction&lt;T, U, R&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">R apply(T, U)</code></td>
      <td><code class="language-plaintext highlighter-rouge">(a, b) -&gt; result</code></td>
    </tr>
    <tr>
      <td>判斷條件</td>
      <td>回傳布林</td>
      <td><code class="language-plaintext highlighter-rouge">Predicate&lt;T&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">boolean test(T)</code></td>
      <td><code class="language-plaintext highlighter-rouge">x -&gt; x &gt; 0</code></td>
    </tr>
    <tr>
      <td>提供值</td>
      <td>無參有回傳</td>
      <td><code class="language-plaintext highlighter-rouge">Supplier&lt;T&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">T get()</code></td>
      <td><code class="language-plaintext highlighter-rouge">() -&gt; value</code></td>
    </tr>
    <tr>
      <td>同型轉換</td>
      <td>一參有回傳</td>
      <td><code class="language-plaintext highlighter-rouge">UnaryOperator&lt;T&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">T apply(T)</code></td>
      <td><code class="language-plaintext highlighter-rouge">x -&gt; x + 1</code></td>
    </tr>
    <tr>
      <td>同型轉換</td>
      <td>兩參有回傳</td>
      <td><code class="language-plaintext highlighter-rouge">BinaryOperator&lt;T&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">T apply(T, T)</code></td>
      <td><code class="language-plaintext highlighter-rouge">(a, b) -&gt; a + b</code></td>
    </tr>
  </tbody>
</table>

<h1 id="針對原始型別的特殊介面">針對原始型別的特殊介面</h1>

<table>
  <thead>
    <tr>
      <th>原始型別介面</th>
      <th>方法</th>
      <th>範例用途</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntFunction&lt;R&gt;</code></td>
      <td><code class="language-plaintext highlighter-rouge">R apply(int)</code></td>
      <td><code class="language-plaintext highlighter-rouge">i -&gt; "a" + i</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntConsumer</code></td>
      <td><code class="language-plaintext highlighter-rouge">void accept(int)</code></td>
      <td><code class="language-plaintext highlighter-rouge">i -&gt; System.out.println(i)</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntPredicate</code></td>
      <td><code class="language-plaintext highlighter-rouge">boolean test(int)</code></td>
      <td><code class="language-plaintext highlighter-rouge">i -&gt; i % 2 == 0</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntSupplier</code></td>
      <td><code class="language-plaintext highlighter-rouge">int getAsInt()</code></td>
      <td><code class="language-plaintext highlighter-rouge">() -&gt; 42</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntUnaryOperator</code></td>
      <td><code class="language-plaintext highlighter-rouge">int applyAsInt(int)</code></td>
      <td><code class="language-plaintext highlighter-rouge">i -&gt; i * i</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">IntBinaryOperator</code></td>
      <td><code class="language-plaintext highlighter-rouge">int applyAsInt(int, int)</code></td>
      <td><code class="language-plaintext highlighter-rouge">(a, b) -&gt; a + b</code></td>
    </tr>
  </tbody>
</table>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="java" /><summary type="html"><![CDATA[介紹 介紹]]></summary></entry><entry><title type="html">Java Lambda介紹</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Java-Lambda%E4%BB%8B%E7%B4%B9/" rel="alternate" type="text/html" title="Java Lambda介紹" /><published>2025-07-27T00:00:00+00:00</published><updated>2025-07-27T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Java%20Lambda%E4%BB%8B%E7%B4%B9</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Java-Lambda%E4%BB%8B%E7%B4%B9/"><![CDATA[<h1 id="介紹">介紹</h1>
<p>背後結合了 <strong>函數式編程（Functional Programming）</strong> 與 <strong>物件導向</strong> 的概念。它跟 C# 的 <code class="language-plaintext highlighter-rouge">delegate</code> 類似，但底層實作方式不同</p>

<h1 id="lambda-模板">Lambda 模板</h1>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="n">parameters</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="n">statement</span> <span class="o">}</span>
</code></pre></div></div>

<p>使用心法</p>
<ul>
  <li>只有一行可以省略 <code class="language-plaintext highlighter-rouge">{}</code> 和 <code class="language-plaintext highlighter-rouge">return</code></li>
  <li>儘量搭配 <code class="language-plaintext highlighter-rouge">Stream API</code> 做資料處理</li>
  <li>避免在 Lambda 裡寫太複雜的邏輯，保持簡潔</li>
</ul>

<h1 id="方法參考method-reference">方法參考（Method Reference）</h1>
<p><strong>方法參考</strong>是 Java 8 引入的一種 <strong>簡化 Lambda 表達式的語法</strong>
 當 Lambda 的內容只是「<strong>直接呼叫某個已存在的方法</strong>」時，可以用 <code class="language-plaintext highlighter-rouge">::</code> 替代</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//靜態方法參考----------------------------</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Utils</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">isPositive</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
<span class="c1">//執行</span>
<span class="nc">Predicate</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">p</span> <span class="o">=</span> <span class="nl">Utils:</span><span class="o">:</span><span class="n">isPositive</span><span class="o">;</span>
<span class="c1">// 相當於：x -&gt; Utils.isPositive(x)</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">p</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="mi">10</span><span class="o">));</span> <span class="c1">// true</span>

<span class="c1">//物件實例方法參考----------------------------</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Printer</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">print</span><span class="o">(</span><span class="nc">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"&gt;&gt; "</span> <span class="o">+</span> <span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
<span class="c1">//執行</span>
<span class="nc">Printer</span> <span class="n">printer</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Printer</span><span class="o">();</span>
<span class="nc">Consumer</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="nl">printer:</span><span class="o">:</span><span class="n">print</span><span class="o">;</span>
<span class="c1">// 相當於：s -&gt; printer.print(s)</span>
<span class="n">c</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="s">"Hello"</span><span class="o">);</span>

<span class="c1">//類別的實例方法參考（重點）----------------------------</span>
<span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">list</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"a"</span><span class="o">,</span> <span class="s">"b"</span><span class="o">,</span> <span class="s">"c"</span><span class="o">);</span>
<span class="n">list</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">);</span>
<span class="c1">// 相當於：s -&gt; System.out.println(s)</span>
<span class="c1">//or</span>
<span class="nc">BiPredicate</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span> <span class="n">equals</span> <span class="o">=</span> <span class="nl">String:</span><span class="o">:</span><span class="n">equals</span><span class="o">;</span>
<span class="c1">// 相當於：(a, b) -&gt; a.equals(b)</span>

<span class="c1">//建構子參考----------------------------</span>
<span class="nc">Supplier</span><span class="o">&lt;</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;&gt;</span> <span class="n">listSupplier</span> <span class="o">=</span> <span class="nl">ArrayList:</span><span class="o">:</span><span class="k">new</span><span class="o">;</span>
<span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">list</span> <span class="o">=</span> <span class="n">listSupplier</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="c1">// new ArrayList()</span>


</code></pre></div></div>

<h2 id="關於類別的實例方法參考">關於類別的實例方法參考</h2>

<h3 id="為什麼能用在-listforeachsystemoutprintln">為什麼能用在 <code class="language-plaintext highlighter-rouge">list.forEach(System.out::println)</code>？</h3>
<ul>
  <li><code class="language-plaintext highlighter-rouge">forEach</code> 方法需要一個 <strong><code class="language-plaintext highlighter-rouge">Consumer&lt;T&gt;</code></strong> 介面實作。</li>
  <li><code class="language-plaintext highlighter-rouge">Consumer&lt;T&gt;</code> 是一個 <strong>函式式介面</strong>，定義方法：
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">accept</span><span class="o">(</span><span class="no">T</span> <span class="n">t</span><span class="o">);</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">System.out::println</code> 實際上是「指向 <code class="language-plaintext highlighter-rouge">PrintStream</code> 物件的 <code class="language-plaintext highlighter-rouge">println</code> 方法」的<strong>方法參考</strong>，</li>
  <li>
    <p>它的簽名跟 <code class="language-plaintext highlighter-rouge">Consumer&lt;String&gt;</code> 的 <code class="language-plaintext highlighter-rouge">accept(String)</code> 一致，<br />
  因此可以視為一個 <code class="language-plaintext highlighter-rouge">Consumer&lt;String&gt;</code> 實例（底層由 JVM 幫你轉換）。</p>
  </li>
  <li><strong><code class="language-plaintext highlighter-rouge">System.out::println</code> 是一個方法參考（method reference），符合 Lambda 所要求的函式介面（Consumer）的單一抽象方法簽名</strong>。</li>
  <li>它本質上是符合 <code class="language-plaintext highlighter-rouge">Consumer&lt;String&gt;</code> 的實作，可以賦值給該介面型態的變數或參數。</li>
</ul>

<h3 id="自轉轉換的條件">自轉轉換的條件</h3>

<p>JVM（編譯器）自動轉換的條件：</p>
<ul>
  <li>
    <h2 id="只要-你的目標類型是-functional-interface且lambda-表達式或方法參考的簽名參數和回傳型態符合該介面唯一抽象方法的簽名">只要 <strong>你的目標類型是 Functional Interface</strong>，且Lambda 表達式或方法參考的<strong>簽名（參數和回傳型態）符合該介面唯一抽象方法的簽名</strong></h2>
  </li>
</ul>

<h4 id="詳細說明">詳細說明：</h4>
<ul>
  <li>
    <p><strong>函式介面（Functional Interface）</strong>：必須有且只有一個抽象方法</p>
  </li>
  <li>
    <p><strong>Lambda 表達式或方法參考</strong>：編譯器會檢查它們的參數與回傳型態，是否可以對應介面方法</p>
  </li>
  <li>
    <p><strong>只要匹配，就會自動幫你轉換成該介面的實例</strong></p>
  </li>
</ul>

<h2 id="bipredicatestring-string-equals--stringequals說明">BiPredicate&lt;String, String&gt; equals = String::equals說明</h2>
<p><code class="language-plaintext highlighter-rouge">BiPredicate&lt;T, U&gt;</code> 介面有一個抽象方法</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1">//`BiPredicate&lt;T, U&gt;` 介面有一個抽象方法</span>
<span class="kt">boolean</span> <span class="nf">test</span><span class="o">(</span><span class="no">T</span> <span class="n">t</span><span class="o">,</span> <span class="no">U</span> <span class="n">u</span><span class="o">);</span>

<span class="c1">//執行</span>
<span class="kt">boolean</span> <span class="n">result1</span> <span class="o">=</span> <span class="n">equals</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="s">"hello"</span><span class="o">,</span> <span class="s">"hello"</span><span class="o">);</span> <span class="c1">// true</span>
<span class="kt">boolean</span> <span class="n">result2</span> <span class="o">=</span> <span class="n">equals</span><span class="o">.</span><span class="na">test</span><span class="o">(</span><span class="s">"hello"</span><span class="o">,</span> <span class="s">"world"</span><span class="o">);</span> <span class="c1">// false</span>
</code></pre></div></div>

<p><strong>小結</strong></p>

<table>
  <thead>
    <tr>
      <th>類型</th>
      <th>語法</th>
      <th>參考範例</th>
      <th>解釋</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>靜態方法</td>
      <td><code class="language-plaintext highlighter-rouge">Class::staticMethod</code></td>
      <td><code class="language-plaintext highlighter-rouge">Math::abs</code></td>
      <td>x -&gt; Math.abs(x)</td>
    </tr>
    <tr>
      <td>特定物件方法</td>
      <td><code class="language-plaintext highlighter-rouge">object::instanceMethod</code></td>
      <td><code class="language-plaintext highlighter-rouge">System.out::println</code></td>
      <td>x -&gt; System.out.println(x)</td>
    </tr>
    <tr>
      <td>類別實例方法</td>
      <td><code class="language-plaintext highlighter-rouge">Class::instanceMethod</code></td>
      <td><code class="language-plaintext highlighter-rouge">String::toUpperCase</code></td>
      <td>x -&gt; x.toUpperCase()</td>
    </tr>
    <tr>
      <td>類別實例方法（二參）</td>
      <td><code class="language-plaintext highlighter-rouge">String::equals</code></td>
      <td>(a, b) -&gt; a.equals(b)</td>
      <td> </td>
    </tr>
    <tr>
      <td>建構子</td>
      <td><code class="language-plaintext highlighter-rouge">Class::new</code></td>
      <td><code class="language-plaintext highlighter-rouge">ArrayList::new</code></td>
      <td>() -&gt; new ArrayList&lt;&gt;()</td>
    </tr>
  </tbody>
</table>

<h1 id="lambda-表達式的限制與注意事項"><strong>Lambda 表達式的限制與注意事項</strong></h1>
<ul>
  <li>無法直接丟 checked exception（需 try-catch 包起來或改成 unchecked）</li>
  <li>不可使用非 final 或 effectively final 的外部區域變數（closure 限制）</li>
</ul>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="o">;</span>
<span class="nc">Runnable</span> <span class="n">r</span> <span class="o">=</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span>
    <span class="c1">// x++; //  編譯錯誤</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">x</span><span class="o">);</span> <span class="c1">//  ok，因為 x 未被修改</span>
<span class="o">};</span>

<span class="nc">Function</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span> <span class="n">f</span> <span class="o">=</span> <span class="n">s</span> <span class="o">-&gt;</span> <span class="o">{</span>
    <span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">1000</span><span class="o">);</span> <span class="c1">//  sleep 會丟 InterruptedException（checked）</span>
    <span class="k">return</span> <span class="n">s</span><span class="o">;</span>
<span class="o">};</span>
</code></pre></div></div>
<h2 id="lambda-中只能使用-final-或-effectively-final-的變數"><strong>Lambda 中只能使用 “final 或 effectively final” 的變數</strong></h2>

<ul>
  <li>
    <p>Java 為了確保 Lambda 是 <strong>可安全封閉（closure）</strong>，不允許修改外部區域變數。</p>
  </li>
  <li>
    <p>雖然你沒加 <code class="language-plaintext highlighter-rouge">final</code>，但只要沒改變它，Java 編譯器會<strong>自動視為</strong> <code class="language-plaintext highlighter-rouge">effectively final</code>。</p>
  </li>
  <li>
    <p>一旦你想做 <code class="language-plaintext highlighter-rouge">x++</code>，它就不是 final，會 <strong>編譯錯誤</strong>。</p>
  </li>
</ul>

<h2 id="lambda-表達式中不能直接丟出-checked-exception受檢例外除非顯式處理">Lambda 表達式中不能直接丟出 <strong>checked exception（受檢例外）</strong>，除非顯式處理</h2>

<p>Java 的內建函數式介面（像 <code class="language-plaintext highlighter-rouge">Function&lt;T, R&gt;</code>）<strong>方法簽章並未宣告 throws</strong></p>
<ul>
  <li>所以你不能丟出任何 checked exception，否則會編譯錯。</li>
  <li>若你真的需要，可以：
    <ul>
      <li>包 try-catch（如上）</li>
      <li>使用自定義會 throws 的函數式介面（例如 <code class="language-plaintext highlighter-rouge">ThrowingFunction&lt;T, R&gt;</code>）</li>
    </ul>
  </li>
</ul>

<p>Thread.sleep</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">sleep</span><span class="o">(</span><span class="kt">long</span> <span class="n">millis</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">InterruptedException</span>

</code></pre></div></div>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="java" /><summary type="html"><![CDATA[介紹 背後結合了 函數式編程（Functional Programming） 與 物件導向 的概念。它跟 C# 的 delegate 類似，但底層實作方式不同]]></summary></entry><entry><title type="html">Java-泛型</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Java-%E6%B3%9B%E5%9E%8B/" rel="alternate" type="text/html" title="Java-泛型" /><published>2025-07-27T00:00:00+00:00</published><updated>2025-07-27T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Java-%E6%B3%9B%E5%9E%8B</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Java-%E6%B3%9B%E5%9E%8B/"><![CDATA[<h1 id="java-泛型">Java-泛型</h1>

<h2 id="多重型態參數">多重型態參數</h2>

<p>在 Java 中，泛型（Generics）讓我們可以將「型態」延後到使用時才指定，讓程式更具彈性與可重用性。</p>

<p>舉例來說，你可以建立一個通用的 <code class="language-plaintext highlighter-rouge">Example&lt;K, V&gt;</code> 類別，用來表示一個 key 對應一個 value</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Example</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">&gt;{</span>
<span class="o">}</span>

<span class="c1">//人對應一個名子(字串)</span>
<span class="nc">Example</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Person</span><span class="o">&gt;</span> <span class="n">ex1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Example</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Person</span><span class="o">&gt;();</span> 

<span class="c1">//區域對應一個數字</span>
<span class="nc">Example</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Region</span><span class="o">&gt;</span> <span class="n">ex2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Example</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Region</span><span class="o">&gt;();</span>

</code></pre></div></div>

<h2 id="泛型參數的使用範圍">泛型參數的使用範圍</h2>
<p>有兩個地方你無法在類別裡面使用泛型參數</p>

<ol>
  <li>不能用在靜態變數上</li>
  <li>泛型類別的型別參數（例如 T）不能直接用在靜態方法中，不論是方法的參數還是回傳值，因為靜態方法不屬於任何具體型別的物件</li>
</ol>

<p>錯誤範例</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Gen</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;{</span>
    <span class="kd">static</span> <span class="no">T</span> <span class="n">value</span><span class="o">;</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">test1</span><span class="o">(</span><span class="no">T</span> <span class="n">v1</span><span class="o">){</span>
    <span class="o">}</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="no">T</span> <span class="nf">test2</span><span class="o">(){</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="泛型的靜態變數">泛型的靜態變數</h2>

<p>即使你使用不同的型別來建立泛型類別實例，這些實例<strong>共用同一個靜態變數</strong>。因為靜態變數是類別層級的，而不是泛型實例化後各自擁有的。</p>

<h2 id="方法自帶的泛型">方法自帶的泛型</h2>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Box</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;</span> <span class="no">U</span> <span class="nf">echo</span><span class="o">(</span><span class="no">U</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">value</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>這裡的 <code class="language-plaintext highlighter-rouge">&lt;U&gt;</code> 是屬於這個方法本身的泛型參數，與類別的 <code class="language-plaintext highlighter-rouge">T</code> 無關，因此合法、可編譯</p>

<h2 id="小結">小結</h2>

<table>
  <thead>
    <tr>
      <th>錯誤場景</th>
      <th>編譯結果</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">static T someVar;</code></td>
      <td>編譯錯誤</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">public static T method()</code></td>
      <td>編譯錯誤</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">public static void method(T t)</code></td>
      <td>編譯錯誤</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">public static &lt;U&gt; U method(U u)</code></td>
      <td>合法</td>
    </tr>
  </tbody>
</table>

<h2 id="java-的泛型generics只能使用參考型別reference-type不能用原始型別primitive-type">Java 的泛型（Generics）只能使用參考型別（Reference Type），不能用原始型別（Primitive Type）</h2>

<ul>
  <li>
    <p><strong>原始型別（primitive types）</strong>：像 <code class="language-plaintext highlighter-rouge">int</code>、<code class="language-plaintext highlighter-rouge">double</code>、<code class="language-plaintext highlighter-rouge">char</code>、<code class="language-plaintext highlighter-rouge">boolean</code>，這些是效率較高的基本資料型別，不是物件。</p>
  </li>
  <li>
    <p><strong>參考型別（reference types）</strong>：像 <code class="language-plaintext highlighter-rouge">Integer</code>、<code class="language-plaintext highlighter-rouge">Double</code>、<code class="language-plaintext highlighter-rouge">Character</code>、<code class="language-plaintext highlighter-rouge">Boolean</code>，這些是物件（classes），包裝了原始型別。</p>
  </li>
</ul>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">numbers</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span> <span class="c1">// 錯誤！不能用原始型別 int</span>

<span class="nc">List</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">numbers</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span> <span class="c1">// 用 Integer（參考型別）包裝 int</span>

</code></pre></div></div>

<p>這是因為 Java 的泛型設計時，是用「類別」來實作的，所以泛型只能接受 <strong>物件型別（即參考型別）</strong></p>

<h2 id="一般方法參數可以用-int-嗎">一般方法參數可以用 <code class="language-plaintext highlighter-rouge">int</code> 嗎</h2>

<p>泛型的限制只出現在「泛型參數」中，但一般方法中的參數可以是任何型別</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">printInt</span><span class="o">(</span><span class="kt">int</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="java-有一種自動轉換稱為裝箱與拆箱boxing--unboxing">Java 有一種自動轉換稱為「裝箱與拆箱（Boxing / Unboxing）</h2>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">list</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;&gt;();</span>
<span class="n">list</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span> <span class="c1">// 自動 boxing: int → Integer</span>
<span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="n">list</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span> <span class="c1">// 自動 unboxing: Integer → int</span>

</code></pre></div></div>

<p>泛型支援原始型別，可以考慮 Java 8 之後的 <code class="language-plaintext highlighter-rouge">IntStream</code>、<code class="language-plaintext highlighter-rouge">OptionalInt</code> 或一些特殊的泛型工具（如 Guava 的 <code class="language-plaintext highlighter-rouge">PrimitiveSpecialized</code>），或使用泛型+手動處理 boxing/unboxing</p>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="java" /><summary type="html"><![CDATA[Java-泛型]]></summary></entry><entry><title type="html">安裝Docker的細節</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/%E5%AE%89%E8%A3%9DDocker%E7%9A%84%E7%B4%B0%E7%AF%80/" rel="alternate" type="text/html" title="安裝Docker的細節" /><published>2025-07-22T00:00:00+00:00</published><updated>2025-07-22T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/%E5%AE%89%E8%A3%9DDocker%E7%9A%84%E7%B4%B0%E7%AF%80</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/%E5%AE%89%E8%A3%9DDocker%E7%9A%84%E7%B4%B0%E7%AF%80/"><![CDATA[<h1 id="安裝步驟">安裝步驟</h1>
<h1 id="安裝步驟-1">安裝步驟</h1>

<ol>
  <li>安裝docker
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>docker-ce docker-ce-cli containerd.io
</code></pre></div>    </div>
  </li>
  <li>啟用並啟動服務
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> docker
</code></pre></div>    </div>
  </li>
</ol>

<h1 id="核心部件">核心部件</h1>

<ol>
  <li><strong>docker-ce（Docker Community Edition Daemon）</strong>
    <ul>
      <li><strong>是 Docker 的核心守護程式（daemon）</strong>，也就是 <code class="language-plaintext highlighter-rouge">dockerd</code>。</li>
      <li>
        <p>負責管理：</p>

        <ul>
          <li>
            <p>容器的啟動、停止、監控</p>
          </li>
          <li>
            <p>與 Linux kernel 的整合（如 Cgroups、namespaces）</p>
          </li>
          <li>
            <p>呼叫 containerd 來執行容器</p>
          </li>
        </ul>
      </li>
      <li>啟動後會常駐於系統背景：<code class="language-plaintext highlighter-rouge">/usr/bin/dockerd</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">docker-ce-cli</code>（<strong>Docker Command-Line Interface</strong>）
    <ul>
      <li>
        <p>也就是 <code class="language-plaintext highlighter-rouge">docker</code> 指令本體（終端機輸入的 `docker run ）。</p>
      </li>
      <li>
        <p>與 <code class="language-plaintext highlighter-rouge">dockerd</code>（Daemon）透過 Unix socket 或 REST API 通訊。</p>
      </li>
      <li>
        <p>單獨安裝 CLI 沒辦法真正啟動容器，需要 <code class="language-plaintext highlighter-rouge">docker-ce</code> 搭配使用。</p>
      </li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">containerd.io</code>
    <ul>
      <li>
        <p>是 Docker 使用的 <strong>容器執行引擎（container runtime）</strong>。</p>
      </li>
      <li>
        <p>原本是 Docker 的一部分，現在獨立發展，由 CNCF 維護。</p>
      </li>
      <li>
        <p>Docker 的 daemon（dockerd）會呼叫 <code class="language-plaintext highlighter-rouge">containerd</code> 來管理容器生命週期。</p>
      </li>
      <li>
        <p><strong>Kubernetes</strong> 等也可直接使用 containerd，不需要 Docker。</p>
      </li>
    </ul>
  </li>
</ol>

<h1 id="與docker-desktop的差異">與Docker Desktop的差異</h1>
<p>一直以為我都安裝Docker Desktop，直到我開始同時使用vm ，兩個服務就會互相衝突，必須一直手動關閉重起虛擬化服務</p>

<h2 id="為什麼-docker-ce-不會跟-virtualbox-衝突">為什麼 <code class="language-plaintext highlighter-rouge">docker-ce</code> 不會跟 VirtualBox 衝突？</h2>
<h3 id="docker-ce-是原生安裝在-linux-上的-docker-daemon"><code class="language-plaintext highlighter-rouge">docker-ce</code> 是「原生」安裝在 Linux 上的 Docker Daemon：</h3>

<ul>
  <li>
    <p>它 <strong>直接使用 Linux 的 kernel 功能</strong>（如 namespaces、cgroups）來執行容器，不需要任何虛擬機（VM）。</p>
  </li>
  <li>
    <p>所以只要是 **Linux 原生環境，Docker CE 就能直接跑，不會跟 VirtualBox 衝突。</p>
  </li>
</ul>

<h2 id="為什麼-docker-desktop-會跟-virtualbox-衝突">為什麼 <strong>Docker Desktop</strong> 會跟 VirtualBox 衝突</h2>
<h3 id="docker-desktop-在-linuxmacoswindows-上用的是-虛擬機vm">Docker Desktop 在 Linux/macOS/Windows 上用的是 <strong>虛擬機（VM）</strong>：</h3>

<ul>
  <li>
    <p>即使是在 Linux 上裝 Docker Desktop，它還是會 <strong>用一台內建的虛擬機（如使用 <code class="language-plaintext highlighter-rouge">qemu</code>、<code class="language-plaintext highlighter-rouge">virt-manager</code>、<code class="language-plaintext highlighter-rouge">systemd-nspawn</code>）</strong> 來跑 Docker 引擎。</p>
  </li>
  <li>
    <p>它會啟動一個 daemon VM（叫 <code class="language-plaintext highlighter-rouge">docker-desktop</code> 或 <code class="language-plaintext highlighter-rouge">docker-desktop-data</code>），並建立 bridge 網路介面、用 <code class="language-plaintext highlighter-rouge">iptables</code> 設定轉發。</p>
  </li>
</ul>

<h3 id="總結比較">總結比較</h3>

<table>
  <thead>
    <tr>
      <th>比較項目</th>
      <th>Docker CE</th>
      <th>Docker Desktop</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>安裝位置</td>
      <td>Native 安裝於 Linux 上</td>
      <td>使用虛擬機環境包裝 Docker</td>
    </tr>
    <tr>
      <td>是否使用 VM</td>
      <td>不使用</td>
      <td>使用</td>
    </tr>
    <tr>
      <td>是否與 VirtualBox 衝突</td>
      <td>幾乎不會</td>
      <td>可能會（尤其在 Linux 上）</td>
    </tr>
    <tr>
      <td>推薦使用時機</td>
      <td>你是 Linux 使用者</td>
      <td>你是 Windows/macOS 或懶得設定時</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="Docker" /><summary type="html"><![CDATA[安裝步驟 安裝步驟]]></summary></entry><entry><title type="html">Fedora41 升版 42 的大災難錯誤</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Fedora41-%E5%8D%87%E7%89%88-42-%E7%9A%84%E5%A4%A7%E7%81%BD%E9%9B%A3%E9%8C%AF%E8%AA%A4/" rel="alternate" type="text/html" title="Fedora41 升版 42 的大災難錯誤" /><published>2025-07-15T00:00:00+00:00</published><updated>2025-07-15T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Fedora41%20%E5%8D%87%E7%89%88%2042%20%E7%9A%84%E5%A4%A7%E7%81%BD%E9%9B%A3%E9%8C%AF%E8%AA%A4</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Fedora41-%E5%8D%87%E7%89%88-42-%E7%9A%84%E5%A4%A7%E7%81%BD%E9%9B%A3%E9%8C%AF%E8%AA%A4/"><![CDATA[<p>近期幫我的桌機從 Fedora 41 升級到 Fedora 42，這是我第二次升級 Fedora 41。第一次是協助 Intel 125H 微電腦（內顯）升級，過程很順利，因此這次操作稍微輕忽。</p>

<p>這兩次升級的主要差異在於：</p>

<ol>
  <li>
    <p>桌機有安裝 Nvidia 官方驅動。</p>
  </li>
  <li>
    <p>這次使用手機 USB 網路分享，升級重開機後，疑似未自動連上網路，導致升級過程中部分套件未能完整更新。</p>
  </li>
</ol>

<p>升級後進入系統時，畫面只有白屏，並顯示「系統有錯誤，請聯絡管理員」，無法進入登入畫面，<del>也無法進行任何操作</del>（0716修正） 應該能進tty模式。</p>

<p>0716新增
應是安裝Nvidia專用驅動後，系統衝突，連白畫面都無法到達，所以透過Grub</p>

<p>透過 Grub，在對應 kernel 的 linux 行加入 <code class="language-plaintext highlighter-rouge">nomodeset 3</code>，強制以文字模式啟動，進入 TTY 修復模式。</p>

<p>一開始懷疑是 Nvidia 驅動與新版 GNOME 相容性問題，嘗試過移除與重裝官方及 Rpmfusion 驅動，但無法解決，系統依然無法進入圖形介面。</p>

<p>進一步檢查發現，<code class="language-plaintext highlighter-rouge">cat /etc/os-release</code> 顯示已為 Fedora 42，但系統內部仍有不少套件維持在 41 版本（例如 GNOME 與某些核心元件）。<br />
這種版本混雜，導致 GNOME 啟動時崩潰，無法進入桌面，只能進 TTY 模式維修。</p>

<h1 id="操作紀錄">操作紀錄</h1>

<p>由於先前曾誤刪 NetworkManager，導致開機時網路完全沒啟動，網卡也沒拿到 IP。</p>

<p><strong>網路完全沒啟動，網卡沒 IP</strong></p>

<h3 id="修復步驟臨時手動啟用網卡">修復步驟（臨時手動啟用網卡）</h3>

<ol>
  <li><strong>啟動網卡</strong>
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>先透過ip addr找到網卡 如enp6s0
<span class="nb">sudo </span>ip <span class="nb">link set </span>enp6s0 up
</code></pre></div>    </div>
  </li>
  <li>手動分配 IP（路由器所在網段）
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ip addr add 192.168.1.123/24 dev enp6s0
<span class="nb">sudo </span>ip route add default via 192.168.1.1
</code></pre></div>    </div>
    <p>（這邊 <code class="language-plaintext highlighter-rouge">192.168.1.1</code> 家 or 公司的閘道器，通常是路由器 IP）</p>
  </li>
  <li>設置 DNS 解析（臨時）
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"nameserver 8.8.8.8"</span> | <span class="nb">sudo tee</span> /etc/resolv.conf
</code></pre></div>    </div>
  </li>
  <li>測試網路是否通
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ping <span class="nt">-c</span> 3 8.8.8.8
ping <span class="nt">-c</span> 3 mirrors.fedoraproject.org
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="處理套件同步與衝突">處理套件同步與衝突</h3>

<p>使用 <code class="language-plaintext highlighter-rouge">dnf distro-sync</code> 同步時，遇到多個套件依賴衝突或版本錯誤。</p>

<p>目前解法為：</p>

<ul>
  <li>先移除出錯套件
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    sudo dnf remove &lt;出錯套件&gt;
</code></pre></div>    </div>
    <ul>
      <li>先將有問題（如升級衝突、版本錯誤或殘留）的套件移除，讓依賴樹恢復乾淨，避免後續操作又被同一個問題卡住。</li>
    </ul>
  </li>
  <li>再安裝必要套件
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nb">sudo </span>dnf <span class="nb">install</span> &lt;必要套件&gt;
</code></pre></div>    </div>
    <ul>
      <li>重新安裝所需的核心/重要套件，確保使用的是新版本並補齊所有依賴。</li>
    </ul>
  </li>
  <li>重複執行 <code class="language-plaintext highlighter-rouge">distro-sync</code>，直到全部同步完成。必要時可加上 <code class="language-plaintext highlighter-rouge">--allowerasing</code> 參數強制排除有衝突的舊套件。</li>
</ul>

<h3 id="補充建議">補充建議</h3>

<ul>
  <li>
    <p>網路連線不穩或升級途中網路中斷，是造成系統版本混雜的主因，<strong>升級時建議全程使用穩定的有線網路</strong>，並確認所有套件都已完整升級。</p>
  </li>
  <li>
    <p>針對無法同步或依賴衝突，建議每次遇到報錯時，先確認錯誤訊息，針對特定套件移除再安裝，並多次重複同步，直到不再出現衝突。</p>
  </li>
</ul>

<p>目前持續Debug中</p>

<h1 id="20240716-終於升版成功">20240716 終於升版成功</h1>

<p>總結以下幾點問題</p>

<h2 id="一fedora-系統版本升級">一、Fedora 系統版本升級</h2>

<p>我原先使用</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf system-upgrade download <span class="nt">--releasever</span><span class="o">=</span>42
<span class="nb">sudo </span>dnf system-upgrade reboot
</code></pre></div></div>
<p>但後來因 Fedora 42 已採用新工具（dnf5）</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf5 offline upgrade <span class="nt">--releasever</span><span class="o">=</span>42
<span class="nb">sudo </span>dnf5 offline reboot
</code></pre></div></div>

<p>但因為沒有正確完成離線升級步驟，導致系統套件版本部分仍停留在 Fedora 41，發生嚴重相依性衝突。</p>

<p>有可能是因為發現指令的修改 讓我reboot並未打在 upgrade後，造成升級錯誤
這要追蹤紀錄</p>

<h2 id="二nvidia-專有驅動重新安裝">二、NVIDIA 專有驅動重新安裝</h2>

<p>升級後，原本的 NVIDIA 驅動 (run file: 570.153.02) 無法正常運作，畫面黑屏</p>

<p>進入 TTY (Ctrl+Alt+F3)</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> ./NVIDIA-Linux-x86_64-570.153.02.run
</code></pre></div></div>

<p>安裝失敗或成功但啟動畫面仍然異常（可能 Wayland 衝突）</p>

<p>編輯 <code class="language-plaintext highlighter-rouge">/etc/gdm/custom.conf</code>，設置
強制使用 Xorg 而非 Wayland</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">WaylandEnable</span><span class="o">=</span><span class="nb">false
</span><span class="nv">DefaultSession</span><span class="o">=</span>gnome-xorg.desktop

</code></pre></div></div>

<p><strong>安裝了 NVIDIA <code class="language-plaintext highlighter-rouge">.run</code> 專有驅動</strong><br />
<strong>嘗試強制用 X11 (<code class="language-plaintext highlighter-rouge">WaylandEnable=false</code>) 但反而完全黑畫面，連 tty 都進不去</strong></p>

<p>通常表示：<strong><code class="language-plaintext highlighter-rouge">.run</code> 驅動與 kernel 模組或 framebuffer 設定發生衝突</strong>，導致整個顯示系統（包含 TTY）也壞了</p>

<p>開機進 GRUB，編輯 kernel 參數，按ｅ編輯</p>

<p><code class="language-plaintext highlighter-rouge">nomodeset</code>：避免驅動初始化 framebuffer，能避免黑畫面<br />
<code class="language-plaintext highlighter-rouge">3</code>：強制進入 runlevel 3（multi-user.target），不載入 GUI</p>

<p>卸載NVIDIA驅動後，終於能進指令模式</p>

<h2 id="三系統升級問題排查與修復">三、系統升級問題排查與修復</h2>

<p>GPU問題解決後，系統仍異常</p>

<p>嘗試了許多操作</p>

<ul>
  <li>清理 repo 快取，重建 dnf metadata
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf clean all
<span class="nb">sudo </span>dnf makecache
</code></pre></div>    </div>
  </li>
  <li>多個libicu 衝突</li>
  <li>因為 libicu 套件衝突（版本混亂，Fedora 41 與 42 混合）</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf remove libicu<span class="se">\*</span> libtiny<span class="se">\*</span>
<span class="nb">sudo </span>dnf <span class="nb">install </span>libicu libicu-devel

</code></pre></div></div>
<ul>
  <li><strong>使用 distro-sync 同步套件</strong>
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf distro-sync <span class="nt">--releasever</span><span class="o">=</span>42 <span class="nt">--allowerasing</span> <span class="nt">--skip-broken</span> <span class="nt">-y</span>
</code></pre></div>    </div>
    <p>用以強制同步系統套件到 Fedora 42，過程中遇到許多受保護的套件無法同步。</p>
  </li>
  <li><strong>強制手動移除 Fedora 41 套件資料庫記錄</strong></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>rpm <span class="nt">-e</span> <span class="nt">--justdb</span> <span class="nt">--nodeps</span> grub2-tools-minimal selinux-policy selinux-policy-targeted
</code></pre></div></div>
<p>此步驟是為了解決嚴重的相依性循環</p>

<h2 id="四網路問題處理-dns--networkmanager">四、網路問題處理 (DNS &amp; NetworkManager)</h2>

<p>因無法同步我一度移除 NetworkManager，導致網路中斷與無 DNS</p>

<ul>
  <li>手動恢復網路連線</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ip <span class="nb">link set </span>dev enp6s0 up
<span class="nb">sudo </span>ip addr add 192.168.50.199/24 dev enp6s0
<span class="nb">sudo </span>ip route add default via 192.168.50.1
<span class="nb">echo</span> <span class="s1">'nameserver 8.8.8.8'</span> | <span class="nb">sudo tee</span> /etc/resolv.conf
</code></pre></div></div>
<ul>
  <li>測試 DNS 和網路連通性</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ping 8.8.8.8
nslookup mirrors.fedoraproject.org
</code></pre></div></div>

<ul>
  <li>修正 DNS 解析異常 (IPv6 precedence issue)</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'precedence ::ffff:0:0/96 100'</span> | <span class="nb">sudo tee</span> <span class="nt">-a</span> /etc/gai.conf

</code></pre></div></div>

<p>後來發現修改這個nslookup還是無法解析host</p>

<ul>
  <li>最終透過修改 <code class="language-plaintext highlighter-rouge">/etc/nsswitch.conf</code></li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>從
hosts: myhostname mdns4_minimal <span class="o">[</span><span class="nv">NOTFOUND</span><span class="o">=</span><span class="k">return</span><span class="o">]</span> resolve <span class="o">[!</span><span class="nv">UNAVAIL</span><span class="o">=</span><span class="k">return</span><span class="o">]</span> dns files
修正為
hosts: files dns

</code></pre></div></div>
<p>問題根源是<strong><code class="language-plaintext highlighter-rouge">nsswitch.conf</code> 配置異常</strong></p>

<ul>
  <li>重新安裝 NetworkManager</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>NetworkManager <span class="nt">-y</span>
<span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> NetworkManager
</code></pre></div></div>

<p>網路與 DNS 終於正常運作</p>

<h2 id="五efi-與-grub2-問題處理">五、EFI 與 GRUB2 問題處理</h2>

<p>系統載入新內核</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 找出 grub.cfg 位置</span>
<span class="nb">sudo </span>find /boot <span class="nt">-name</span> <span class="s2">"grub.cfg"</span>
<span class="c"># 重新生成 grub.cfg  位置若有更動 os 會提示</span>
<span class="nb">sudo </span>grub2-mkconfig <span class="nt">-o</span>  新位置

</code></pre></div></div>

<h2 id="六安裝-gnome-桌面環境並啟用-gdm">六、安裝 GNOME 桌面環境並啟用 GDM</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安裝 GNOME 桌面環境</span>
<span class="nb">sudo </span>dnf-3 groupinstall <span class="s2">"GNOME Desktop Environment"</span> <span class="nt">-y</span>
<span class="c"># 啟用 GDM（圖形登入管理）</span>
<span class="nb">sudo </span>systemctl set-default graphical.target
<span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> gdm

<span class="nb">sudo </span>reboot
</code></pre></div></div>

<p>使用dnf(連結到dnf5)碰上語法已異動，修正為dnf-3</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">dnf-3</code> 是 Fedora 傳統的 dnf 版本，通常穩定度高。</li>
  <li>在升級災難發生後，使用 <code class="language-plaintext highlighter-rouge">dnf-3</code> 通常能有效避開 <code class="language-plaintext highlighter-rouge">dnf5</code> 的問題，提供更穩定的套件解析</li>
</ul>

<h2 id="關鍵指令">關鍵指令</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c"># Fedora 離線升級</span>
<span class="nb">sudo </span>dnf5 offline upgrade <span class="nt">--releasever</span><span class="o">=</span>42
<span class="c"># rpm 強制清空舊資料庫</span>
<span class="nb">sudo </span>rpm <span class="nt">-e</span> <span class="nt">--justdb</span> <span class="nt">--nodeps</span> <span class="si">$(</span>rpm <span class="nt">-qa</span> | <span class="nb">grep </span>fc41<span class="si">)</span>
<span class="c"># DNS/nsswitch.conf 修正</span>
<span class="c"># /etc/nsswitch.conf:</span>
hosts: files dns
<span class="c"># 強制指定 IPv4 DNS</span>
<span class="nb">echo</span> <span class="s1">'nameserver 8.8.8.8'</span> | <span class="nb">sudo tee</span> /etc/resolv.conf
<span class="c"># GNOME 桌面重裝</span>
<span class="nb">sudo </span>dnf-3 groupinstall <span class="s2">"GNOME Desktop Environment"</span> <span class="nt">-y</span>
<span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> gdm
</code></pre></div></div>

<h2 id="這次問題與操作的總結">這次問題與操作的總結</h2>

<table>
  <thead>
    <tr>
      <th>階段</th>
      <th>問題原因</th>
      <th>修復方法與使用指令</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>升級 Fedora 41→42</strong></td>
      <td>套件下載不完全或 repo 配置錯誤</td>
      <td>使用 <code class="language-plaintext highlighter-rouge">dnf system-upgrade</code> 和 <code class="language-plaintext highlighter-rouge">dnf5 offline</code> 方法；但最終因 repo 衝突導致混亂</td>
    </tr>
    <tr>
      <td><strong>NVIDIA 驅動衝突</strong></td>
      <td>專有驅動與新內核衝突</td>
      <td>使用 NVIDIA 官方 run 檔安裝，設置 <code class="language-plaintext highlighter-rouge">WaylandEnable=false</code> 強制用 Xorg</td>
    </tr>
    <tr>
      <td><strong>網路、DNS 異常</strong></td>
      <td>NetworkManager 誤刪、 <code class="language-plaintext highlighter-rouge">/etc/nsswitch.conf</code> 配置錯誤</td>
      <td>手動設置網路 (<code class="language-plaintext highlighter-rouge">ip addr add</code>)、強制 IPv4 (<code class="language-plaintext highlighter-rouge">/etc/gai.conf</code>)、修改 <code class="language-plaintext highlighter-rouge">/etc/nsswitch.conf</code> 為 <code class="language-plaintext highlighter-rouge">files dns</code> 修正 DNS</td>
    </tr>
    <tr>
      <td><strong>套件相依性災難</strong></td>
      <td>大量 Fedora 41 與 Fedora 42 混合相依性衝突</td>
      <td>使用 <code class="language-plaintext highlighter-rouge">rpm -e --justdb --nodeps</code> 清空 rpm 資料庫內所有 Fedora 41 舊版套件</td>
    </tr>
    <tr>
      <td><strong>GRUB、EFI啟動問題</strong></td>
      <td>多個 grub.cfg 路徑混亂、grub 未更新</td>
      <td>使用 <code class="language-plaintext highlighter-rouge">grub2-mkconfig</code> 更新 grub 啟動設定</td>
    </tr>
    <tr>
      <td><strong>GNOME 桌面缺失</strong></td>
      <td>升級後桌面環境損壞或移除</td>
      <td>使用 <code class="language-plaintext highlighter-rouge">dnf-3 groupinstall "GNOME Desktop Environment"</code> 重新安裝 GNOME</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="Linux" /><summary type="html"><![CDATA[近期幫我的桌機從 Fedora 41 升級到 Fedora 42，這是我第二次升級 Fedora 41。第一次是協助 Intel 125H 微電腦（內顯）升級，過程很順利，因此這次操作稍微輕忽。]]></summary></entry><entry><title type="html">Cookie劫持的對應</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Cookie%E5%8A%AB%E6%8C%81%E7%9A%84%E5%B0%8D%E6%87%89/" rel="alternate" type="text/html" title="Cookie劫持的對應" /><published>2025-07-10T00:00:00+00:00</published><updated>2025-07-10T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Cookie%E5%8A%AB%E6%8C%81%E7%9A%84%E5%B0%8D%E6%87%89</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Cookie%E5%8A%AB%E6%8C%81%E7%9A%84%E5%B0%8D%E6%87%89/"><![CDATA[<p>近期系統碰到一個漏洞，攻擊者可以透過攻擊者冒充其他登入的使用者</p>

<p>在使用 Cookie 紀錄登入狀態的 ASP.NET MVC 系統中，<br />
若攻擊者能成功竊取使用者的認證 Cookie（例如 Session ID 或身份憑證），<br />
只要將該 Cookie 複製到自己的瀏覽器中，即可冒用該用戶，繞過伺服器的身份驗證。</p>

<p>透過以下兩個方法做防禦</p>

<h3 id="1-db-綁定來源資訊ipuser-agent等">1. <strong>DB 綁定來源資訊（IP、User-Agent等）</strong></h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 在登入成功後</span>
 <span class="n">user</span><span class="p">.</span><span class="n">LoginIP</span> <span class="p">=</span> <span class="n">Request</span><span class="p">.</span><span class="n">UserHostAddress</span><span class="p">;</span>
 <span class="n">db</span><span class="p">.</span><span class="nf">SaveChanges</span><span class="p">();</span>

</code></pre></div></div>

<p>在用戶登入後，記錄來源 IP 或 User-Agent，之後每次請求都驗證</p>

<h4 id="2-請求時的監控">2. <strong>請求時的監控</strong></h4>

<p>此步驟為關鍵安全機制之一：</p>

<ol>
  <li>
    <p><strong>驗證使用者登入狀態</strong><br />
 每當有請求進入時，系統會在 <code class="language-plaintext highlighter-rouge">Application_AcquireRequestState</code> 事件中檢查使用者是否已登入（<code class="language-plaintext highlighter-rouge">IsAuthenticated == true</code>）。</p>
  </li>
  <li>
    <p><strong>IP 驗證邏輯</strong><br />
 若使用者已登入，系統會比對：</p>

    <ul>
      <li>
        <p>當前請求的 IP（<code class="language-plaintext highlighter-rouge">Request.UserHostAddress</code>）</p>
      </li>
      <li>
        <p>使用者登入時儲存的原始 IP（由資料庫取得）</p>
      </li>
    </ul>

    <p>若兩者不一致，視為潛在異常行為，系統會強制導向登出（<code class="language-plaintext highlighter-rouge">/Account/Logout</code>）。</p>
  </li>
  <li>
    <p><strong>避免登出遞迴陷阱</strong><br />
 由於 <code class="language-plaintext highlighter-rouge">Application_AcquireRequestState</code> 會在每次請求前執行，若沒有排除登入、登出、驗證等網址，將導致如下無限循環：</p>

    <ul>
      <li>
        <p>使用者發送請求 → 偵測到 IP 不一致 → 導向 <code class="language-plaintext highlighter-rouge">/Account/Logout</code></p>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">/Account/Logout</code> 也觸發 <code class="language-plaintext highlighter-rouge">AcquireRequestState</code> → 再次驗證失敗 → 再次導向 <code class="language-plaintext highlighter-rouge">/Account/Logout</code>…</p>
      </li>
      <li>
        <p>造成實際登出流程永遠無法執行</p>
      </li>
    </ul>

    <p>為避免此情況，需排除以下路徑的檢查：</p>

    <ul>
      <li>
        <p><code class="language-plaintext highlighter-rouge">/account/login</code></p>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">/account/logout</code></p>
      </li>
      <li>
        <p><code class="language-plaintext highlighter-rouge">/account/verify</code></p>
      </li>
    </ul>
  </li>
</ol>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">protected</span> <span class="k">void</span> <span class="nf">Application_AcquireRequestState</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">e</span><span class="p">)</span>
  <span class="p">{</span>
      <span class="kt">var</span> <span class="n">ctx</span> <span class="p">=</span> <span class="n">HttpContext</span><span class="p">.</span><span class="n">Current</span><span class="p">;</span>
      <span class="kt">var</span> <span class="n">path</span> <span class="p">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Path</span><span class="p">.</span><span class="nf">ToLower</span><span class="p">();</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">User</span><span class="p">.</span><span class="n">Identity</span><span class="p">.</span><span class="n">IsAuthenticated</span> <span class="p">&amp;&amp;</span> 
          <span class="p">!</span><span class="n">path</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"account/logout"</span><span class="p">)</span> <span class="p">&amp;&amp;</span> 
          <span class="p">!</span><span class="n">path</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"account/login"</span><span class="p">)</span> <span class="p">&amp;&amp;</span>  
          <span class="p">!</span><span class="n">path</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"account/verify"</span><span class="p">))</span>
      <span class="p">{</span>
          <span class="c1">// 取得當前 IP</span>

          <span class="kt">var</span> <span class="n">currentIP</span> <span class="p">=</span> <span class="n">ctx</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">UserHostAddress</span><span class="p">;</span>

          <span class="kt">var</span> <span class="n">originalIP</span> <span class="p">=</span> <span class="n">UserRepository</span><span class="p">.</span><span class="nf">GetUserById</span><span class="p">(</span><span class="n">ctx</span><span class="p">.</span><span class="n">User</span><span class="p">.</span><span class="n">Identity</span><span class="p">.</span><span class="n">Name</span><span class="p">);</span>

          <span class="c1">// 驗證 IP</span>
          <span class="k">if</span> <span class="p">(</span><span class="n">originalIP</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="n">originalIP</span> <span class="p">!=</span> <span class="n">currentIP</span><span class="p">)</span>
          <span class="p">{</span>
              <span class="c1">// IP 不一致，強制登出</span>
              <span class="n">ctx</span><span class="p">.</span><span class="n">Response</span><span class="p">.</span><span class="nf">Redirect</span><span class="p">(</span><span class="s">"/Account/Logout"</span><span class="p">);</span>
          <span class="p">}</span>
      <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="資安" /><summary type="html"><![CDATA[近期系統碰到一個漏洞，攻擊者可以透過攻擊者冒充其他登入的使用者]]></summary></entry><entry><title type="html">Numpy常見排序Library與應用</title><link href="https://xcv11xcv22.github.io/bardlee.github.io/Numpy%E5%B8%B8%E8%A6%8B%E6%8E%92%E5%BA%8FLibrary%E8%88%87%E6%87%89%E7%94%A8/" rel="alternate" type="text/html" title="Numpy常見排序Library與應用" /><published>2025-07-09T00:00:00+00:00</published><updated>2025-07-09T00:00:00+00:00</updated><id>https://xcv11xcv22.github.io/bardlee.github.io/Numpy%E5%B8%B8%E8%A6%8B%E6%8E%92%E5%BA%8FLibrary%E8%88%87%E6%87%89%E7%94%A8</id><content type="html" xml:base="https://xcv11xcv22.github.io/bardlee.github.io/Numpy%E5%B8%B8%E8%A6%8B%E6%8E%92%E5%BA%8FLibrary%E8%88%87%E6%87%89%E7%94%A8/"><![CDATA[<h1 id="1-npargsort">1. np.argsort</h1>
<ul>
  <li><code class="language-plaintext highlighter-rouge">np.argsort(scores)</code>：傳回的是分數排序後的原始索引（預設是升冪）</li>
  <li><code class="language-plaintext highlighter-rouge">[::-1]</code>：反轉 array → 降冪排序</li>
  <li>利用 <code class="language-plaintext highlighter-rouge">names[indices]</code> 可以對 array 根據 index 重排</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="n">names</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="s">'Alice'</span><span class="p">,</span> <span class="s">'Bob'</span><span class="p">,</span> <span class="s">'Charlie'</span><span class="p">,</span> <span class="s">'David'</span><span class="p">,</span> <span class="s">'Eva'</span><span class="p">])</span>
<span class="n">scores</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">85</span><span class="p">,</span> <span class="mi">92</span><span class="p">,</span> <span class="mi">88</span><span class="p">,</span> <span class="mi">76</span><span class="p">,</span> <span class="mi">90</span><span class="p">])</span>

<span class="c1"># 取得分數排序（從大到小）的 index
</span><span class="n">sorted_indices</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">scores</span><span class="p">)[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="c1"># 取前三名
</span><span class="n">top3_indices</span> <span class="o">=</span> <span class="n">sorted_indices</span><span class="p">[:</span><span class="mi">3</span><span class="p">]</span>

<span class="c1"># 對應姓名
</span><span class="n">top3_names</span> <span class="o">=</span> <span class="n">names</span><span class="p">[</span><span class="n">top3_indices</span><span class="p">]</span>
<span class="n">top3_scores</span> <span class="o">=</span> <span class="n">scores</span><span class="p">[</span><span class="n">top3_indices</span><span class="p">]</span>

<span class="k">print</span><span class="p">(</span><span class="n">top3_names</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">top3_scores</span><span class="p">)</span>

<span class="c1"># 輸出
</span><span class="p">[</span><span class="s">'Bob'</span> <span class="s">'Eva'</span> <span class="s">'Charlie'</span><span class="p">]</span>
<span class="p">[</span><span class="mi">92</span> <span class="mi">90</span> <span class="mi">88</span><span class="p">]</span>

</code></pre></div></div>

<h1 id="2-npsort">2. np.sort</h1>

<h2 id="排序多維陣列的每一列">排序多維陣列的每一列</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">np.sort(arr, axis=1)</code>：對每一 row 做升冪排序</li>
  <li><code class="language-plaintext highlighter-rouge">axis=0</code> 則會針對每一「欄」做排序（column-wise）</li>
  <li><code class="language-plaintext highlighter-rouge">[:, ::-1]</code>：反轉每列順序，從升冪變降冪</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
                <span class="p">[</span><span class="mi">9</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
                <span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">]])</span>

<span class="n">sorted_arr</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">sort</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">sorted_arr</span><span class="p">)</span>

</code></pre></div></div>

<h1 id="3-nplexsort">3. np.lexsort</h1>

<p>用來<strong>多欄位排序</strong>（lexicographical sort，字典序排序）的方法</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="c1"># 先依「班級」排序，再依「成績」排序
</span><span class="n">classes</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span>
<span class="n">scores</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">60</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">90</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">70</span><span class="p">])</span>
<span class="n">idx</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">lexsort</span><span class="p">((</span><span class="n">scores</span><span class="p">,</span> <span class="n">classes</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">classes</span><span class="p">[</span><span class="n">idx</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">scores</span><span class="p">[</span><span class="n">idx</span><span class="p">])</span>


</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">np.lexsort((次要, 主要))</code><br />
  — 右邊最後一個是「最主要的排序依據」。</li>
  <li>回傳排序後的 index，<strong>可用於重排原本的陣列</strong>。</li>
  <li>-<strong>np.lexsort 永遠是升序。</strong></li>
  <li><strong>要降序→對 key 做處理 給Key負值即可。</strong></li>
</ul>

<h1 id="4-npargmax">4. np.argmax</h1>

<p>取得指定維度最大索引</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">max_index</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">max_index</span><span class="p">)</span>

</code></pre></div></div>

<h1 id="5-npmax">5. np.max</h1>

<p>取得指定維度最大值</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">max_value</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">arr</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">max_value</span><span class="p">)</span>
</code></pre></div></div>

<h1 id="按條件排序應用">按條件排序應用</h1>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="n">names</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="s">'Ann'</span><span class="p">,</span> <span class="s">'Ben'</span><span class="p">,</span> <span class="s">'Cathy'</span><span class="p">,</span> <span class="s">'David'</span><span class="p">,</span> <span class="s">'Eve'</span><span class="p">])</span>
<span class="n">scores</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">95</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">80</span><span class="p">,</span> <span class="mi">45</span><span class="p">,</span> <span class="mi">70</span><span class="p">])</span>

<span class="n">pass_mask</span> <span class="o">=</span> <span class="n">scores</span> <span class="o">&gt;=</span> <span class="mi">60</span>
<span class="c1"># 只對通過的分數排序
</span><span class="n">sorted_indices</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">scores</span><span class="p">[</span><span class="n">pass_mask</span><span class="p">])</span>
<span class="c1"># 原始及格的 index
</span><span class="n">pass_indices</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">pass_mask</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="c1"># 新順序
</span><span class="n">new_order</span> <span class="o">=</span> <span class="n">pass_indices</span><span class="p">[</span><span class="n">sorted_indices</span><span class="p">]</span>

<span class="c1"># 建立新 array：不及格的留在原位
</span><span class="n">sorted_scores</span> <span class="o">=</span> <span class="n">scores</span><span class="p">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">sorted_names</span> <span class="o">=</span> <span class="n">names</span><span class="p">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">sorted_scores</span><span class="p">[</span><span class="n">pass_mask</span><span class="p">]</span> <span class="o">=</span> <span class="n">scores</span><span class="p">[</span><span class="n">new_order</span><span class="p">]</span>
<span class="n">sorted_names</span><span class="p">[</span><span class="n">pass_mask</span><span class="p">]</span> <span class="o">=</span> <span class="n">names</span><span class="p">[</span><span class="n">new_order</span><span class="p">]</span>

<span class="k">print</span><span class="p">(</span><span class="n">sorted_names</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">sorted_scores</span><span class="p">)</span>
</code></pre></div></div>

<ul>
  <li><strong>np.where 的輸出為tupple</strong></li>
</ul>

<h1 id="維度的mask重點">維度的Mask重點</h1>

<h2 id="一維">一維</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">])</span>
<span class="n">mask</span> <span class="o">=</span> <span class="n">arr</span> <span class="o">&gt;</span> <span class="mi">5</span>

<span class="k">print</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">mask</span><span class="p">))</span>    <span class="c1"># 輸出: (array([1, 3]),)
</span>
</code></pre></div></div>

<h2 id="二維">二維</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>

<span class="n">arr</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]])</span>
<span class="n">mask</span> <span class="o">=</span> <span class="n">arr</span> <span class="o">&gt;</span> <span class="mi">2</span>
<span class="n">idx_tuple</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">where</span><span class="p">(</span><span class="n">mask</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">idx_tuple</span><span class="p">)</span>       <span class="c1"># (array([1, 1]), array([0, 1]))
</span></code></pre></div></div>

<ul>
  <li>這裡 <code class="language-plaintext highlighter-rouge">[1, 1]</code> 是 row index，<code class="language-plaintext highlighter-rouge">[0, 1]</code> 是 column index</li>
</ul>]]></content><author><name>Bard</name><email>yountai.lee@gmail.com</email></author><category term="Numpy" /><summary type="html"><![CDATA[1. np.argsort np.argsort(scores)：傳回的是分數排序後的原始索引（預設是升冪） [::-1]：反轉 array → 降冪排序 利用 names[indices] 可以對 array 根據 index 重排]]></summary></entry></feed>