Java 从 XML 到设计模式:零基础到精通指南(附代码详解)
XML 作为数据交换和配置的经典格式,与设计模式(代码设计的最佳实践)共同构成了 Java 开发的重要基础。本文从 XML 基础、解析技术到设计模式,通过实例代码逐步讲解,帮助零基础读者实现从入门到精通的跨越。
一、XML 基础:数据描述的通用语言
1. 什么是 XML?
XML(Extensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言,其核心是自定义标签,允许开发者根据需求定义标签名,实现数据的结构化描述。
与 HTML 的区别:
- HTML 是预定义标签(如
<p>、<div>),用于展示数据; - XML 无预定义标签,专注于描述数据的结构和含义(如
<user>、<order>)。
2. XML 基本语法与结构
(1)基本规则
- 标签成对出现:
<tag>内容</tag>(空标签可简写为<tag/>); - 嵌套有序:子标签必须完全包含在父标签内,不能交叉;
- 大小写敏感:
<User>和<user>是不同标签; - 必须有根标签:整个 XML 有且仅有一个根标签。
(2)示例:用户信息 XML
<?xml version="1.0" encoding="UTF-8"?> <!-- 声明:版本和编码 -->
<users> <!-- 根标签 -->
<user id="1001"> <!-- 属性:id 描述用户唯一标识 -->
<name>张三</name> <!-- 子标签:姓名 -->
<age>25</age> <!-- 子标签:年龄 -->
<address>
<city>北京</city>
<street>中关村</street>
</address>
</user>
<user id="1002">
<name>李四</name>
<age>30</age>
<address>
<city>上海</city>
<street>陆家嘴</street>
</address>
</user>
</users>说明:
<?xml ...?>是 XML 声明,定义版本和编码;<user id="1001">中id是属性,用于补充标签信息;- 标签嵌套形成层次结构,清晰描述数据间的关系。
3. XML 的应用场景
- 配置文件:Java 框架(如 Spring、MyBatis)用 XML 配置Bean、映射关系等;
- 数据交换:不同系统间通过 XML 传输数据(如接口调用返回 XML 格式结果);
- 存储数据:小型应用用 XML 替代数据库存储简单数据。
二、XML 解析:Java 读取 XML 数据的四种方式
Java 中解析 XML 的核心是将 XML 文档转换为程序可操作的对象。常用解析方式有 DOM、SAX、JDOM、DOM4J,各有适用场景。
1. DOM 解析:基于文档对象模型
DOM(Document Object Model)将整个 XML 文档加载到内存,生成一个树形结构(文档对象模型),允许对节点进行增删改查。
特点:
- 优点:支持随机访问(可任意操作任意节点)、支持修改 XML;
- 缺点:加载整个文档到内存,适合小型 XML(大型 XML 可能导致内存溢出)。
代码示例:DOM 解析用户 XML
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class DomParserDemo {
public static void main(String[] args) throws Exception {
// 1. 创建 DOM 解析器工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// 2. 获取解析器
DocumentBuilder builder = factory.newDocumentBuilder();
// 3. 解析 XML 文件,生成文档对象
Document document = builder.parse(new File("src/users.xml"));
// 4. 获取根标签 <users>
Element root = document.getDocumentElement();
System.out.println("根标签:" + root.getNodeName());
// 5. 获取所有 <user> 标签
NodeList userNodes = root.getElementsByTagName("user");
System.out.println("用户数量:" + userNodes.getLength());
// 6. 遍历用户节点
for (int i = 0; i < userNodes.getLength(); i++) {
Element userElement = (Element) userNodes.item(i);
// 获取属性 id
String id = userElement.getAttribute("id");
// 获取子标签内容
String name = userElement.getElementsByTagName("name").item(0).getTextContent();
String age = userElement.getElementsByTagName("age").item(0).getTextContent();
// 获取嵌套子标签 <city>
String city = userElement.getElementsByTagName("city").item(0).getTextContent();
System.out.printf("用户ID:%s,姓名:%s,年龄:%s,城市:%s%n", id, name, age, city);
}
}
}输出结果:
根标签:users
用户数量:2
用户ID:1001,姓名:张三,年龄:25,城市:北京
用户ID:1002,姓名:李四,年龄:30,城市:上海2. SAX 解析:基于事件驱动
SAX(Simple API for XML)是一种事件驱动的解析方式,逐行扫描 XML 文档,触发startElement、endElement等事件,无需加载整个文档到内存。
特点:
- 优点:内存占用小,适合大型 XML(如几GB的日志文件);
- 缺点:仅支持读取(无法修改),不支持随机访问(只能顺序解析)。
代码示例:SAX 解析用户 XML
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
// 自定义处理器:重写事件方法
class UserHandler extends DefaultHandler {
private boolean isName = false;
private boolean isAge = false;
private boolean isCity = false;
private String currentId;
// 开始解析标签时触发
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals("user")) {
// 获取 user 标签的 id 属性
currentId = attributes.getValue("id");
System.out.println("用户ID:" + currentId);
} else if (qName.equals("name")) {
isName = true; // 标记即将读取 name 内容
} else if (qName.equals("age")) {
isAge = true;
} else if (qName.equals("city")) {
isCity = true;
}
}
// 读取标签内容时触发
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (isName) {
System.out.println("姓名:" + new String(ch, start, length));
isName = false; // 重置标记
} else if (isAge) {
System.out.println("年龄:" + new String(ch, start, length));
isAge = false;
} else if (isCity) {
System.out.println("城市:" + new String(ch, start, length));
isCity = false;
}
}
}
public class SaxParserDemo {
public static void main(String[] args) throws Exception {
// 1. 创建 SAX 解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2. 获取解析器
SAXParser saxParser = factory.newSAXParser();
// 3. 解析 XML,使用自定义处理器
saxParser.parse(new File("src/users.xml"), new UserHandler());
}
}输出结果:
用户ID:1001
姓名:张三
年龄:25
城市:北京
用户ID:1002
姓名:李四
年龄:30
城市:上海3. DOM4J 解析:高效易用的主流方案
DOM4J 是一个开源解析框架(需导入依赖),结合了 DOM 和 SAX 的优点,支持 XPath 快速查询,是实际开发中最常用的解析方式。
(1)导入 DOM4J 依赖(Maven)
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 支持 XPath -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>(2)代码示例:DOM4J 解析 + XPath 查询
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
public class Dom4jParserDemo {
public static void main(String[] args) throws DocumentException {
// 1. 创建 SAXReader(DOM4J 的解析器)
SAXReader reader = new SAXReader();
// 2. 读取 XML 文件
Document document = reader.read(new File("src/users.xml"));
// 3. 获取根标签
Element root = document.getRootElement();
System.out.println("根标签:" + root.getName());
// 4. 遍历所有 user 标签(传统方式)
List<Element> userElements = root.elements("user");
for (Element user : userElements) {
String id = user.attributeValue("id");
String name = user.elementText("name"); // 直接获取子标签文本
System.out.printf("传统遍历:ID=%s,姓名=%s%n", id, name);
}
// 5. 使用 XPath 快速查询(推荐)
// 查询所有 user 标签中 city 为 "北京" 的 name
List<Element> beijingUsers = root.selectNodes("//user[address/city='北京']/name");
for (Element name : beijingUsers) {
System.out.println("北京用户:" + name.getText()); // 输出:张三
}
// 查询 id=1002 的 user 的 age
Element ageElement = (Element) root.selectSingleNode("//user[@id='1002']/age");
System.out.println("用户1002的年龄:" + ageElement.getText()); // 输出:30
}
}XPath 语法说明:
//user:匹配所有 user 标签(无论层级);user[@id='1002']:匹配 id 属性为 1002 的 user 标签;address/city='北京':筛选 address 子标签中 city 为北京的节点。
4. 四种解析方式对比与选择
| 解析方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| DOM | 支持增删改查、随机访问 | 内存占用大 | 小型 XML、需修改数据 |
| SAX | 内存占用小、速度快 | 仅支持读取、顺序访问 | 大型 XML、仅需读取数据 |
| JDOM | 简化 DOM API、纯 Java 实现 | 功能较少 | 中等规模 XML |
| DOM4J | 支持 XPath、功能全面、易用 | 需导入第三方依赖 | 大部分场景(推荐) |
三、设计模式:Java 代码设计的最佳实践
设计模式是对反复出现的问题的标准化解决方案,目的是提高代码的可复用性、可维护性和扩展性。Java 中常用的设计模式有 23 种,这里重点讲解 5 种基础且高频使用的模式。
1. 单例模式(Singleton Pattern)
意图:确保一个类仅有一个实例,并提供全局访问点(如工具类、配置管理器)。
(1)饿汉式(线程安全,简单)
public class SingletonHungry {
// 1. 私有构造器:防止外部创建实例
private SingletonHungry() {}
// 2. 私有静态实例(类加载时初始化)
private static final SingletonHungry INSTANCE = new SingletonHungry();
// 3. 公开静态方法:返回唯一实例
public static SingletonHungry getInstance() {
return INSTANCE;
}
}(2)懒汉式(延迟初始化,线程安全版)
public class SingletonLazy {
private SingletonLazy() {}
// volatile 防止指令重排序
private static volatile SingletonLazy instance;
// 双重检查锁(DCL)保证线程安全
public static SingletonLazy getInstance() {
if (instance == null) { // 第一次检查:减少锁竞争
synchronized (SingletonLazy.class) { // 加锁
if (instance == null) { // 第二次检查:防止多线程同时创建
instance = new SingletonLazy();
}
}
}
return instance;
}
}应用场景:日志工具类(Logger)、数据库连接池(DataSource)。
2. 简单工厂模式(Simple Factory)
意图:通过一个工厂类统一创建不同类型的对象,隐藏对象创建的细节。
代码示例:计算器工厂
// 1. 抽象产品:运算
interface Operation {
double calculate(double a, double b);
}
// 2. 具体产品:加法
class AddOperation implements Operation {
@Override
public double calculate(double a, double b) {
return a + b;
}
}
// 具体产品:减法
class SubtractOperation implements Operation {
@Override
public double calculate(double a, double b) {
return a - b;
}
}
// 3. 工厂类:创建运算对象
class OperationFactory {
// 根据类型创建对应运算实例
public static Operation createOperation(String type) {
switch (type) {
case "+": return new AddOperation();
case "-": return new SubtractOperation();
default: throw new IllegalArgumentException("无效运算类型");
}
}
}
// 测试
public class SimpleFactoryDemo {
public static void main(String[] args) {
// 无需直接 new 对象,通过工厂创建
Operation add = OperationFactory.createOperation("+");
System.out.println("3+5=" + add.calculate(3, 5)); // 8
Operation subtract = OperationFactory.createOperation("-");
System.out.println("10-4=" + subtract.calculate(10, 4)); // 6
}
}优点:客户端无需知道对象创建细节,只需调用工厂方法,符合“单一职责原则”。
3. 观察者模式(Observer Pattern)
意图:定义对象间的一对多依赖关系,当一个对象状态变化时,所有依赖它的对象会自动收到通知(如事件监听、消息订阅)。
代码示例:天气预报系统
import java.util.ArrayList;
import java.util.List;
// 1. 被观察者接口(主题)
interface Subject {
void registerObserver(Observer observer); // 注册观察者
void removeObserver(Observer observer); // 移除观察者
void notifyObservers(); // 通知所有观察者
}
// 2. 观察者接口
interface Observer {
void update(float temperature); // 接收更新
}
// 3. 具体被观察者:天气预报站
class WeatherStation implements Subject {
private float temperature; // 温度(被观察的状态)
private List<Observer> observers = new ArrayList<>();
public void setTemperature(float temperature) {
this.temperature = temperature;
notifyObservers(); // 温度变化时通知观察者
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
// 通知所有观察者
for (Observer observer : observers) {
observer.update(temperature);
}
}
}
// 4. 具体观察者:手机客户端
class PhoneDisplay implements Observer {
@Override
public void update(float temperature) {
System.out.println("手机显示:当前温度 " + temperature + "℃");
}
}
// 具体观察者:电脑客户端
class ComputerDisplay implements Observer {
@Override
public void update(float temperature) {
System.out.println("电脑显示:当前温度 " + temperature + "℃");
}
}
// 测试
public class ObserverDemo {
public static void main(String[] args) {
// 创建被观察者
WeatherStation station = new WeatherStation();
// 创建观察者
Observer phone = new PhoneDisplay();
Observer computer = new ComputerDisplay();
// 注册观察者
station.registerObserver(phone);
station.registerObserver(computer);
// 温度变化(触发通知)
station.setTemperature(25.5f);
// 输出:
// 手机显示:当前温度 25.5℃
// 电脑显示:当前温度 25.5℃
}
}应用场景:GUI 事件监听(如按钮点击)、消息队列订阅。
4. 策略模式(Strategy Pattern)
意图:定义一系列算法,将每个算法封装起来,使它们可互相替换(如不同的排序算法、支付方式)。
代码示例:支付系统
// 1. 策略接口:支付方式
interface PaymentStrategy {
void pay(double amount);
}
// 2. 具体策略:支付宝支付
class AlipayStrategy implements PaymentStrategy {
private String username;
public AlipayStrategy(String username) {
this.username = username;
}
@Override
public void pay(double amount) {
System.out.println(username + "使用支付宝支付 " + amount + "元");
}
}
// 具体策略:微信支付
class WechatPayStrategy implements PaymentStrategy {
private String openId;
public WechatPayStrategy(String openId) {
this.openId = openId;
}
@Override
public void pay(double amount) {
System.out.println(openId + "使用微信支付 " + amount + "元");
}
}
// 3. 上下文:订单(使用策略)
class Order {
private PaymentStrategy paymentStrategy;
// 设置支付策略(灵活切换)
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
// 支付
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}
// 测试
public class StrategyDemo {
public static void main(String[] args) {
Order order = new Order();
// 选择支付宝支付
order.setPaymentStrategy(new AlipayStrategy("zhangsan@alipay.com"));
order.checkout(199.9); // 输出:zhangsan@alipay.com使用支付宝支付 199.9元
// 切换为微信支付
order.setPaymentStrategy(new WechatPayStrategy("wx123456"));
order.checkout(99.5); // 输出:wx123456使用微信支付 99.5元
}
}优点:算法可独立于客户端变化,新增支付方式只需实现 PaymentStrategy,无需修改订单类(符合“开闭原则”)。
5. 工厂方法模式(Factory Method)
意图:定义一个创建对象的接口,但让子类决定实例化哪个类(比简单工厂更灵活,支持扩展)。
代码示例:文档编辑器
// 1. 产品接口:文档
interface Document {
void open();
}
// 2. 具体产品:文本文档
class TextDocument implements Document {
@Override
public void open() {
System.out.println("打开文本文档");
}
}
// 具体产品:表格文档
class SpreadsheetDocument implements Document {
@Override
public void open() {
System.out.println("打开表格文档");
}
}
// 3. 工厂接口:文档工厂
interface DocumentFactory {
Document createDocument(); // 工厂方法:创建文档
}
// 4. 具体工厂:文本工厂
class TextDocumentFactory implements DocumentFactory {
@Override
public Document createDocument() {
return new TextDocument();
}
}
// 具体工厂:表格工厂
class SpreadsheetDocumentFactory implements DocumentFactory {
@Override
public Document createDocument() {
return new SpreadsheetDocument();
}
}
// 测试
public class FactoryMethodDemo {
public static void main(String[] args) {
// 创建文本文档(通过文本工厂)
DocumentFactory textFactory = new TextDocumentFactory();
Document textDoc = textFactory.createDocument();
textDoc.open(); // 打开文本文档
// 创建表格文档(通过表格工厂)
DocumentFactory sheetFactory = new SpreadsheetDocumentFactory();
Document sheetDoc = sheetFactory.createDocument();
sheetDoc.open(); // 打开表格文档
}
}与简单工厂的区别:
- 简单工厂由一个工厂类创建所有产品,新增产品需修改工厂类;
- 工厂方法为每个产品对应一个工厂类,新增产品只需新增工厂类(符合“开闭原则”)。
四、综合实战:XML 配置 + 工厂模式实现灵活扩展
结合 XML 和设计模式,实现一个可通过 XML 配置动态切换日志输出方式的系统。
1. 定义日志接口与实现类
// 日志接口
interface Logger {
void log(String message);
}
// 控制台日志
class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("控制台日志:" + message);
}
}
// 文件日志
class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("文件日志:" + message); // 实际会写入文件
}
}2. 编写 XML 配置文件(指定日志类型)
<?xml version="1.0" encoding="UTF-8"?>
<config>
<loggerType>console</loggerType> <!-- 可改为 file 切换日志方式 -->
</config>3. 工厂类:通过 XML 配置创建日志对象
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class LoggerFactory {
public static Logger createLogger() throws DocumentException {
// 1. 解析 XML 配置
SAXReader reader = new SAXReader();
Document document = reader.read("src/config.xml");
Element root = document.getRootElement();
String loggerType = root.elementText("loggerType");
// 2. 根据配置创建对应日志对象
if ("console".equals(loggerType)) {
return new ConsoleLogger();
} else if ("file".equals(loggerType)) {
return new FileLogger();
} else {
throw new IllegalArgumentException("无效的日志类型");
}
}
}4. 测试:动态切换日志方式
import org.dom4j.DocumentException;
public class LoggerDemo {
public static void main(String[] args) throws DocumentException {
// 从工厂获取日志对象(由 XML 配置决定类型)
Logger logger = LoggerFactory.createLogger();
logger.log("系统启动成功");
// 若修改 config.xml 中 loggerType 为 file,输出变为:
// 文件日志:系统启动成功
}
}优势:无需修改代码,仅通过修改 XML 配置即可切换日志输出方式,体现了设计模式的“开闭原则”(对扩展开放,对修改关闭)。
五、总结与进阶路线
1. 核心收获
- XML:掌握基本语法和四种解析方式,重点理解 DOM4J + XPath 的高效查询;
- 设计模式:理解单例、工厂、观察者等模式的意图和应用场景,能通过模式解决实际问题。
2. 进阶建议
- XML:学习 XML Schema(XSD)验证 XML 格式,掌握 JAXB 实现 XML 与 Java 对象的自动转换;
- 设计模式:深入学习装饰器模式、代理模式(与 Spring AOP 相关)、迭代器模式(集合框架底层),阅读《设计模式:可复用面向对象软件的基础》;
- 实战:分析 Spring 框架源码中的设计模式(如 BeanFactory 是工厂模式,事件机制是观察者模式),加深理解。
通过 XML 解析获取配置信息,结合设计模式实现灵活扩展,是 Java 框架(如 Spring、MyBatis)的核心思想。掌握这些知识,能帮助你从“会写代码”提升到“写出高质量代码”。