Java研学-MyBatis框架

news2024/11/15 4:30:55

一 MyBatis框架

1 框架介绍

  框架:对基础代码进行封装并提供相应的API,调用API可省去一些代码的编写,从而提高效率。一个好的框架一定是经过测试,自身的功能已经实现,可以完成特定的功能。

2 MyBatis 框架

  MyBatis 框架是一款持久层框架,他集成了绝大部分的JDBC代码与手动设置参数以及获取结果集。MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的domain对象,支持自定义sql,存储过程与高级映射
在这里插入图片描述
Mybatis的功能架构分为三层:

  API接口层:提供给外部使用的接口API,通过这些本地API可操纵数据库。接口层接收到调用请求就会调用数据处理层完成具体的数据处理。

  数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

  基础支撑层:负责最基础的功能支撑,即连接管理、事务管理、配置加载和缓存处理,将这些共用的东西抽取出来作为最基础的组件。为上层提供最基础的支撑(数据处理层)

3 使用流程

  ① 编写 mybatis-config.xml 文件:配置连接数据的四要素(数据源),以及项目需要依赖的通用配置
  ② 编写 XxxMapper.xml文件:映射文件,编写SQL语句和类型映射
  ③ MyBatis 加载配置文件,创建SqlSessionFactory 对象(类似连接池)
  ④ 通过SqlSessionFactory来生产SqlSesson 对象 SqlSesson(类似 Connection连接对象)
  ⑤ 使用SqlSession即可执行CRUD操作
  ⑤ mybatis底层会进行数据处理,SQL执行以及结果集的处理

4 准备环境与测试

① 添加项目需要的 jar 包,或者导入对应的依赖

  Lombok,可自动生成getter/setter/toString等方法

  mysql-connector-java-5.1.36-bin.jar,Java语言连接MySQL数据库的驱动包,它提供了一系列的类和方法,可以用来执行创建和查询数据库、插入和更新数据、管理用户和权限等任务

  mybatis-3.4.6.jar,MyBatis框架的核心包,它提供了一系列的类和方法,用于对数据库进行操作。MyBatis框架是一个持久层框架,它可以通过配置文件和映射文件来将SQL语句映射到Java对象,从而简化了数据库的操作。

② 数据库表

CREATE TABLE `t_employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `eid` varchar(10) NOT NULL,
  `ename` varchar(6) NOT NULL,
  `job` varchar(10) DEFAULT NULL,
  `salary` int(10) DEFAULT NULL,
  `password` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `eid` (`eid`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

③ 实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    private Long id;
    private String eid;
    private String ename;
    private String password;
    private String job;
    private Integer salary;
}

④ resources下编写主配置文件:mybatis-config.xml,拷贝xml约束,添加环境配置(事物管理器,连接池,映射文件),在此还需要关联映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--配置日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--配置数据库连接信息-->
     <environments default="dev">
         <environment id="dev">
             <!--配置事务管理模式-->
             <transactionManager type="JDBC"></transactionManager>
             <!--配置数据源信息-->
             <dataSource type="POOLED">
                 <property name="driver" value="com.mysql.jdbc.Driver"/>
                 <property name="url" value="jdbc:mysql:///web_crud"/>
                 <property name="username" value="root"/>
                 <property name="password" value="root"/>
             </dataSource>
         </environment>
     </environments>
    <!--加载映射文件-->
    <mappers>
        <mapper resource="mappers/EmployeeMapper.xml"></mapper>
        <!--扫描映射文件所在的包路径:mapper映射文件和mapper接口的路径需要保持一致,mapper路径需通过/分隔-->
    </mappers>
</configuration>

⑤ 于mapper目录中,编写对应的mapper文件,拷贝约束信息,编写SQL语句,一个项目可以操作多张表,每张表都需要一个mapper配置文件来编写SQL语句每条SQL语句都需要有一个唯一的标识,这个唯一的标识由 namespace+sqlid 组成
使用namespace+sqlid就得到了保存用户信息的唯一标识,如:cn.tj.mybatis.mapper.EmployeeMapper.insert 之后可以通过标识找到这条SQL语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace用来区别项目下的多个文件的标识-->
<mapper namespace="employee">
    <!--增加用户:id当前文件中sql标签的唯一标识-->
    <insert id="insertEmployee">
        insert into t_employee(eid,ename,job,salary,password) values(#{eid},#{ename},#{job},#{salary},#{password})
    </insert>
</mapper>

⑥ 接口

public interface EmployeeDao {
    //增加员工
    void insertEmployee(Employee employee);
}

⑦ 实现类

public class EmployeeDaoImpl implements EmployeeDao {
    @Override
    public void insertEmployee(Employee employee) {
        try {
            //1.加载配置文件信息
            InputStream rs = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(rs);
            //2.创建sqlSession
            //openSession()默认的时候是需要手动提交事务,如果设置自动提交事务需要参数设置为true
            SqlSession sqlSession = factory.openSession(true);
            //3.执行增加操作
            sqlSession.insert("employee.insertEmployee",employee);
            //手动提交事务
            //sqlSession.commit();
            //4.关闭对象
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

⑧ 测试类

public class EmployeeTest {
    /*增加用户测试*/
    @Test
    public void employee_insert(){
        EmployeeDao employeeDao=new EmployeeDaoImpl();
        Employee employee = new Employee(null, "tj012", "大黄", "123", "BA", 18000);
        employeeDao.insertEmployee(employee);
        System.out.println("增加用户成功");
    }
}

二 代码重构

  通过代码实现对员工表的基础增删改查

1 实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    private Long id;
    private String eid;
    private String ename;
    private String password;
    private String job;
    private Integer salary;
}

2 mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--加载外部配置文件-->
    <properties resource="db.properties"></properties>
    <!--类型别名-->
    <typeAliases>
        <!--一个实体类对应一个别名,映射文件中的resultType属性可以将路径缩写-->
        <!--<typeAlias type="cn.tj.mybatis.domain.Employee" alias="employee"></typeAlias>-->
        <!--扫描实体类所在的包路径将实体类全部起别名:别名默认是实体类名称  不区分大小写-->
        <package name="cn.tj.mybatis.domain"/>
    </typeAliases>
    <!--配置数据库连接信息-->
     <environments default="dev">
         <environment id="dev">
             <!--配置事务管理模式-->
             <transactionManager type="JDBC"></transactionManager>
             <!--配置数据源信息-->
             <dataSource type="POOLED">
                 <property name="driver" value="${jdbc.driver}"/>
                 <property name="url" value="${jdbc.url}"/>
                 <property name="username" value="${jdbc.user}"/>
                 <property name="password" value="${jdbc.password}"/>
             </dataSource>
         </environment>
     </environments>
    <!--加载映射文件-->
    <mappers>
        <mapper resource="mappers/EmployeeMapper.xml"></mapper>
    </mappers>
</configuration>

3 mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace用来区别项目下的多个文件的标识-->
<mapper namespace="cn.tj.mybatis.mapper.EmployeeMapper">
    <!--增加员工:id当前文件中sql标签的唯一标识-->
    <insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
        insert into t_employee(eid,ename,password,job,salary) values(#{eid},#{ename},#{password},#{job},#{salary})
    </insert>
    <!--修改员工-->
    <update id="updateEmployee">
        update t_employee set eid=#{eid} ,ename=#{ename} ,password=#{password} ,job=#{job},salary=#{salary} WHERE id=#{id}
    </update>
    <!--删除员工-->
    <delete id="deleteEmployee">
        DELETE from t_employee WHERE id=#{id}
    </delete>
    <!--根据id查询员工:查询必须设置返回类型参数resultType-->
    <select id="selectOne" resultType="employee">
        SELECT * from t_employee WHERE id=#{id}
    </select>
    <!--查询所有员工-->
    <select id="selectAll" resultType="employee">
        SELECT * from t_employee
    </select>
    <!--模糊查询-->
    <select id="selectByName" resultType="employee">
        SELECT * from t_employee  WHERE ename like "%"#{ename}"%"
    </select>
</mapper>

3 db.properties文件(连接数据所需信息)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///web_crud
jdbc.user=root
jdbc.password=root

4 log4j.properties日志文件,可监控mybatis运行(每一条SQL)(需导入log4j jar包)

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.cn.tj.mybatis.mapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

5 数据库表

CREATE TABLE `t_employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `eid` varchar(10) NOT NULL,
  `ename` varchar(6) NOT NULL,
  `job` varchar(10) DEFAULT NULL,
  `salary` int(10) DEFAULT NULL,
  `password` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `eid` (`eid`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

6 接口

public interface EmployeeDao {
    //增加用户
    public void insertEmployee(Employee user);
    //根据id修改用户
    public void updateEmployee(Employee user);
    //根据id删除用户
    public void deleteEmployee(Long id);
    //根据id查询用户
    public Employee selectOne(Long id);
    //查询所有用户
    public List<Employee> selectAll();
    //模糊查询
    public List<Employee> selectByName(String name);
}

7 实现类

public class EmpoyeeDaoImpl implements EmployeeDao {
    @Override
    public void insertEmployee(Employee employee) {
        try {
            //1.加载配置文件信息
            InputStream rs = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(rs);
            //2.创建sqlSession
            //openSession()默认的时候是需要手动提交事务,如果设置自动提交事务需要参数设置为true
            SqlSession sqlSession = factory.openSession(true);
            //3.执行增加操作
            sqlSession.insert("cn.tj.mybatis.mapper.EmployeeMapper.insertEmployee",employee);
            //手动提交事务
//            sqlSession.commit();
            //4.关闭对象
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void updateEmployee(Employee employee) {
        try {
            InputStream rs = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(rs);
            SqlSession sqlSession = factory.openSession(true);
            sqlSession.update("cn.tj.mybatis.mapper.EmployeeMapper.updateEmployee",employee);
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteEmployee(Long id) {
        try {
            InputStream rs = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(rs);
            SqlSession sqlSession = factory.openSession(true);
            sqlSession.delete("cn.tj.mybatis.mapper.EmployeeMapper.deleteEmployee",id);
            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Employee selectOne(Long id) {
        try {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession sqlSession = factory.openSession(true);
            Employee employee = sqlSession.selectOne("cn.tj.mybatis.mapper.EmployeeMapper.selectOne", id);
            return employee;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Employee> selectAll() {
        try {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            SqlSession sqlSession = factory.openSession(true);
            List<Employee> employeeList = sqlSession.selectList("cn.tj.mybatis.mapper.EmployeeMapper.selectAll");

            return employeeList;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Employee> selectByName(String name) {
            SqlSession sqlSession = MybatisUtil.getSqlSession();
            return sqlSession.selectList("cn.tj.mybatis.mapper.EmployeeMapper.selectByName", name);
    }
}

8 工具类

public class MybatisUtil {
    private static SqlSessionFactory factory=null;
    /*加载配置文件创建工厂对象,只需要加载一次即可*/
    static {
        try {
            InputStream rs = Resources.getResourceAsStream("mybatis-config.xml");
             factory = new SqlSessionFactoryBuilder().build(rs);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /*加载配置文件创建factory对象*/
    public static SqlSession getSqlSession(){
        return factory.openSession(true);
    }

    // 关闭对象
    public static void close(SqlSession sqlSession){
        if (sqlSession!=null){
            sqlSession.commit();
            sqlSession.close();
        }
    }
}

9 测试类

public class EmployeeTest {
    private EmployeeDao employeeDao=new EmpoyeeDaoImpl();
    /*增加用户测试*/
    @Test
    public void employee_insert(){
        Employee employee=new Employee(null,"tj0118","大黄","123","CTO",18000);
        employeeDao.insertEmployee(employee);
        System.out.println("用户的id:"+employee.getId());
        System.out.println("增加用户成功");
    }
    /*修改用户*/

    @Test
    public void employee_update() {
        employeeDao.updateEmployee(new Employee(2L,"tj018","大黄","123","CEO",18000));
    }


    /*删除用户*/
    @Test
    public void user_delete() {
        employeeDao.deleteEmployee(8l);
    }

    /*根据id查询*/
    @Test
    public void employee_selectOne() {
        Employee employee = employeeDao.selectOne(1l);
        System.out.println(employee);
    }

    /*查询所有*/
    @Test
    public void employee_selectAll() {
        List<Employee> employeeList = employeeDao.selectAll();
        for (Employee employee : employeeList) {
            System.out.println(employee);
        }
    }

    /*模糊查询*/
    @Test
    public void employee_selectByname() {
        List<Employee> userList =employeeDao.selectByName("大");
        for (Employee employee : userList) {
            System.out.println(employee);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1313138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十四 动手学深度学习v2计算机视觉 ——转置矩阵

文章目录 基本操作填充、步幅和多通道再谈转置卷积不填充&#xff0c;步幅为1填充为p&#xff0c;步幅为1填充为p&#xff0c;步幅为s 基本操作 填充、步幅和多通道 填充&#xff1a; 与常规卷积不同&#xff0c;在转置卷积中&#xff0c;填充被应用于的输出&#xff08;常规卷…

ShellCode注入程序

程序功能是利用NtQueueApcThreadEx注入ShellCode到一个进程中&#xff0c;程序运行后会让你选择模式&#xff0c;按1为普通模式&#xff0c;所需的常规API接口都是使用Windows原本正常的API&#xff1b;在有游戏保护的进程中Windows原本正常的API无法使用&#xff0c;这时候需要…

在windows系统搭建LVGL模拟器(codeblock工程)

1.codeblock准备 下载codeblock(mingw)&#xff0c;安装。可参考网上教程。 2.pc_simulator_win_codeblocks 工程获取 仓库地址&#xff1a;lvgl/lv_port_win_codeblocks: Windows PC simulator project for LVGL embedded GUI Library (github.com) 拉取代码到本地硬盘&…

Fabric使用自己的链码进行测试-go语言

书接前文 Fabric链码部署-go语言 通过上面这篇文章&#xff0c;你可以部署好自己的链码 &#xff08;后面很多命令是否需要修改&#xff0c;都是根据上面这篇文章来的&#xff0c;如果零基础的话建议先看上面这篇&#xff09; 就进行下一步 在测试网络上运行自己的链码 目…

STM32单片机项目实例:基于TouchGFX的智能手表设计(5)硬件驱动层程序设计

STM32单片机项目实例&#xff1a;基于TouchGFX的智能手表设计&#xff08;5&#xff09;硬件驱动层程序设计 目录 一、 概述 二、 新建工程与外设配置 三、 TouchGFX配置 四、 增加TouchGFX关键驱动 一、 概述 本文内容主要进行工程新建&#xff0c;硬件外设的配置以及添加…

《PySpark大数据分析实战》-10.独立集群模式的代码运行

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

03. 医院设置_后端

1、Swagger2 测试工具 编写和维护接口文档是每个程序员的职责&#xff0c;根据Swagger2可以快速帮助我们编写最新的API接口文档&#xff0c;再也不用担心开会前仍忙于整理各种资料了&#xff0c;间接提升了团队开发的沟通效率。 swagger通过注解表明该接口会生成文档&#xf…

CSC公派研究生项目|电气工程在读博士谈丹麦奥尔堡大学联培体会

2023年已近兔尾&#xff0c;很多人已经开始新一年的规划&#xff0c;对于国内在读博士而言&#xff0c;申请国家留学基金委&#xff08;CSC&#xff09;公派研究生项目也开始列入议事日程&#xff0c;然而&#xff0c;如何申请&#xff1f;在国外学习收获如何&#xff1f;本篇知…

IDEA小技巧

目录 1. IDEA自动添加注释 创建类的时候自动添加注释 创建函数、方法的注释 1. IDEA自动添加注释 参考文档&#xff1a;idea java 自动添加文件注释 idea新建类自动注释_mob6454cc73c728的技术博客_51CTO博客 【操作工具】IDEA创建类及已有类添加注释-详细操作_idea设置创建…

【状态机FSM 序列检测 饮料机_2023.12.1】

同步状态机 概念 同步状态机&#xff08;同一脉冲边沿触发&#xff09;&#xff1a;有限个离散状态及某状之间的转移 异步状态机无法综合 分类 Moore状态机 只和状态有关&#xff0c;与输入无关 Mealy状态机 和状态和输入都有关 Mealy型比Moore型少一个状态 结构 由状态寄…

AI日报:OpenAI向新用户重新开放ChatGPT Plus订阅

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 总览Chatgptplus重新开放订阅#暂停原因功能 OpenAI的1000万美元安全人工智能拨款拨款初衷学术捐赠 总览 ChatGPT Plus再次向新用户开放&#xff0c;但目前每三小时限制发送40条消息。 OpenAI还宣布拨款1000万美元…

喜报!Coremail荣获2023信创“大比武”优秀生态融合奖

近期&#xff0c;2023信创“大比武”金融业务创新应用赛道&#xff08;简称金融赛道&#xff09;活动正式落下帷幕。经过赛程的层层考核&#xff0c;中泰证券股份有限公司&#xff08;简称“中泰证券”&#xff09;与Coremail联合组成的“中泰证券CACTER邮件安全保卫队”最终在…

Linux篇:信号

一、信号的概念&#xff1a; ①进程必须识别能够处理信号&#xff0c;信号没有产生&#xff0c;也要具备处理信号的能力---信号的处理能力属于进程内置功能的一部分 ②进程即便是没有收到信号&#xff0c;也能知道哪些信号该怎么处理。 ③当进程真的受到了一个具体的信号的时候…

2021实战面试

1、Rem , em , px , % , vw 之间的区别 PX: px像素&#xff08;Pixel&#xff09;。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。 em: 1,子元素字体大小的em是相对于父元素字体大小 2,元素的width/height/padding/margin用em的话是相对于该元素的font-size rem:1rem是…

【回眸】Tessy 单元测试软件使用指南(三)怎么打桩和指针测试

目录 前言 Tessy 如何进行打桩操作 普通桩 高级桩 手写桩 Tessy单元测试之指针相关测试注意事项 有类型的指针&#xff08;非函数指针&#xff09;&#xff1a; 有类型的函数指针&#xff1a; void 类型的指针&#xff1a; 结语 前言 进行单元测试之后&#xff0c;但凡…

免费且强大卸载软件工具-Geek Uninstaller

Geek Uninstaller是一款用于Windows操作系统的免费卸载软件。它提供了一种比Windows内置卸载工具更彻底的卸载程序的方法。界面简单没有广告&#xff0c;操作也十分的简单。 特点 完全的程序卸载&#xff1a;Geek Uninstaller 被设计为彻底卸载程序&#xff0c;包括删除剩余…

YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定义模型

💡该教程为改进YOLO专栏,属于《芒果书》📚系列,包含大量的原创改进方式🚀 💡🚀🚀🚀内含改进源代码 按步骤操作运行改进后的代码即可💡更方便的统计更多实验数据,方便写作 YOLOv8改进《目标对象计数》多任务实验:深度集成版来了!支持自定义数据集训练自定…

springboot发送邮件,内容使用thymeleaf模板引擎排版

springboot发送邮件,内容使用thymeleaf模板引擎排版 1、导入jar包2、yml设置3、收件人以及收件信息设置4、发邮件service5、模版页面6、controller 1、导入jar包 <!--发送邮件--><dependency><groupId>org.springframework.boot</groupId><artifac…

使用Axure RP结合内网穿透工具制作本地静态web页面并实现公网访问

作者简介&#xff1a; 懒大王敲代码&#xff0c;正在学习嵌入式方向有关课程stm32&#xff0c;网络编程&#xff0c;数据结构C/C等 今天给大家讲解使用Axure RP结合内网穿透工具制作本地静态web页面并实现公网访问&#xff0c;希望大家能觉得实用&#xff01; 欢迎大家点赞 &am…

订单系统设计-状态机

1. 状态机 1.1 状态机简介 状态机是有限状态自动机的简称&#xff0c;是现实事物运行规则抽象而成的一个数学模型。 有限状态机一般都有以下特点&#xff1a; 可以用状态来描述事物&#xff0c;并且任一时刻&#xff0c;事物总是处于一种状态&#xff1b;事物拥有的状态总数…