Java进阶(十五)XML、XML解析、设计模式

发布于:2022-11-05 ⋅ 阅读:(382) ⋅ 点赞:(0)

十五、XML、XML解析、设计模式

需要学会什么?

  • XML:在有些业务场景下,存储数据或者传输数据给别人的时候,数据需要满足一定的规范进行组织。
  • XML解析技术:XML文件中存储的数据是需要提取出来的。
  • XPath:如何方便的在XML文件中进行数据的检索?
  • 设计模式:开发中还有一些比较常见的设计模式是需要掌握,理解设计模式有利于理解某些程序。

1.XML

a.XML概述

XML概述:

  • XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是一种数据表示格式,可以藐视非常复杂的数据结构,常用于传输和存储数据。

    <?xml version="1.0" encoding="UTF-8"?>
    <note>
        <to>Tove</to>
        <from>Jani</from>
        <heading>Reminder</heading>
        <body>Don't forget me this weekend!</body>
    </note>
    

XML的几个特点和使用场景:

  • 纯文本,默认使用UTF-8编码。
  • 可嵌套。
  • 如果把XML内容存为文件,那么它就是一个XML文件。
  • XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息

总结:

  1. XML是什么?
    • XML全称为(eXtensible Markup Language),是一种可扩展的标记语言。
    • XML是一种数据表示格式,可以用于自定义数据格式。
  2. XML的作用是什么?
    • 用于进行存储数据和传输数据。
    • 作为软件的配置文件。

b.XML的创建、语法规则

XML的创建:

  • 创建一个XML类型的文件,要求文件的后缀必须使用XML,如:hello_world.xml。

XML的语法规则:

  • XML文件的后缀名为:xml

  • 文档声明必须是第一行。

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

XML的标签(元素)规则:

  • 标签由一对尖括号和合法标识符组成:<name></name>必须存一个根标签,有且只能有一个。

  • 标签必须成对出现,又开始和结束:

  • 特殊的标签可以不成对,但是必须有结束标记,如:<br/>

  • 标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来<student id="1"></student>

  • 标签需要正确的嵌套。

    <?xml version="1.0" encoding="UTF-8" ?>
    <student>
        <name>花千骨</name>
        <sex></sex>
        <hobby>修仙</hobby>
        <info>
            <address>花莲村</address>
            <age>16</age>
        </info>
    </student>
    
    

XML的其他组成:

  • XML文件中可以定义注释信息:

    <!--注释-->
    
  • XML文件中可以存在以下特殊字符:

    &lt; < 小于
    &gt; > 大于
    &amp; & 和号
    &apos; ' 单引号
    &quot; " 双引号
    
  • XML文件中可以存在CDATA区:

        <![CDATA[
        select * from user where age < 18;    
        ]]>
    
<?xml version="1.0" encoding="UTF-8" ?>
<student>
    <name>花千骨</name>
    <sex></sex>
    <hobby>修仙</hobby>
    <info>
        <address>花莲村</address>
        <age>16</age>
    </info>
    <!--注释-->
    <![CDATA[
    select * from user where age < 18;    
    ]]>
    
    <sql>
        select * from user where age &lt; 18;
    </sql>
</student>

c.XML文档约束方法一:DTD约束

什么是文档约束?

  • 用来限定XML文件中的标签以及属性应该怎么写。

文档约束的分类:

  • DTD
  • schema

案例:XML文档约束-DTD的使用。

需求:

  • 利用DTD文档约束,约束一个XML文件的编写。

分析:

  1. 编写DTD约束文档,后缀必须是.dtd。

    <!ELEMENT 书架 (书+)>
    <!ELEMENT 书 (书名,作者, 售价)>
    <!ELEMENT 书名 (#PCDATA)>
    <!ELEMENT 作者 (#PCDATA)>
    <!ELEMENT 售价 (#PCDATA)>
    
  2. 在需要编写的XML文件中导入该DTD约束文档。

  3. 按照约束的规定编写XML文件的内容。

data.dtd

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

hello_world_dtd.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 书架 SYSTEM "data.dtd">
<书架>
    <>
        <书名>一往无前</书名>
        <作者>范海涛</作者>
        <售价>78</售价>
    </>
</书架>

总结:

  1. XML的文档约束-DTD的作用和问题?
    • 可以约束XML文件的编写。
    • 不能约束具体的数据类型。

d.XML文档约束方式二:schema约束

文档约束-schema:

  • schema可以约束具体的数据类型,约束能力上更强大。
  • schema本身也是一个XML文件,本身也收到其他约束文件的要求,所以编写的更加严谨。
约束
约束
其他文件
schema
XML

案例:XML文档约束-schema的使用。

需求:

  • 利用schema文档约束,约束一个XML文件的编写。

分析:

  1. 编写schema约束文档,后缀必须是.xsd,具体的形式到代码中观看。
  2. 在需要编写的XML文件中导入该scheam约束文档。
  3. 按照约束内容编写XML文件的标签。

data.xsd

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

hello_world_schema.xml

<?xml version="1.0" encoding="UTF-8" ?>
<书架 xmlns="http://www.javase.cn">
    <>
        <书名>一往无前</书名>
        <作者>范海涛</作者>
        <售价>78</售价>
    </>
</书架>

总结:

  1. XML的文档约束-schema的优点?
    • 可以约束XML文件的标签内容格式,以及具体的数据类型。

2.XML解析技术

a.XML解析技术概述

XML的数据的作用是什么?最终需要怎么处理?

  • 存储数据、做配置i信息、进行数据传输。
  • 最终需要被程序进行读取,解析里面的信息。

什么是XML解析?

  • 使用程序读取XML中的数据。
  • 两种解析方式:
    • SAX解析。
    • DOM解析。

DOM常见的解析工具:

名称 说明
JAXP SUM公司提供的一套XML的解析API。
JDOM JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。
dom4j 是JDOM的升级品,用来读写XML文件的。具有性能优异、功能强大和极易使用的特点,它得到性能超过SUN公司官方的DOM技术,同时它也是一个开源代码的软件,Hibernate也用它来读写配置文件。
jsoup 功能强大DOM方式的XML解析开发包,尤其对HTML解析更加方便。

DOM解析文档对象模型:

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <!--第一个学生-->
    <student id="1">
        <name>花千骨</name>
        <age>16</age>
    </student>
    
    <!--第二个学生-->
    <student id="2">
        <name>轻水</name>
        <age>17</age>
    </student>
    
</students>

1
2
16
17
Document
students
student
student
id
name
age
花千骨
id
name
age
轻水
  • Document对象:整个XML文档。
  • Node对象:
    • Element对象:标签。
    • Attribute对象:属性。
    • Text对象:文本内容。

b.dom4j解析XML文件

dom4j解析XML得到Document对象:

SAXReader类:

构造器/方法 说明
public SAXReader() 创建Dom4J的解析器对象。
Document read(String url) 加载XML文档成为Document对象。

Document类:

方法名 说明
Element getRootElement() 获取根元素对象。

使用dom4j解析出XML文件。

需求:

  • 使用dom4j把一个XML文件的数据进行解析。

分析:

  1. 下载dom4j框架,https://dom4j.github.io/
  2. 在项目中创建一个文件夹:lib
  3. 将dom4j-x.x.x.jar文件复制到lib文件夹。
  4. 在IDEA中在jar文件上右键,选择Add as LIbrary,点击OK。
  5. 在类中导包使用。

students.xml

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <!--第一个学生-->
    <student id="1">
        <name>花千骨</name>
        <age>16</age>
    </student>

    <!--第二个学生-->
    <student id="2">
        <name>轻水</name>
        <age>17</age>
    </student>

</students>

Dom4jDemo.java

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;

import java.io.InputStream;

/**
 * 目标: 学会使用dom4j解析XML文件中的数据.
 * 1. 导入dom4j框架.
 * 2.准备一个XML文件
 */
public class Dom4jDemo {
    @Test
    public void parseXMLData() throws Exception {

        // 1.创建一个dom4j的解析器对象 代表了整个dom4j框架
        SAXReader saxReader = new SAXReader();

        // 2.把XML文件加载到内存中成为一个Document文档对象
        // getResourceAsStream中 / 是直接去src下寻找文件
        InputStream inputStream = Dom4jDemo.class.getResourceAsStream("/students.xml");
        Document document = saxReader.read(inputStream);

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

Dom4j解析XML的元素、属性、文本:

方法名 说明
List elements() 得到当前元素下所有子元素。
List elements(String name) 得到当前元素下制定名字的子元素返回集合。
Element element(String name) 得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个。
String getName() 得到元素名字。
String attributeValue(String name) 通过属性名直接得到属性值。
String elementText(子元素名) 得到指定名称的子元素的文本。
String getText() 得到文本。

案例:XML解析案例。

需求:

  • 利用dom4j的知识,将students.xml文件中的联系人数据封装成List集合,其中每个元素都是实体类Student,打印输出List中每个元素。

students.xml

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <!--第一个学生-->
    <student id="1">
        <name>花千骨</name>
        <age>16</age>
        <sex></sex>
        <email>hqg@changliu.com</email>
    </student>

    <!--第二个学生-->
    <student id="2">
        <name>轻水</name>
        <age>17</age>
        <sex></sex>
        <email>qs@changliu.com</email>
    </student>

    <!--第三个学生-->
    <student id="3">
        <name>孟玄朗</name>
        <age>20</age>
        <sex></sex>
        <email>mxl@changliu.com</email>
    </student>

</students>

Student.java

public class Student {
    private int id;
    private String name;
    private int age;
    private char sex;
    private String email;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Student() {
    }

    public Student(int id, String name, int age, char sex, String email) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", email='" + email + '\'' +
                '}';
    }
}

Dom4jDemo.java

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * 需求: 解析XML中的数据成为一个List集合对象
 */
public class Dom4jDemo {
    @Test
    public void parseToList() throws Exception {

        // 1.创建一个dom4j的解析器对象 代表了整个dom4j框架
        SAXReader saxReader = new SAXReader();

        // 2.把XML文件加载到内存中成为一个Document文档对象
        Document document = saxReader.read(Dom4jDemo.class.getResourceAsStream("/students.xml"));

        // 3.获取根元素
        Element root = document.getRootElement();

        // 4.获取students子元素
        List<Element> elements = root.elements("student");

        // 5.准备一个ArrayList集合封装联系人信息
        List<Student> studentArrayList = new ArrayList<>();

        // 6.遍历students子元素
        for (Element element : elements) {

            // 创建Student对象
            Student student = new Student();
            student.setId(Integer.parseInt(element.attributeValue("id")));
            student.setName(element.elementTextTrim("name"));
            student.setSex(element.elementTextTrim("sex").charAt(0));
            student.setEmail(element.elementText("email"));

            // Student对象加入到List集合中
            studentArrayList.add(student);
        }

        // 7.遍历List集合
        for (Student student : studentArrayList) {
            System.out.println(student);
        }
    }
}

执行结果:

Student{id=1, name='花千骨', age=0, sex=女, email='hqg@changliu.com'}
Student{id=2, name='轻水', age=0, sex=女, email='qs@changliu.com'}
Student{id=3, name='孟玄朗', age=0, sex=男, email='mxl@changliu.com'}

3.XML检索技术:XPath

如果需要从XML中检索需要的某个信息怎么解决?

  • dom4j需要进行文件的全部解析,然后再寻找数据。
  • XPath技术更加适合做信息检索。

XPath介绍:

  • XPath在解析XML文档方面提供了独树一帜的路径思想,更加优雅、高效。
  • XPath使用路径表达式来定位XML文档中的元素节点或属性节点。

XPath的四大检索方案:

  • 绝对路径:采用绝对路径获取从根节点开始逐层的查找节点列表并打印信息。

    方法名 说明
    /根元素/子元素/孙元素 从根元素开始,一级一级向下查找,不能跨级。
  • 相对路径:先得到students根节点,然后向下一级student节点的name子节点并打印信息。

    方法名 说明
    ./子元素/孙元素 从当前元素开始,一级一级向下查找,不能跨级。
  • 全文检索:直接全文搜索所有的name元素并打印。

    方法名 说明
    //元素 在全文找这个元素。
    //元素1/元素2 在全文找元素1下面的一级元素2
    //元素1//元素2 在全文找元素1下面的全部元素2
  • 属性查找:在全文中搜索属性,或者带属性的元素。

    方法名 说明
    //@属性名 查找属性对象,无论是哪个元素,只要有这个属性即可。
    //元素[@属性名] 查找元素对象,全文搜索指定元素名和属性名。
    //元素[@属性名=值] 查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。

案例:使用XPath检索出XML文件。

需求:

  • 使用dom4j把一个XML文件的数据进行解析。

分析:

  1. 下载jaxen-x.x.x.jar https://search.maven.org/artifact/jaxen/jaxen/1.2.0/bundle

  2. 导入jar包(dom4j和jaxen),XPath技术依赖dom4j技术。

  3. 通过dom4j的SAXReader获取Document对象。

  4. 利用XPath提供的API,结合XPath的语法完成选取XML文档元素节点进行解析操作。

  5. Document中与XPath相关的API如下:

    方法名 说明
    Node selectSingleNode(“表达式”) 获取符合表达式的唯一元素。
    List selectNodes(“表达式”) 获取符合表达式的元素集合。

students.xml

<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <!--第一个学生-->
    <student id="1">
        <name id="111">花千骨</name>
        <age>16</age>
        <sex></sex>
        <email>hqg@changliu.com</email>
    </student>

    <!--第二个学生-->
    <student id="2">
        <name>轻水</name>
        <age>17</age>
        <sex></sex>
        <email>qs@changliu.com</email>
    </student>

    <!--第三个学生-->
    <student id="3">
        <name>孟玄朗</name>
        <age>20</age>
        <sex></sex>
        <email>mxl@changliu.com</email>
    </student>

</students>

XPathDemoTest.java

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;

import java.util.List;


public class XPathDemoTest {
    /**
     * 1.绝对路径: /根元素/子元素/子元素.
     */
    @Test
    public void parse1() throws Exception {
        // a.创建解析器对象
        SAXReader saxReader = new SAXReader();

        // b.把XML加载成Document文档对象
        Document document = saxReader.read(XPathDemoTest.class.getResourceAsStream("/students.xml"));

        // c.检索全部姓名
        List<Node> nameNodes = document.selectNodes("/students/student/name");

        for (Node nameNode : nameNodes) {
            Element element = (Element) nameNode;
            // 花千骨
            // 轻水
            // 孟玄朗
            System.out.println(element.getTextTrim());
        }
    }

    /**
     * 2.相对路径: ./子元素/子元素
     * '.' 代表当前元素
     */
    @Test
    public void parse2() throws Exception {
        // a.创建解析器对象
        SAXReader saxReader = new SAXReader();

        // b.把XML加载成Document文档对象
        Document document = saxReader.read(XPathDemoTest.class.getResourceAsStream("/students.xml"));

        // c.获取根元素 students
        Element root = document.getRootElement();

        // d.检索全部姓名
        List<Node> nameNodes = root.selectNodes("./student/name");

        for (Node nameNode : nameNodes) {
            Element element = (Element) nameNode;
            // 花千骨
            // 轻水
            // 孟玄朗
            System.out.println(element.getTextTrim());
        }
    }

    /**
     * 3.全文检索:
     * //元素 在全文找这个元素
     * //元素1/元素2 在全文找元素1下面的一级元素2
     * //元素1//元素2 在全文找元素1下面的全部元素2
     */
    @Test
    public void parse3() throws Exception {
        // a.创建解析器对象
        SAXReader saxReader = new SAXReader();

        // b.把XML加载成Document文档对象
        Document document = saxReader.read(XPathDemoTest.class.getResourceAsStream("/students.xml"));

        // c.检索全部姓名
        // List<Node> nameNodes = document.selectNodes("//name");
        // List<Node> nameNodes = document.selectNodes("//students/name");
        List<Node> nameNodes = document.selectNodes("//students//name");

        for (Node nameNode : nameNodes) {
            Element element = (Element) nameNode;
            // 花千骨
            // 轻水
            // 孟玄朗
            System.out.println(element.getTextTrim());
        }
    }

    /**
     * 4.属性查找:
     * //@属性名称 在全文检索属性对象
     * //元素[@属性名称] 在全文检索包含该属性的元素对象
     * //元素[@属性名称=值] 在全文检索包含该属性的元素且属性值为该值的元素对象
     */
    @Test
    public void parse4() throws Exception {
        // a.创建解析器对象
        SAXReader saxReader = new SAXReader();

        // b.把XML加载成Document文档对象
        Document document = saxReader.read(XPathDemoTest.class.getResourceAsStream("/students.xml"));

        // c.检索包含id属性
        List<Node> nameNodes = document.selectNodes("//@id");

        for (Node nameNode : nameNodes) {
            Attribute attribute = (Attribute) nameNode;
            // id==>1
            // id==>111
            // id==>2
            // id==>3
            System.out.println(attribute.getName() + "==>" + attribute.getValue());
        }

        // 查找包含id属性的name元素
        // Node nameNode = document.selectSingleNode("//name[@id]");
        Node nameNode = document.selectSingleNode("//name[@id=111]");
        Element nameElement = (Element) nameNode;
        // 花千骨
        System.out.println(nameElement.getTextTrim());


    }

}

4.设计模式:工厂模式

什么是工厂设计模式?

  • 之前我们创建类对象时,都是使用new对象的形式创建,在很多业务场景下也提供了不直接new的方式。
  • 工厂模式(Factory Pattern)是Java中最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。

工厂设计模式的作用:

  • 工厂的方法可以封装对象的创建细节,比如:为该对象进行加工和数据注入。
  • 可以实现类与类之间的解耦操作(核心思想)。

Computer.java

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

    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 Computer() {
    }

    public Computer(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public abstract void start();
}

Mac.java

/**
 * Mac电脑类
 */
public class Mac extends Computer{

    /**
     * 开机
     */
    @Override
    public void start() {
        System.out.println(getName() + "开机,展示苹果Logo!");
    }
}

Dell.java

/**
 * Dell电脑
 */
public class Dell extends Computer{
    /**
     * 开机
     */
    @Override
    public void start() {
        System.out.println(getName() + "开机,并展示戴尔Logo!");
    }
}

FactoryPattern.java

package com.javase.factorydemo;

/**
 * 工厂模式
 */
public class FactoryPattern {
    /**
     * 创建对象返回
     */
    public static Computer createComputer(String info) {
        switch (info) {
            case "Mac":
                Computer mac = new Mac();
                mac.setName("Macbook Pro M1 Pro");
                mac.setPrice(14899);
                return mac;
            case "Dell":
                Computer dell = new Dell();
                dell.setName("Dell 灵越 16 Plus");
                dell.setPrice(16298.98);
                return dell;
            default:
                return null;
        }
    }
}

总结:

  1. 工厂设计模式的作用:

    • 对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入。
    • 可以实现类与类的解耦操作(核心思想)。

5.设计模式:装饰模式

什么是装饰设计模式?

  • 创建一个新类,包装原始类,从而在新类中提升原来类的功能。

装饰设计模式的作用:

  • 作用:装饰模式指的是在不改变原类的基础上,动态地扩展一个类的功能。

    InputStream(抽象父类)
    
    FileInputStream(实现子类,读写性能较差)
    
    BufferedInputStream(实现子类,装饰类,读写性能高)
    
  • 步骤:

    1. 定义父类。
    2. 定义原始类,继承父类,定义功能。
    3. 定义装饰类,继承父类,包装原始类,增强功能。

InputStream.java

/**
 * 共同父类
 */
public abstract class InputStream {
    public abstract int read();
    public abstract int read(byte[] bytes);
}

FileInputStream.java

import java.util.Arrays;

/**
 * 原始类
 */
public class FileInputStream extends InputStream {

    @Override
    public int read() {

        System.out.println("低性能的方式读取了一个字节a");

        return 97;
    }

    @Override
    public int read(byte[] bytes) {
        bytes[0] = 97;
        bytes[1] = 98;
        bytes[2] = 99;

        System.out.println("低性能的方式读取了一个字节数组" + Arrays.toString(bytes));
        return 3;
    }
}

BufferedInputStream.java

/**
 * 装饰类
 */
public class BufferedInputStream extends InputStream{

    private InputStream inputStream;

    public BufferedInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() {
        System.out.println("提供8KB的缓冲区,提高读数据性能!");
        return inputStream.read();
    }

    @Override
    public int read(byte[] bytes) {
        System.out.println("提供8KB的缓冲区,提高读数据性能!");

        return inputStream.read(bytes);
    }
}

DecoratorPattern.java

public class DecoratorPattern {
    public static void main(String[] args) {
        InputStream inputStream = new BufferedInputStream(new FileInputStream());

        System.out.println(inputStream.read());
        System.out.println(inputStream.read(new byte[3]));
    }
}

执行结果:

提供8KB的缓冲区,提高读数据性能!
低性能的方式读取了一个字节a
97
提供8KB的缓冲区,提高读数据性能!
低性能的方式读取了一个字节数组[97, 98, 99]
3

总结:

  1. 装饰设计模式的作用?
    • 装饰模式指的是在不改变原类的基础上,动态地扩展一个类的功能。
本文含有隐藏内容,请 开通VIP 后查看