JavaSE进阶笔记:13(XML相关、工厂模式、装饰模式)

156 阅读8分钟

一、XML

1.XML概述

  • 可扩展标记语言(eXtensible Markuo Language)的缩写,一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据;
  • 纯文本,默认使用UTF—8编码;可嵌套
  • XML内容存为文件,就是一个XML文件
  • 使用场景:
    • XML内容常常被当成消息进行网络传输;
    • 作为配置文件用于存储系统的消息;

2.XML的创建

  • 创建XML类型文件,文件后缀为.xml

  • 文档声明必须在第一行

    • <?xml version="1.0" encoding="UTF-8" ?>
      version:XML默认版本号,该属性必须存在
      encoding:本XML文件编码格式
      

2.1 XML标签(元素)规则

  • 标签由一对尖括号和合法标识符组成,必须存在一个根标签,有且只有一个

  • 标签需成对出现,开始结束<name></name>

  • 特殊标签单独出现,必须有结束标签<br/>

  • 标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来;

    • <Studnet id="1">
  • 标签需要正确的嵌套;

  • 定义注释信息:<!-注释内容-->

  • 可以存在以下特殊字符

    • &lt; < 小于
      &gt; > 大于
      &amp; &
      &apos; ‘ 单引号
      &quot;  "
      
  • 可以存在CDATA区:

    • <![CDATA[...内容...]]>
      
<?xml version="1.0" encoding="UTF-8" ?>
<Student>
    <name>张三</name>
    <sex></sex>
    <info>
        <addr>哪里</addr>
        <hobby>刑法</hobby>
    </info>
    <sql>
        <!--注释信息:&lt;小于  &gt;大于 &amp;&amp;为&& -->
        select name from student where age &lt; 30;
        select name from student where age &gt; 18;
        select name from student where age &lt; 30 &amp;&amp; age &gt; 18;

        <!-- 字符数据区:内部可以正常使用特殊字符 -->
        <![CDATA[

          select name from student where age < 30 && age > 18;
        ]]>
    </sql>
</Student>

3.XML文档约束

  • 文档约束:用来限定xml文件中的标签和属性要怎么写;
  • 强制约束程序员必须按照文档约束的规定来编写xml文件;

3.1约束方式一:DTD

  • DTD约束文档,后缀必须是.dtd;
  • 需要编写的xml文件中导入该DTD约束文档;
  • 按照约束的规定编写xml文件中内容;
  • 优点:
    • 可以约束书写格式;
  • 缺点:
    • 无法约束属性类型

3..1.1 data.dtd文件

<!ELEMENT书架 (书+)>
<!ELEMENT 书 (书名,作者,售价)>
<!ELEMENT 书名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>

3.1.2 helloword_dtd.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 书架 SYSTEM "data.dtd">
<书架>
    <书>
        <书名>java基础</书名>
        <作者>黑马</作者>
        <售价>暂无</售价>
    </书>
    <书>
        <书名>禅与摩托车维修艺术</书名>
        <作者>不知道</作者>
        <售价>暂无</售价>
    </书>
</书架>

3.2约束方式二:schema

  • 可以约束具体数据类型,约束能力更强,文档后缀必须是.xsd;
  • schema本身是xml文件,本身受其他约束文件要求,编写更加严格;

3.2.1 data.xsd

<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.itcast.cn"
        elementFormDefault="qualified">
    <!-- targetNamespace:申明约束文档的地址(命名空间) -->
    <element name="书架">
        <!--子元素-->
        <complexType>
            <!--maxOccurs="unbounded"书架下子元素有任意多个-->
            <sequence maxOccurs="unbounded">
                <element name="书">
                    <!--子元素,仅一个-->
                    <complexType>
                        <sequence>
                            <element name="书名" type="string"/>
                            <element name="作者" type="string"/>
                            <element name="售价" type="double"/>
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

3.2.2 helloword_schema.xml

<?xml version="1.0" encoding="UTF-8" ?>
<书架 xmlns="http://www.itcast.cn"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.itcast.cn data.xsd">
    <!-- xmlns="http://www.itcast.cn" 基本位置
    xsi:schemaLocation="http://www.itcast.cn data.xsd">  具体位置
    -->
    <>
        <书名>java</书名>
        <作者></作者>
        <售价>99.9</售价>
    </>
    <>
        <书名></书名>
        <作者></作者>
        <售价>999</售价>
    </>
</书架>

二、XML解析技术

1.解析概述

1.1两种解析方式

解析方式
SAX解析读一行,解析一行
DOM解析文件加载到内存,用树解析
以文档Document方式存储

1.2Dom常见解析工具

名称说明
JAXPSUN提供的xml解析API
JDOM开源项目,基于树形结构,纯java技术对XML文档实现解析、生成、序列化及各种操作
dom4j是JDOM升级品,读写xml文件,性能优异、功能强大、方便使用,性能超过官方的dom,源代码开放,框架用来读写配置文件**(重点)**
jsoup功能强大的DOM方式的xml解析开发包,对HTML解析更方便

1.3 DOM解析文档对象模型

  • Document对象:整个xml文档

  • 元素属性对象都继承Node接口

    • 以下都是节点类型
节点类型
Element对象标签
Attribute对象属性
Text文本内容

2.Dom4j解析xml文件

/**
 * Dom4j解析xml文件
 */
public class Dom4jHelloWorldDemo01 {
    @Test
    public void parseXMLDate() throws Exception {
        //1.创建一个Dom4j的解析器对象,代表整个dom4j对象
        SAXReader saxReader = new SAXReader();
        //2.xml文件加载到内存,成为一个Document文档对象
        //Document document1 = saxReader.read(new File("day13_xml_app\src\Contacts.xml"));//需要通过模块名定位,改变影响
        //Document document2 = saxReader.read(new FileInputStream(""));

        //注意:getResourceAsStream("");中的/是直接去src下找文件
        InputStream is = Dom4jHelloWorldDemo01.class.getResourceAsStream("/Contacts.xml");
        Document document = saxReader.read(is);

        //3.获取根元素对象
        Element root = document.getRootElement();
        System.out.println(root.getName());//contactList

        //4.根元素下所有子元素
        List<Element> sonEles = root.elements();
        //可以拿指定标签的所有子元素
        //List<Element> sonEles = root.elements("contact");
        for (Element sonEle : sonEles) {
            System.out.println(sonEle.getName());
        }
        

        // 5.根元素下某个子元素;有多个默认取第一个
        Element contact = root.element("contact");
        System.out.println(contact.getName());
        // 6.获取子元素文本
        System.out.println(contact.elementText("name"));
        // 7.文本去掉前后空格
        System.out.println(contact.elementTextTrim("name"));
        // 8.根据元素获取属性值
        Attribute idAttr = contact.attribute("id");
        System.out.println(idAttr.getName()+idAttr.getValue());
        //直接获取属性值
        System.out.println(contact.attributeValue("id"));
        // 9.获取当前元素下子元素对象
        Element email = contact.element("email");
        System.out.println(email.getText());
        System.out.println(email.getTextTrim());
    }
}

Contacts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<contactList>
    <contact id="1" vip="true">
        <name>曲云</name>
        <gender></gender>
        <email>123@qq.com</email>
    </contact>
    <contact id="2" vip="false">
        <name>公孙二</name>
        <gender></gender>
        <email>123456@qq.com</email>
    </contact>
    <user>
        <contact>
            <infor>
                <name id="8">天工万里鸽</name>
            </infor>
        </contact>
    </user>
    <name id="99">一级名</name>
</contactList>

3.案例:XML解析

/**
 * 案例:XML解析
 */
public class Dom4jDemo02 {
    @Test
    public void parseToList() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(Dom4jDemo02.class.getResourceAsStream("/Contacts.xml"));
        Element root = document.getRootElement();
        List<Element> contactEles = root.elements("contact");
        List<Contact> contacts = new ArrayList<>();
        for (Element contactEle : contactEles) {
            Integer id = Integer.valueOf(contactEle.attributeValue("id"));
            boolean vip = Boolean.parseBoolean(contactEle.attributeValue("vip"));
            String name = contactEle.elementText("name");
            char gender = contactEle.elementText("gender").charAt(0);
            String email = contactEle.elementText("email");

            Contact contact = new Contact(id, vip, name, gender, email);
            contacts.add(contact);
        }
        for (Contact contact : contacts) {
            System.out.println(contact);
        }
        /**
         * Contact{id=1, vip=true, name='曲云', gender=女, email='123@qq.com'}
         * Contact{id=2, vip=false, name='公孙二', gender=女, email='123456@qq.com'}
         */
    }
}

三、Xpath检索XML文件

  • XPath:使用路径表达式来定位XML文档中的元素节点或属性节点;
  • 示例:
    • /元素/子元素/孙元素
    • //子元素//孙元素

1.检索准备

  • 导入jar包(dom4j和jaxen-1.1.2.jar),Xpath技术依赖Dom4j技术;
  • 通过dom4j的SAXReader获取Document对象;
  • 利用XPath的API,选取XML文档元素节点进行解析操作;
  • Documnet与XPath相关API:
    • Node selectSingleNode("表达式") 获取符合表达式的唯一元素
    • List<Node> selectNodes("表达式") 获取符合表达式的元素集合

2.检索的四种方式

/**
 * 检索的四种方式
 */
public class XPathDemo {
    /**
     * 绝对路径:/根元素/子元素/子元素
     */
    @Test
    public void parse01() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts.xml"));
        //检索全部内容
        List<Node> nameNodes = document.selectNodes("/contactList/contact/name");
        for (Node node : nameNodes) {
            Element nameFile = (Element) node;
            System.out.println(nameFile.getTextTrim());
        }
    }

    /**
     * 相对路径:./子元素/子元素(.代表了当前元素)
     */
    @Test
    public void parse02() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts.xml"));
        //root代表contactList
        Element root = document.getRootElement();
        List<Node> nameNodes = root.selectNodes("./contact/name");
        for (Node node : nameNodes) {
            Element nameFile = (Element) node;
            System.out.println(nameFile.getTextTrim());
        }
    }

    /**
     * 全文搜索:
     *  //元素, 全文中检索
     *  //元素1/元素2 全文找元素1下面的一级元素2
     *  //元素1//元素2 全文找元素1下面的全部元素2
     *
     */
    @Test
    public void parse03() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts.xml"));
        //检索全部内容
        //List<Node> nameNodes = document.selectNodes("//name");//文件下所有name
        //List<Node> nameNodes = document.selectNodes("//contact/name");//contact下直属的一级name
        List<Node> nameNodes = document.selectNodes("//contact//name");//contact下所有name
        for (Node node : nameNodes) {
            Element nameFile = (Element) node;
            System.out.println(nameFile.getTextTrim());
        }
    }

    /**
     * 属性查找:
     *  @属性名 全文检索属性对象
     *  //元素[@属性名] 全文检索包含该属性的元素对象
     *  //元素[@属性名=值] 全文检索包含该属性的元素对象
     *
     */
    @Test
    public void parse04() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts.xml"));

        //查询id属性
        List<Node> nameNodes = document.selectNodes("//@id");
        for (Node node : nameNodes) {
            Attribute attr = (Attribute) node;
            System.out.println(attr.getName()+attr.getValue());
        }

        //查询name元素(含有id属性)
        Node node = document.selectSingleNode("//name[@id]");
        Element element = (Element) node;
        System.out.println(element.getTextTrim());//天工万里鸽

        //查询name元素(含有id属性且有值)
        Node node1 = document.selectSingleNode("//name[@id=99]");
        Element element1 = (Element) node1;
        System.out.println(element1.getTextTrim());//一级名
    }
}

四、设计模式:工厂模式

  • 工厂模式(Factory Pattern):是java最常用的设计模式之一,属于创建型模式,提供了一种获取对象的方式;
  • 作用:工厂方法可以封装对象创建细节,为该对象进行加工和数据注入;
  • 实现类与类之间的解耦操作(核心思想)
    • 降低类间关联
    • 修改创建只需要在FactoryPatten即可
/**
 * 由工厂通过选择创建对象
 */
public class FactoryPatten {
    /**
     * 定义创建方法,返回创建对象
     */
    public static Computer createComputer(String info){
        switch (info){
            case "Mi":
                Computer mi = new Mi();
                mi.setName("Mi U11");
                mi.setPrice(2999.0);
                return mi;
            case "HW":
                Computer hw = new HW();
                hw.setName("HW 5");
                hw.setPrice(3999.9);
                return hw;
            default:
                return null;
        }
    }
}

/**
 * 示例
 */
public class FactoryDemo {
    public static void main(String[] args) {
        Computer mi = FactoryPatten.createComputer("Mi");
        mi.start();//Mi开机了

        Computer hw = FactoryPatten.createComputer("HW");
        hw.start();
    }
}

/**
 * 定义电脑抽象类
 */
public abstract class Computer {
    private String name;
    private double price;

    public abstract void start();

    public Computer() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

/**
 * 定义实现类
 */
public class Mi extends Computer{
    @Override
    public void start() {
        System.out.println("Mi开机了");
    }
}

五、设计模式:装饰模式

  • 装饰模式:创建新类,包装原始类,在新类中提升类的功能;
  • 作用:不改变原类,动态扩展类的功能;
  • 步骤:
    • 定义父类
    • 定义原始类,继承父类,定义功能
    • 定义装饰类,继承父类,包装原始类,增强功能
/**
 * 装饰模式
 */
public class DecoratorPattern {
    public static void main(String[] args) {
        InputStream is = new BufferedInputStream(new FileInputStream());
        System.out.println(is.read());
        System.out.println(is.read(new byte[3]));
        /**
         * 创建8KB缓冲区,提高读取效率
         * FileInputStream低性能读取一个字节
         * 97
         * 创建8KB缓冲区,提高读取效率
         * FileInputStream低性能读取一个字节数组:[96, 97, 98]
         * 3
         */
    }
}

/**
 * 定义父类
 */
public abstract class InputStream {
    public abstract int read();
    public abstract int read(byte[] buffer);
}

/**
 * 定义原始类,继承父类,定义功能
 */
public class FileInputStream extends InputStream{
    @Override
    public int read() {
        System.out.println("FileInputStream低性能读取一个字节");
        return 97;
    }

    @Override
    public int read(byte[] buffer) {
        for (int i = 0; i < 3; i++) {
            buffer[i] = (byte) (96+i);
        }
        System.out.println("FileInputStream低性能读取一个字节数组:"+ Arrays.toString(buffer));
        return buffer.length;
    }
}


/**
 * 定义装饰类,继承父类,包装原始类,增强功能
 */
public class BufferedInputStream extends InputStream{
    private InputStream is;
    public BufferedInputStream(InputStream is){
        this.is = is;
    }

    @Override
    public int read() {
        System.out.println("创建8KB缓冲区,提高读取效率");
        return is.read();
    }

    @Override
    public int read(byte[] buffer) {
        System.out.println("创建8KB缓冲区,提高读取效率");
        return is.read(new byte[3]);
    }
}

此处略抽象,后续在设计模式的笔记中再详细展示。

RecordDate:2021/08/23