0%

StAX 解析:基于拉模式的 XML 解析方式

StAX(Streaming API for XML)是一种介于 DOM 和 SAX 之间的 XML 解析标准,它结合了 DOM 的灵活性和 SAX 的高效性,采用 “拉模式”(Pull Model)让应用程序主动控制解析过程,相比 SAX 的 “推模式”(Push Model)更易于使用。

StAX 解析的核心原理

StAX 解析的核心思想是 “应用程序主导解析流程”,其工作方式如下:

  1. 解析器初始化:创建 StAX 解析器(如XMLStreamReader),关联 XML 文档。
  2. 主动拉取事件:应用程序通过调用next()等方法主动获取下一个解析事件(如元素开始、文本内容、元素结束等)。
  3. 事件处理:根据拉取到的事件类型(如START_ELEMENTCHARACTERSEND_ELEMENT),应用程序自行决定如何处理(如提取数据、跳过节点)。
  4. 按需终止:解析过程完全由应用程序控制,可随时停止解析(如找到目标数据后)。

这种 “拉模式” 的优势在于:应用程序无需预先定义事件处理器,而是在需要时主动获取事件,代码逻辑更直观,尤其适合处理复杂 XML 结构。

StAX 的核心组件与事件类型

核心接口

StAX 提供了两种主要的解析接口,分别适用于不同场景:

  • XMLStreamReader:只读接口,用于从 XML 文档中拉取事件,是 StAX 的核心接口。
  • XMLEventReader:基于事件对象的只读接口,将解析事件封装为XMLEvent对象,提供更面向对象的操作方式。

其中,XMLStreamReader因轻量高效而更常用,以下主要围绕该接口展开。

常用方法(XMLStreamReader

方法 作用
int next() 推进到下一个事件,返回事件类型(如START_ELEMENT)。
String getLocalName() 获取当前元素的本地名称(不带命名空间前缀)。
String getText() 获取当前文本节点的内容。
int getAttributeCount() 获取当前元素的属性数量。
String getAttributeLocalName(int index) 获取指定索引的属性名称。
String getAttributeValue(int index) 获取指定索引的属性值。
String getAttributeValue(String namespaceURI, String localName) 根据命名空间和名称获取属性值。
boolean hasNext() 判断是否还有下一个事件。

常见事件类型(XMLStreamConstants

XMLStreamReadernext()方法返回的事件类型定义在XMLStreamConstants中,主要包括:

阅读全文 »

SAX 解析:基于事件驱动的 XML 解析方式

SAX(Simple API for XML)是一种基于事件驱动的 XML 解析标准,与 DOM 解析的树形结构不同,SAX 无需将整个 XML 文档加载到内存,而是通过逐行扫描文档并触发事件的方式处理数据。这种特性使其在处理大型 XML 文档时具有显著的内存优势,成为 DOM 解析的重要补充。

SAX 解析的核心原理

SAX 解析的核心思想是 “流式处理 + 事件驱动”,其工作流程如下:

  1. 逐行扫描:SAX 解析器按顺序读取 XML 文档,无需一次性加载整个文档到内存。
  2. 事件触发:当解析到特定结构(如元素开始、文本内容、元素结束等)时,解析器触发对应的事件(如startElementcharactersendElement)。
  3. 事件处理:开发者通过自定义事件处理器(继承DefaultHandler),重写事件方法以响应特定事件,提取所需数据。
  4. 按需停止:解析过程中可随时终止(如找到目标数据后),无需解析剩余内容,节省资源。

SAX 解析的核心组件

SAX 解析主要依赖以下组件完成解析流程:

组件 作用
SAXParserFactory SAX 解析器的工厂类,用于创建SAXParser实例
SAXParser SAX 解析器的核心接口,负责读取 XML 并触发事件
DefaultHandler 事件处理器的基类,实现了 SAX 的四大事件监听接口,提供空方法实现
事件监听接口 包括ContentHandlerDTDHandlerEntityResolverErrorHandler,定义了各类事件的回调方法

四大事件监听接口

SAX 通过四大接口定义不同类型的事件,DefaultHandler实现了这些接口并提供空方法,开发者可按需重写:

(1)ContentHandler(核心接口,处理文档内容)

定义与 XML 元素、文本相关的事件,是最常用的接口:

  • startDocument():文档解析开始时触发。
  • endDocument():文档解析结束时触发。
  • startElement(String uri, String localName, String qName, Attributes atts):元素开始时触发(如<book>)。
    • uri:命名空间 URI(无则为空)。
    • localName:不带前缀的元素名。
    • qName:带前缀的元素名(如ns:book)。
    • atts:元素的属性集合。
  • endElement(String uri, String localName, String qName):元素结束时触发(如</book>)。
  • characters(char[] ch, int start, int length):解析到文本内容时触发,ch为字符数组,startlength指定文本范围。
  • startPrefixMapping(String prefix, String uri):命名空间前缀映射开始时触发。
  • endPrefixMapping(String prefix):命名空间前缀映射结束时触发。
(2)DTDHandler(处理 DTD 相关事件)
  • notationDecl(String name, String publicId, String systemId):解析 DTD 符号声明时触发。
  • unparsedEntityDecl(String name, String publicId, String systemId, String notationName):解析未解析实体时触发。
(3)EntityResolver(处理实体解析)
  • resolveEntity(String publicId, String systemId):解析外部实体(如 DTD 文件)时触发,可自定义实体加载方式(如本地缓存)。
(4)ErrorHandler(处理解析错误)
  • warning(SAXParseException e):非致命错误(如语法警告)。
  • error(SAXParseException e):可恢复的错误(如未闭合标签)。
  • fatalError(SAXParseException e):致命错误(如文档格式错误),会终止解析。
阅读全文 »

DOM 解析:XML 文档的树形结构解析方式

DOM(Document Object Model,文档对象模型)是一种基于树形结构的 XML 解析标准,它将整个 XML 文档加载到内存中并构建一个完整的节点树,允许开发者通过树结构随机访问、修改和操作 XML 中的任何节点。这种解析方式直观且功能强大,是 XML 处理的重要手段之一。

DOM 解析的核心原理

DOM 解析的核心思想是将 XML 文档映射为一棵节点树,树中的每个节点对应 XML 文档的一个组成部分(如元素、属性、文本等)。开发者可以通过操作这棵树来访问和修改 XML 内容,具体流程如下:

  1. 加载文档:DOM 解析器读取整个 XML 文档,将其加载到内存中。
  2. 构建树结构:根据 XML 的层级关系,在内存中构建一棵 DOM 树,根节点为整个文档,各级子节点对应 XML 的元素、属性等。
  3. 操作节点:通过 DOM 提供的 API 遍历、查询、添加、修改或删除树中的节点。
  4. 持久化:(可选)将修改后的 DOM 树重新写入文件或输出流。

DOM 核心接口与类

Java 中 DOM 解析主要通过javax.xml.parsers包中的接口实现,核心组件包括:

接口 / 类 作用
DocumentBuilderFactory DOM 解析器的工厂类,用于创建DocumentBuilder
DocumentBuilder DOM 解析器的核心接口,负责解析 XML 并生成Document对象
Document 代表整个 XML 文档,是 DOM 树的根节点,提供创建和查询节点的方法
Node 所有 DOM 节点的父接口,定义节点的通用方法(如appendChildgetNodeValue
NodeList 节点集合,用于存储多个节点(如元素的子节点)
Element 元素节点的接口,继承自Node,提供属性操作等方法
Attr 属性节点的接口,代表元素的属性

关键接口方法解析

1. Document接口(核心方法)
  • getElementsByTagName(String tagname):根据标签名获取所有匹配的元素节点集合(NodeList)。
  • createElement(String tagName):创建指定名称的元素节点。
  • createTextNode(String data):创建文本节点。
  • createAttribute(String name):创建属性节点。
2. Node接口(核心方法)
  • appendChild(Node newChild):向当前节点添加子节点。
  • getChildNodes():获取当前节点的所有子节点(NodeList)。
  • getAttributes():获取当前节点的所有属性(NamedNodeMap)。
  • getNodeName():获取节点名称(如元素名、属性名)。
  • getNodeValue():获取节点值(如文本内容、属性值)。
  • hasChildNodes():判断当前节点是否有子节点。
3. NodeList接口(核心方法)
  • getLength():获取节点集合的长度。
  • item(int index):根据索引获取指定节点(索引从 0 开始)。
阅读全文 »

Lock接口及其实现类:Java 并发编程的灵活锁机制

Lock 接口是 JDK 5 引入的同步工具,为 Java 并发编程提供了比 synchronized 更灵活的锁控制能力。它支持显式加锁 / 解锁、可中断获取、超时获取等特性,弥补了 synchronized 隐式操作的局限性。本文将深入解析 Lock 接口的核心实现类(ReentrantLockReentrantReadWriteLock),并对比其与 synchronized 的差异。

Lock 接口核心方法

Lock 接口定义了锁的基本操作,核心方法如下:

方法 作用描述
lock() 获取锁(阻塞式,直到获取成功)
lockInterruptibly() 可中断地获取锁(获取过程中若线程被中断,会抛出 InterruptedException
tryLock() 尝试非阻塞获取锁(成功返回 true,失败返回 false
tryLock(long timeout, TimeUnit unit) 超时获取锁(超时未获取则返回 false
unlock() 释放锁(必须在 finally 块中调用,避免锁泄漏)
newCondition() 创建条件对象,实现线程间通信(类似 synchronizedwait/notify

ReentrantLock:可重入独占锁

ReentrantLockLock 接口的最常用实现,提供与 synchronized 类似的独占锁功能,但支持更灵活的控制。其核心特性是可重入性(同一线程可多次获取锁)和公平 / 非公平模式

公平锁与非公平锁

ReentrantLock 允许通过构造函数指定锁的公平性:

1
2
3
4
// 非公平锁(默认)  
Lock lock = new ReentrantLock();
// 公平锁
Lock fairLock = new ReentrantLock(true);
(1)非公平锁(NonfairSync)
  • 获取逻辑:线程尝试直接获取锁(CAS 操作),失败后才进入等待队列;
  • 特点:可能存在线程饥饿(先请求的线程后获取锁),但吞吐量更高;
  • 适用场景:并发激烈但允许偶尔不公平的场景。
(2)公平锁(FairSync)
  • 获取逻辑:线程必须按请求顺序进入队列,只有队首线程能获取锁;
  • 特点:保证公平性,避免饥饿,但频繁的上下文切换导致吞吐量较低;
  • 适用场景:对公平性要求高的场景(如资源调度)。

核心源码解析

ReentrantLock 基于 AQS(AbstractQueuedSynchronizer)实现,内部通过 Sync 抽象类区分公平与非公平逻辑:

获取锁(lock() 方法)
阅读全文 »

Mac 强制退出卡住的应用:快捷方式与替代方法

当 Mac 上的应用程序无响应(卡住、闪退或无法操作)时,无需重启电脑,只需强制退出该应用即可恢复系统流畅性。最便捷的方式是使用快捷键 Option + Command + Esc,以下是详细操作及替代方法:

快捷键强制退出(推荐)

  1. 按下组合键:同时按住 Option(⌥) + Command(⌘) + Esc,弹出 “强制退出应用程序” 窗口。
  2. 选择目标应用:在列表中找到无响应的应用(通常标记为 “未响应”)。
  3. 强制退出:点击窗口右下角的 “强制退出” 按钮,应用将被立即关闭。

优势:操作最快,适合紧急情况(如应用卡死导致鼠标无法移动时)。

通过 Dock 栏强制退出

若应用在 Dock 栏有图标,可右键操作:

  1. 右键点击 Dock 栏中的应用图标(如卡住的浏览器)。
  2. 按住 Option 键:此时菜单中的 “退出” 会变为 “强制退出”
  3. 点击 “强制退出”:应用将被关闭。
阅读全文 »