首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 通訊錄管理系統:基於 XML 持久化的分層架構完整實作

     編輯:Java 相關知識

專案概述

本系統為教學級 Java 桌面應用,採用 **XML 檔案持久化** (DOM 解析)、**兩層架構** (UI + DAO)、**命令列介面 (CLI)** 互動,實現用戶認證、通訊錄 CRUD 功能。適合學習 DOM XML 操作、分層設計、工廠模式、序列化替代方案。

一、資料模型設計 (XML 結構)

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<contacts>
  <user name="Jack" pwd="1234">
    <contact id="707dede609dd4a2990f7cfa4cd5233f9">
      <name>xiaoming</name>
      <sex>male</sex>
      <tel>123456</tel>
    </contact>
    <contact id="80983802eaa6402d8bac8bb39e71c48f">
      <name>12</name>
      <sex>12</sex>
      <tel>12</tel>
    </contact>
  </user>
  <user name="Rose" pwd="4321">
    <contact id="eedb795b97194c3aaa9bacda7e2948e9">
      <name>xiaoming</name>
      <sex>female</sex>
      <tel>123</tel>
    </contact>
  </user>
</contacts>
  • 根元素 <contacts> 包含多個 <user>
  • <user> 屬性:name (用戶名)、pwd (密碼)
  • <contact> 屬性:id (UUID),子元素:name, sex, tel

二、分層架構與套件結構

cn.hncu.contact
├── util/
│   ├── IDGenerate.java          // UUID 生成器
│   └── myDocumentFactory.java   // DOM Document 單例工廠 (載入/儲存 XML)
├── dao/
│   ├── contactDAO.java          // DAO 介面
│   ├── contactImpl.java         // DOM 實作
│   └── contactDAOFactory.java   // 工廠
└── cmd/
    └── contanctAction.java      // CLI 互動邏輯 (主程式入口)

三、核心類別完整實作

1. UUID 生成器 (IDGenerate)

package cn.hncu.contact.util;
import java.util.UUID;

public class IDGenerate {
    private IDGenerate() {}
    public static String getID() {
        return UUID.randomUUID().toString().replace("-", ""); // 32 位純字串
    }
}

2. DOM Document 工廠 (單例模式,記憶體中操作,統一儲存)

package cn.hncu.contact.util;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

public class myDocumentFactory {
    private static final String FILE_NAME = "./xml/users.xml";
    private static Document dom = null;

    static {
        try {
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            dom = db.parse(FILE_NAME); // 啟動時載入整個 XML 到記憶體
        } catch (Exception e) {
            throw new RuntimeException("XML 文檔解析失敗", e);
        }
    }

    public static Document getDocument() {
        return dom;
    }

    public static void save() {
        try {
            Transformer tf = TransformerFactory.newInstance().newTransformer();
            tf.transform(new DOMSource(dom), new StreamResult(FILE_NAME));
        } catch (Exception e) {
            throw new RuntimeException("XML 文檔儲存失敗", e);
        }
    }
}

3. DAO 介面 (contactDAO)

package cn.hncu.contact.dao;

import java.util.List;
import java.util.Map;
import org.w3c.dom.Element;

public interface contactDAO {
    boolean login(String name, String pwd);
    List> queryAll();        // 當前用戶所有聯繫人
    Element add(String name, String sex, String tel);
    void reg(String name, String pwd);           // 註冊新用戶
    Element delete(String id);                   // 刪除聯繫人
    Element change(String id, String name, String sex, String tel);
}

4. DAO 實作 (contactImpl) - 核心 DOM 操作邏輯

package cn.hncu.contact.dao;

import java.util.*;
import org.w3c.dom.*;
import cn.hncu.contact.util.*;

public class contactImpl implements contactDAO {
    private Element currentUser; // 當前登入用戶節點 (記憶體引用)
    private Document dom;

    public contactImpl() {
        this.dom = myDocumentFactory.getDocument();
    }

    @Override
    public boolean login(String name, String pwd) {
        dom = myDocumentFactory.getDocument(); // 重新獲取最新 DOM
        Element root = (Element) dom.getFirstChild(); // 
        NodeList users = root.getElementsByTagName("user");
        
        for (int i = 0; i < users.getLength(); i++) {
            Element user = (Element) users.item(i);
            if (user.getAttribute("name").equalsIgnoreCase(name) 
                && user.getAttribute("pwd").equalsIgnoreCase(pwd)) {
                currentUser = user; // 緩存當前用戶節點
                return true;
            }
        }
        currentUser = null;
        return false;
    }

    @Override
    public List> queryAll() {
        List> list = new ArrayList<>();
        if (currentUser == null) return list;
        
        NodeList contacts = currentUser.getElementsByTagName("contact");
        for (int i = 0; i < contacts.getLength(); i++) {
            Element contact = (Element) contacts.item(i);
            Map map = new HashMap<>();
            map.put("id", contact.getAttribute("id"));
            map.put("name", getTextContent(contact, "name"));
            map.put("sex", getTextContent(contact, "sex"));
            map.put("tel", getTextContent(contact, "tel"));
            list.add(map);
        }
        return list;
    }

    private String getTextContent(Element parent, String tagName) {
        NodeList nl = parent.getElementsByTagName(tagName);
        return (nl.getLength() > 0) ? nl.item(0).getTextContent() : "";
    }

    @Override
    public Element add(String name, String sex, String tel) {
        Document dom = myDocumentFactory.getDocument();
        Element newContact = dom.createElement("contact");
        newContact.setAttribute("id", IDGenerate.getID());
        
        appendTextElement(dom, newContact, "name", name);
        appendTextElement(dom, newContact, "sex", sex);
        appendTextElement(dom, newContact, "tel", tel);
        
        currentUser.appendChild(newContact); // 掛載到當前用戶下
        myDocumentFactory.save(); // 持久化
        return newContact;
    }

    private void appendTextElement(Document doc, Element parent, String tag, String text) {
        Element elem = doc.createElement(tag);
        elem.setTextContent(text);
        parent.appendChild(elem);
    }

    @Override
    public Element delete(String id) {
        NodeList contacts = currentUser.getElementsByTagName("contact");
        for (int i = 0; i < contacts.getLength(); i++) {
            Element contact = (Element) contacts.item(i);
            if (contact.getAttribute("id").equals(id)) {
                currentUser.removeChild(contact);
                myDocumentFactory.save();
                return contact;
            }
        }
        return null;
    }

    @Override
    public void reg(String name, String pwd) {
        Document dom = myDocumentFactory.getDocument();
        Element newUser = dom.createElement("user");
        newUser.setAttribute("name", name);
        newUser.setAttribute("pwd", pwd);
        dom.getFirstChild().appendChild(newUser); // 掛載到 
        myDocumentFactory.save();
    }

    @Override
    public Element change(String id, String name, String sex, String tel) {
        NodeList contacts = currentUser.getElementsByTagName("contact");
        for (int i = 0; i < contacts.getLength(); i++) {
            Element contact = (Element) contacts.item(i);
            if (contact.getAttribute("id").equals(id)) {
                setTextContent(contact, "name", name);
                setTextContent(contact, "sex", sex);
                setTextContent(contact, "tel", tel);
                myDocumentFactory.save();
                return contact;
            }
        }
        return null;
    }

    private void setTextContent(Element parent, String tag, String text) {
        NodeList nl = parent.getElementsByTagName(tag);
        if (nl.getLength() > 0) {
            nl.item(0).setTextContent(text);
        }
    }
}

5. CLI 互動主程式 (contanctAction)

package cn.hncu.contact.cmd;

import java.util.*;
import org.w3c.dom.Element;
import cn.hncu.contact.dao.*;

public class contanctAction {
    private contactDAO dao = contactDAOFactory.getContactDAO();
    private Scanner sc = new Scanner(System.in);
    private String[] delIds; // 當前查詢結果 ID 陣列 (對應顯示順序)

    public contanctAction() {
        while (true) {
            printMainMenu();
            String op = sc.nextLine();
            if ("1".equals(op)) login();
            else if ("2".equals(op)) reg();
            else break; // 0 退出
        }
    }

    private void printMainMenu() {
        System.out.println("\n=== 通訊錄系統 ===");
        System.out.println("1: 登入");
        System.out.println("2: 註冊");
        System.out.println("0: 退出");
        System.out.print("請選擇: ");
    }

    private void login() {
        System.out.print("用戶名: "); String name = sc.nextLine();
        System.out.print("密碼: "); String pwd = sc.nextLine();
        if (dao.login(name, pwd)) {
            System.out.println("✅ 登入成功");
            operationMenu();
        } else {
            System.out.println("❌ 登入失敗");
        }
    }

    private void reg() {
        System.out.print("用戶名: "); String name = sc.nextLine();
        System.out.print("密碼: "); String pwd = sc.nextLine();
        System.out.print("確認密碼: "); String pwd2 = sc.nextLine();
        if (pwd.equals(pwd2)) {
            dao.reg(name, pwd);
            System.out.println("✅ 註冊成功");
        } else {
            System.out.println("❌ 兩次密碼不一致");
        }
    }

    private void operationMenu() {
        while (true) {
            List> list = dao.queryAll();
            delIds = new String[list.size()];
            
            System.out.println("\n=== 功能選單 ===");
            System.out.println("1: 顯示聯繫人");
            System.out.println("2: 新增聯繫人");
            System.out.println("3: 刪除聯繫人");
            System.out.println("4: 修改聯繫人");
            System.out.println("0: 登出");
            System.out.print("請選擇: ");
            
            String sel = sc.nextLine();
            if ("1".equals(sel)) displayContacts(list);
            else if ("2".equals(sel)) addContact();
            else if ("3".equals(sel)) deleteContact(list);
            else if ("4".equals(sel)) changeContact(list);
            else if ("0".equals(sel)) break;
        }
    }

    private void displayContacts(List> list) {
        System.out.println("\n編號\t姓名\t性別\t電話");
        System.out.println("------------------------------");
        for (int i = 0; i < list.size(); i++) {
            Map m = list.get(i);
            delIds[i] = m.get("id");
            System.out.printf("%d\t%s\t%s\t%s%n", i+1, m.get("name"), m.get("sex"), m.get("tel"));
        }
    }

    private void addContact() {
        System.out.print("姓名: "); String name = sc.nextLine();
        System.out.print("性別: "); String sex = sc.nextLine();
        System.out.print("電話: "); String tel = sc.nextLine();
        Element e = dao.add(name, sex, tel);
        System.out.println("✅ 新增成功: " + e.getElementsByTagName("name").item(0).getTextContent());
    }

    private void deleteContact(List> list) {
        if (list.isEmpty()) { System.out.println("無聯繫人可刪除"); return; }
        displayContacts(list);
        System.out.print("請輸入刪除編號: ");
        try {
            int num = Integer.parseInt(sc.nextLine());
            if (num < 1 || num > delIds.length) { System.out.println("編號無效"); return; }
            Element e = dao.delete(delIds[num-1]);
            if (e != null) System.out.println("✅ 刪除成功: " + getName(e));
            else System.out.println("❌ 刪除失敗");
        } catch (NumberFormatException e) { System.out.println("請輸入數字"); }
    }

    private void changeContact(List> list) {
        if (list.isEmpty()) { System.out.println("無聯繫人可修改"); return; }
        displayContacts(list);
        System.out.print("請輸入修改編號: ");
        try {
            int num = Integer.parseInt(sc.nextLine());
            if (num < 1 || num > delIds.length) { System.out.println("編號無效"); return; }
            System.out.print("新姓名: "); String name = sc.nextLine();
            System.out.print("新性別: "); String sex = sc.nextLine();
            System.out.print("新電話: "); String tel = sc.nextLine();
            Element e = dao.change(delIds[num-1], name, sex, tel);
            if (e != null) System.out.println("✅ 修改成功: " + getName(e));
            else System.out.println("❌ 修改失敗");
        } catch (NumberFormatException e) { System.out.println("請輸入數字"); }
    }

    private String getName(Element contact) {
        return contact.getElementsByTagName("name").item(0).getTextContent();
    }

    public static void main(String[] args) {
        new contanctAction();
    }
}

四、關鍵技術點總結

技術點實作方式優點缺點/限制
XML 持久化DOM 解析 (整樹載入記憶體)結構清晰、可讀性強、支援複雜查詢、標準 API記憶體佔用大、不適合大數據量 (>10MB)
單例 DocumentmyDocumentFactory 靜態載入/儲存避免重複解析、操作統一記憶體模型多執行緒不安全、需外部同步
UUID 主鍵UUID.randomUUID() 去連字號全域唯一、無需中心協調、分散式友好長度 32 字符、非人類可讀
分層架構DAO 介面 + 實作 + 工廠關注點分離、可測試、易替換儲存引擎樣板代碼稍多
CLI 互動Scanner + 狀態機選單零依賴、易理解、適合教學演示無圖形介面、輸入驗證較弱

五、擴展建議

  1. SAX/StAX 替代 DOM:大檔案場景下流式讀寫,降低記憶體。
  2. 密碼加密:儲存前用 BCrypt/Argon2 雜湊,絕不存明文。
  3. Swing/JavaFX GUI:將 CLI 替換為圖形介面,復用 DAO 層。
  4. 資料庫遷移:實作 contactDAO 的 JDBC/JPA 版本,XML 版本無需修改上層邏輯。
  5. 單元測試:使用臨時 XML 檔案測試 DAO CRUD 邏輯。

結語

本系統展示了 **Java 標準 DOM API 操作 XML**、**分層架構 (UI/DAO/Util)**、**工廠模式解耦**、**UUID 主鍵設計**、**CLI 狀態機互動** 等核心技能。雖為教學簡化版,但架構清晰、邏輯完整,是進階學習資料庫應用、Web 開發、企業級架構的優秀基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved