CodeGeeX活动
快来分享你的内容吧~
- 2024-02-20·后端开发如何进行 Github 第三方登录详细讲解 (Java 版本) 创建一个 Github 应用 点击 Github 的头像 然后点击设置 3. 进入设置后,往下滑,点击开发者设置 4. 然后点击 Oauth apps 5. 随后创建一个新的OAuth App 6. 可以看到的是期中有几个必填选项,下面我们来一一说明 1. Application name 这个可以随意填写,就是你的一个应用名字而已查看全文鱼友0412:本文非常优质!欢迎鱼友参与知识碎片的贡献,并获取奖励:https://docs.qq.com/form/page/DQkdHTGFLQmJjV3VU#/fill知识碎片汇总:https://yuyuanweb.feishu.cn/wiki/AqfawFUT0iD69kkiRKoci6Nqnqc2211分享
- 2024-02-20·@编程小助手 微信: leikooo_
2024-02-20·我是皮蛋侠我转正了~ 大家好,我是皮蛋侠。 一个民办三本的可怜虫。自7月份毕业之后,一直在找工作。直到11月中旬。才找到一份相对来说过得去的工作。从毕业到工作,共投了4k+份简历。过程中,陆续又收到几家坑爹公司的offer,文章后面细说。 我与星球的缘分 我忘记我啥时候加入的星球了,大概是临近毕业那时候吧。springBoot囫囵吞枣学了一遍,springCloud似懂非懂。只知道有这个东西,不知道怎么运用查看全文逆风微笑的代码狗:你海投了这么多啊,我感觉工作到现在投的公司应该不到30家。面的也不过十几家吧1545933分享- 2024-02-19·Java后端#2023年度总结# 年度总结感悟: 很久之前就准备写年度总结了,由于是第一次写年度总结,出现了很多问题,导致自己的年终总结一直没有写出来。 第一个问题是写的太细,自己把之前记录的每天做的内容都看一遍后,然后做总结。这样出现的问题就是一是抓不住重点; 二是需要耗费的时间太长,之前总结一个星期的内容,就需要花费近一个小时,实在是搞得我没有动力去做了。 第二个问题是,由于 2023 年前半年,并没有记查看全文编程导航_小y:我在参加编程导航 #2023年度总结# 分享活动:https://t.zsxq.com/15sNIKr4J
1670分享
模板引擎-Thymeleaf与Freemarker
<html> <head></head> <body> <div class="content ql-editor"> <p><span style="font-size: 2em; font-family: PingFangSC-Regular;">模板引擎</span></p> <p><br></p> <p>模板引擎:将数据和页面显示分离。</p> <p><br></p> <p>模板文件 + 数据 --> 模板引擎 --> html</p> <p><br></p> <p>通过正则表达式来识别哪些是固定不变的模板,哪些是变化的数据,用已有的API(如:不同的表达式类型)把什么样的数据用什么格式来渲染进去,根据提供好的规则,模板引擎就可以解析出来。</p> <p><br></p> <p>JSP的局限性:</p> <p><br></p> <p>1)springboot内嵌容器时(打成jar包),不支持JSP。</p> <p>如果要运行JSP需要打成war包,则需要使用外部容器。</p> <p><br></p> <p>2)undertow容器是不支持JSP的。</p> <p><br></p> <p>3)扩展性有限,对于一些默认处理,很难自定义。</p> <p><br></p> <p>4)在容器化技术(如docket)中,对JSP的使用也很繁杂。</p> <p><br></p> <p>因为JSP有这么多的局限性,所以才有了模板引擎</p> <p><br></p> <p>较早的模板引擎:velocity、freemarker</p> <p>国人开发的模板:beetl</p> <p><br></p> <h1>Thymeleaf</h1> <p><br></p> <p>自然的模板,动静分离,是一个处理纯文本的模板引擎。</p> <p><br></p> <h2>引入thymeleaf命名空间</h2> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment"><!--引入thymeleaf的命名空间,使用时才有提示--><html lang="en" xmlns:th="http://www.thymeleaf.org"></span> </div> </div> <h2><br></h2> <h2>表达式</h2> <p><br></p> <p><strong>${}</strong> 变量表达式,引用一个变量</p> <p><strong><em>{}</em></strong><em> 选择变量表达式,当我们通过${}拿到一个对象时,可以通过</em>{}拿到这个对象的属性值</p> <p><strong>#{} </strong>消息表达式,可以通过配置文件拿到某些数据,还可以用来实现国际化效果</p> <p><strong>@{}</strong> 链接表达式</p> <p><strong>~{}</strong> 片段表达式,引用相同的代码片段</p> <p><br></p> <h2>字符串的拼接</h2> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-tag"><p th:text="${person.name} + ' is ' + ${person.age}"></p><!--使用 | 将${}的内容替换成对象,其余的不变--><p th:text="|${person.name} is ${person.age}|"></p></span> </div> </div> <h2><br></h2> <h2>条件判断</h2> <p><br></p> <h3>if的使用</h3> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-tag"><p th:if="${msg=='yes'}"></span>这是第一个msg的取值:<span class="ql-token hljs-tag"></p><p th:text="${msg}" th:if="${msg=='yes'}"></p><p th:if="${msg=='no'}"></span>这是第二个msg的取值:<span class="ql-token hljs-tag"></p><p th:text="${msg}" th:if="${msg=='no'}"></p></span> </div> </div> <h3>unless的使用</h3> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-tag"><p th:unless="${msg=='yes'}"></span>这是第一个msg的取值:<span class="ql-token hljs-tag"></p><p th:text="${msg}" th:unless="${msg=='yes'}"></p><!--当msg的值不等于no时进行展示,所以下面的会展示出来--><p th:unless="${msg=='no'}"></span>这是第二个msg的取值:<span class="ql-token hljs-tag"></p><p th:text="${msg}" th:unless="${msg=='no'}"></p></span> </div> </div> <h3>switch的使用</h3> <div class="ql-code-block-container"> <div class="ql-code-block"> <div th:switch="${num}"><p th:case="1">1</p><p th:case="2">2</p><p th:case="*">*</p></div> </div> </div> <h2><br></h2> <h2>for循环</h2> <h3>th:each</h3> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-tag"><table><thead><tr><th></span>名字<span class="ql-token hljs-tag"></th><th></span>年龄<span class="ql-token hljs-tag"></th></tr></thead><tbody><tr th:each="data:${list}"><td th:text="${data.name}"></span>name<span class="ql-token hljs-tag"></td><td th:text="${data.age}"></span>age<span class="ql-token hljs-tag"></td></tr></tbody></table></span> </div> </div> <h3><br></h3> <h3>状态变量</h3> <p>默认命名是:参数名+Stat(如上的:dataStat),用来<strong>保存迭代状态</strong></p> <p>自定义的命名是:<code><tr th:each="data,status:${list}"></code> 此时状态变量名为status</p> <p>状态变量的属性:</p> <p>index 索引,从0开始</p> <p>count 计数,从1开始</p> <p>size 集合的大小</p> <p>current 当前对象</p> <p>first / last 布尔类型,是否是第一个/最后一个</p> <p>even / odd 布尔类型,是否是 偶数 / 奇数 个</p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-tag"><table><thead><tr><th></span>名字<span class="ql-token hljs-tag"></th><th></span>年龄<span class="ql-token hljs-tag"></th><th></span>index<span class="ql-token hljs-tag"></th><th></span>count<span class="ql-token hljs-tag"></th><th></span>size<span class="ql-token hljs-tag"></th><th></span>current<span class="ql-token hljs-tag"></th><th></span>first<span class="ql-token hljs-tag"></th><th></span>last<span class="ql-token hljs-tag"></th><th></span>even<span class="ql-token hljs-tag"></th><th></span>odd<span class="ql-token hljs-tag"></th></tr></thead><tbody><tr th:each="data:${list}"><td th:text="${data.name}"></span>name<span class="ql-token hljs-tag"></td><td th:text="${data.age}"></span>age<span class="ql-token hljs-tag"></td><td th:text="${dataStat.index}"></span>index<span class="ql-token hljs-tag"></td><td th:text="${dataStat.count}"></span>count<span class="ql-token hljs-tag"></td><td th:text="${dataStat.size}"></span>size<span class="ql-token hljs-tag"></td><td th:text="${dataStat.current}"></span>current<span class="ql-token hljs-tag"></td><td th:text="${dataStat.first}"></span>first<span class="ql-token hljs-tag"></td><td th:text="${dataStat.last}"></span>last<span class="ql-token hljs-tag"></td><td th:text="${dataStat.even}"></span>even<span class="ql-token hljs-tag"></td><td th:text="${dataStat.odd}"></span>odd<span class="ql-token hljs-tag"></td></tr></tbody></table></span> </div> </div> <h2><br></h2> <h2>URL的使用</h2> <h3>基础使用</h3> <div class="ql-code-block-container"> <div class="ql-code-block"> <form th:action="@{/login}" method="post"> </div> <div class="ql-code-block"> username: </div> <div class="ql-code-block"> <input type="text" name="username"> </div> <div class="ql-code-block"> password: </div> <div class="ql-code-block"> <input type="password" name="password"><input type="submit" value="提交"></form> </div> </div> <h3><br></h3> <h3>绝对路径</h3> <h4>1、基本用法</h4> <div class="ql-code-block-container"> <div class="ql-code-block"> <a th:href="@{http://cn.bing.com}">外链到:bing</a><!--渲染效果为<a href="http://cn.bing.com">外链到:bing</a>--> </div> </div> <h4>2、协议自动识别补全</h4> <p>**使用方法: 开头用 // **</p> <p>以引用静态资源举例:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <script type="text/javascript" th:src="@{//code.jquery.com/jquery-3.4.1.min.js}"></script> </div> </div> <p>渲染结果:</p> <p><code><script type="text/javascript" src="[//code.jquery.com/jquery-3.4.1.min.js](http://code.jquery.com/jquery-3.4.1.min.js)"></script></code></p> <h3><br></h3> <h3>相对路径</h3> <h4>1、上下文相关的URL</h4> <h5>1)基本用法</h5> <p><code><form th:action="@{/login}" method="post"></code></p> <p>比如说:部署项目的地址 localhost:8080/demo</p> <p>渲染结果为:</p> <p>验证方式:在配置文件中增加<code>server.servlet.context-path=/demo</code></p> <h5>2)引用静态资源的方式</h5> <p>先利用springboot的依赖配置</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.4.1</version></dependency> </div> </div> <p>接着在某个html文件中引用</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <script type="text/javascript" th:src="@{/webjars/jquery/3.4.1/jquery.js}"></script> </div> </div> <h5>3)参数的使用</h5> <p>请求中携带参数</p> <p>a)携带一个参数时,如:/addPerson?id=1</p> <p>在表达式中使用:@{/addPerson(id=1)}</p> <p><strong>单个参数的使用,用括号括起来</strong></p> <p>b)携带多个参数时,如:/addPerson?id=1&name=‘lsq’</p> <p>在表达式中使用:@{/addPerson(id=1,name=‘lsq’)}</p> <p><strong>用括号将多个参数括起来,参数之间用逗号隔开</strong></p> <p>c)当参数是路径的一部分时,如: /addPerson/1?name=‘lsq’</p> <p>在表达式中使用:@{/addPerson/{id}(id=1,name=‘lsq’)}</p> <p><strong>支持路径中包含变量 可以使用参数去替换</strong></p> <h4>2、服务器相关URL</h4> <p><code><a th:href="@{~/a.html}"></a></code></p> <p>通过 <strong>~</strong> 知道指定的是服务器某个地址,就不是和项目相关的,这样不同的项目可以访问同一个服务器下的某个文件。</p> <p>渲染结果为:<strong><a href="/a.html"></strong></p> <p><strong>不会增加上下文路径(项目访问路径),可以访问同一个服务器上的不同项目,访问到固定地址。</strong></p> <p><br></p> <p><strong>补充:</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/s5anxrvy.jpeg"></p> <p><br></p> <p><br></p> <p><br></p> <h2>内置对象/内嵌变量</h2> <p>工具类的使用:(使用方式是 <strong>加前缀#</strong><a href="http:///a.html" target="_blank">)</a></p> <p>dates、calendars、numbers、strings、objects、bools、arrays、lists、sets、Maps</p> <p>日期dates</p> <p>日期格式化:#datas.format()</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <!--dates的使用--><p th:text="${date}"></p><p th:text="${#dates.format(date,'yyyy-MM-dd HH:mm:ss')}"></p><!--拿到当前的时间--><p th:text="${#dates.createNow()}"></p><p th:text="${#dates.createToday()}"></p> </div> </div> <p>strings的使用</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <!--strings的使用--><p th:text="${#strings.isEmpty(str)}"></p><p th:text="${#strings.length(str)}"></p><p th:text="${#strings.equals(str,'duing')}"></p> </div> </div> <p><br></p> <h2>表达式语言</h2> <p>OGNL:Object-Graph Navigation Language 对象视图导航语言</p> <p>可以通过表达式语言 来获取Java的对象</p> <p>在JavaWeb中使用较多</p> <p>SpEL:基于Spring的表达式语言,提供给我们一种运行时对象的交互能力</p> <p>本质都是 在视图层和控制层将数据建立联系的方式</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <p th:text="${ 1 * 2 + 3 - 4}"></p><p th:text="${list[0].name}"></p><p th:text="${T(java.lang.Math).random()}"></p> </div> </div> <h2><br></h2> <h2>内联表达式</h2> <p>用两个中括号 <strong>[ [ ] ] 将一个引用的对象括起来,对象本身还是${}</strong> 获取,这样就能将展示的信息与其它字符串拼接起来了。(展示文本的时候简化逻辑)</p> <p>凡是可以用th:text或th:utext显示的内容都可以用内联表达式来转换。</p> <p><code>th:text</code> => <code>[[...]]</code> 转义了,若含有标签,标签效果不起作用</p> <p><code>th:utext</code> => <code>[(...)]</code> 不转义,按照标签效果输出</p> <p>如果文本需要展示含<code>[[]]</code>的数据,可以禁用内联表达式。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <p> 加油,[[${info}]] </p><p> 加油,<span th:text="${info}"></span> </p><!--禁用内联表达式--><p th:inline="none"> 加油,[[<span th:text="${info}"></span>]] </p> </div> </div> <p><strong>内联JavaScript</strong></p> <p>当给JavaScript中传一些数据的时候,使用内联JavaScript</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <script type="text/javascript" th:inline="javascript"> </div> <div class="ql-code-block"> //var info = [[${info}]]; </div> <div class="ql-code-block"> <!--内联js,同样支持动静分离,使用 /**/ 将动态数据引起来,在其后跟上静态数据,但这样使用之后原来的注释就不能用了,同时还有一个局限性:在外部js文件里不能使用,在html文件中的js代码生效 --> </div> <div class="ql-code-block"> var info = /*[[${info}]]*/ 123; </div> <div class="ql-code-block"> console.log(info); </div> <div class="ql-code-block"> </script> </div> </div> <p>类似js,同样支持css</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <style th:inline="css"></style> </div> </div> <h2><br></h2> <h2>碎片代码</h2> <p><strong>th:fragment,th:include,th:replace,th:insert</strong></p> <p>有些网页内容,需要在许多网页中使用,可以使用<code>th:fragment</code>在某个独立的(网页)标签中设置碎片代码,并为其命名。</p> <p>在需要引入碎片代码的网页中使用<code>th:include、th:replace、th:insert</code>等引入</p> <p><strong>include,replace,insert的区别:</strong></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>include是只引进碎片代码的内容,不把其标签属性引入(即不引入碎片标签),保留原有的标签属性</li> <li data-list="bullet"><span class="ql-ui"></span>replace与include相反,只引入碎片代码的内容,引入碎片标签,不保留原有的标签</li> <li data-list="bullet"><span class="ql-ui"></span>insert是即引入碎片标签,也保留原有标签(当样式冲突时,碎片标签属性起作用,看网页源码可知)</li> </ol> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1632761346305089537/krhrnx5y.jpeg"></p> <p><br></p> <p><br></p> <h1>Freemarker</h1> <p><br></p> <p>一个用java编写的模板引擎,适合作为web项目中的视图层组件,是生成文本的工具。</p> <p>ftl文件本质也是html格式的</p> <p>freemarker区分大小写</p> <p>ftl组成部分:文本(静态的),插值(动态的),标签,注释**<#-- -->**</p> <h2><br></h2> <h2>使用步骤</h2> <p><strong>1)引入freemarker依赖</strong></p> <div class="ql-code-block-container"> <div class="ql-code-block"> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency> </div> </div> <p><strong>2)增加自定义的配置文件</strong></p> <div class="ql-code-block-container"> <div class="ql-code-block"> #旧的使用方式# 文件位置#spring.mvc.view.prefix=/templates# 文件后缀#spring.mvc.view.suffix=.ftl </div> <div class="ql-code-block"> #新的使用方式# 文件位置spring.freemarker.template-loader-path=classpath:/templates# 文件后缀spring.freemarker.suffix=.ftl </div> </div> <p><strong>3)编写controller及ftl文件</strong></p> <h2><br></h2> <p><br></p> <h2>字符串的使用</h2> <p>定义变量:<strong><#assign</strong> info1 = ‘how are you?’></p> <p>字符串的拼接:</p> <p>Hello ${info <strong>+</strong> info1}</p> <p>字符串的内嵌函数:(调用时使用 <strong>字符串名?</strong> )</p> <p>${info1?substring(0,3)}</p> <p><#--左闭右开--></p> <p>${info1?length}</p> <div class="ql-code-block-container"> <div class="ql-code-block"> Hello ${info} </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <#–定义变量–> </div> <div class="ql-code-block"> <#assign info1 = ‘how are you?’> </div> <div class="ql-code-block"> <#–字符串的拼接–> </div> <div class="ql-code-block"> Hello ${info + info1} </div> <div class="ql-code-block"> <#--字符串的内嵌函数--> </div> <div class="ql-code-block"> ${info1?substring(0,3)} </div> <div class="ql-code-block"> <#--左闭右开--> </div> <div class="ql-code-block"> ${info1?length} </div> </div> <p><br></p> <h2>条件判断</h2> <div class="ql-code-block-container"> <div class="ql-code-block"> <#assign num = 666> </div> <div class="ql-code-block"> <#if num == 666> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>666<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> <#elseif num == 888> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>888<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> <#else> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>000<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> </#if> </div> <div class="ql-code-block"> <#switch num> </div> <div class="ql-code-block"> <#case 666> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>666<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> <#break> </div> <div class="ql-code-block"> <#case 888> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>888<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> <#break> </div> <div class="ql-code-block"> <#default> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><p></span>000<span class="ql-token hljs-tag"></p></span> </div> <div class="ql-code-block"> </#switch> </div> </div> <h2><br></h2> <h2>列表</h2> <div class="ql-code-block-container"> <div class="ql-code-block"> <#assign myList = [1,3,5,7,10,9]> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><h5></span>无序数组:<span class="ql-token hljs-tag"></h5></span> </div> <div class="ql-code-block"> <#list myList as item> </div> <div class="ql-code-block"> ${item} </div> <div class="ql-code-block"> </#list> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><br><h5></span>有序数组:<span class="ql-token hljs-tag"></h5></span> </div> <div class="ql-code-block"> <#list myList?sort as item> </div> <div class="ql-code-block"> ${item} </div> <div class="ql-code-block"> </#list> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><br></span> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <#list [1,2,3] as item> </div> <div class="ql-code-block"> ${item} </div> <div class="ql-code-block"> </#list> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><br></span> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <#list 1..3 as item> </div> <div class="ql-code-block"> ${item} </div> <div class="ql-code-block"> </#list> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><br></span> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <#--元素的别名 缀上 _index 代表下标--> </div> <div class="ql-code-block"> <#list 1..3 as item> </div> <div class="ql-code-block"> ${item_index} <#--获取索引--> </div> <div class="ql-code-block"> , ${item}<span class="ql-token hljs-tag"><br></span> </div> <div class="ql-code-block"> </#list> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> <#list 1..3 as item> </div> <div class="ql-code-block"> <#--判断是否有下一项--> </div> <div class="ql-code-block"> <#if item_has_next> </div> <div class="ql-code-block"> ${item} </div> <div class="ql-code-block"> </#if> </div> <div class="ql-code-block"> </#list> </div> </div> <h2><br></h2> <h2>判断变量是否为空</h2> <div class="ql-code-block-container"> <div class="ql-code-block"> <#--判断变量是否为空:通过 ! 来判断--> </div> <div class="ql-code-block"> <#--<#assign str = 'str'>--> </div> <div class="ql-code-block"> ${str!"default"} </div> </div> <h2><br></h2> <h2>引入其它文件的值</h2> <p>在一个html中引入另一个html中的一些变量的值,不仅能读取,还能修改</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <#import 'other.ftl' as otherFtl> </div> <div class="ql-code-block"> ${otherFtl.name} <span class="ql-token hljs-tag"><br></span> </div> <div class="ql-code-block"> <#--不仅可以读取到它的值,还能修改它的值--> </div> <div class="ql-code-block"> <#assign name = 'welcome ftl is here' in otherFtl> </div> <div class="ql-code-block"> ${otherFtl.name} </div> </div> <p><br></p> <p><br></p> <p>更好的阅读体验可移步到:<a href="https://www.codefather.cn/post/1760679073350279169" target="_blank">模板引擎-Thymeleaf与Freemarker - 编程导航 (</a><a href="http://codefather.cn" target="_blank">code-nav.cn</a><a href="https://www.codefather.cn/post/1760679073350279169" target="_blank">)</a></p> <p><br></p> <p>#模板引擎# #Thymeleaf# #Freemarker# #知识碎片# #CodeGeeX活动#</p> </div> </body> </html>
伙伴匹配引入 GEO 实现搜索附近用户
<html> <head></head> <body> <div class="content ql-editor"> <h2><span style="font-family: PingFangSC-Regular; font-size: 1.5em;">前言</span></h2> <p><br></p> <p>鱼皮哥在直播中提出可以通过Redis GEO实现编辑距离和搜索附近用户功能,也算是是一个拓展点。这个项目我做完有一个多月了,最近在做其拓展功能顺便也把这个功能实现一波,整体的实现并不困难,学完 Redis 再看会更轻松(未学过也没事)。话不多说直接开始撸代码吧。</p> <p><br></p> <h2>设计思路和流程</h2> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>在 User(用户)表中添加两个字段 longitude(经度)和 dimension(维度),用以存储用户的经纬度坐标。因为Redis GEO 通过每个用户的经纬度坐标计算用户间的距离,同时其 Redis 数据结构为ZSET,ZSET 是一个有序的 List 类似 Java 的 SortedSet。在此场景 value 就是用户id,score 是经纬度信息( ZSET 根据 score值升序排序)。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/oytydz8n.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-keyword">create</span> <span class="ql-token hljs-keyword">table</span> hjj.user </div> <div class="ql-code-block"> ( </div> <div class="ql-code-block"> username <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">256</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'用户昵称'</span>, </div> <div class="ql-code-block"> id <span class="ql-token hljs-type">bigint</span> auto_increment comment <span class="ql-token hljs-string">'id'</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">primary</span> key, </div> <div class="ql-code-block"> userAccount <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">256</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'账户'</span>, </div> <div class="ql-code-block"> avatarUrl <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">1024</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'用户头像'</span>, </div> <div class="ql-code-block"> gender tinyint <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'用户性别'</span>, </div> <div class="ql-code-block"> profile <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">512</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'个人简介'</span>, </div> <div class="ql-code-block"> userPassword <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">512</span>) <span class="ql-token hljs-keyword">not</span> <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'用户密码'</span>, </div> <div class="ql-code-block"> phone <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">128</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'电话'</span>, </div> <div class="ql-code-block"> email <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">512</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'邮箱'</span>, </div> <div class="ql-code-block"> userStatus <span class="ql-token hljs-type">int</span> <span class="ql-token hljs-keyword">default</span> <span class="ql-token hljs-number">0</span> <span class="ql-token hljs-keyword">not</span> <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'状态 0 - 正常'</span>, </div> <div class="ql-code-block"> createTime datetime <span class="ql-token hljs-keyword">default</span> <span class="ql-token hljs-built_in">CURRENT_TIMESTAMP</span> <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'创建时间'</span>, </div> <div class="ql-code-block"> updateTime datetime <span class="ql-token hljs-keyword">default</span> <span class="ql-token hljs-built_in">CURRENT_TIMESTAMP</span> <span class="ql-token hljs-keyword">null</span> <span class="ql-token hljs-keyword">on</span> <span class="ql-token hljs-keyword">update</span> <span class="ql-token hljs-built_in">CURRENT_TIMESTAMP</span> comment <span class="ql-token hljs-string">'更新时间'</span>, </div> <div class="ql-code-block"> isDelete tinyint <span class="ql-token hljs-keyword">default</span> <span class="ql-token hljs-number">0</span> <span class="ql-token hljs-keyword">not</span> <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'是否删除'</span>, </div> <div class="ql-code-block"> userRole <span class="ql-token hljs-type">int</span> <span class="ql-token hljs-keyword">default</span> <span class="ql-token hljs-number">0</span> <span class="ql-token hljs-keyword">not</span> <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'用户角色 0 - 普通用户 1 - 管理员'</span>, </div> <div class="ql-code-block"> planetCode <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">512</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'星球编号'</span>, </div> <div class="ql-code-block"> tags <span class="ql-token hljs-type">varchar</span>(<span class="ql-token hljs-number">1024</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'标签列表(json)'</span>, </div> <div class="ql-code-block"> longitude <span class="ql-token hljs-type">decimal</span>(<span class="ql-token hljs-number">10</span>, <span class="ql-token hljs-number">6</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'经度'</span>, </div> <div class="ql-code-block"> dimension <span class="ql-token hljs-type">decimal</span>(<span class="ql-token hljs-number">10</span>, <span class="ql-token hljs-number">6</span>) <span class="ql-token hljs-keyword">null</span> comment <span class="ql-token hljs-string">'纬度'</span> </div> <div class="ql-code-block"> ) </div> <div class="ql-code-block"> comment <span class="ql-token hljs-string">'用户'</span>; </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>在 UserVO 类中添加distance字段,用以向前端返回每个用户与自己之间的距离,类型为Double。</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户信息封装类</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"><span class="ql-token hljs-meta">@Data</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">class</span> <span class="ql-token hljs-title">UserVO</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * id</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> <span class="ql-token hljs-type">long</span> id; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户昵称</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String username; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 账户</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String userAccount; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户头像</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String avatarUrl; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户性别</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Integer gender; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户简介</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String profile; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 电话</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String phone; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 邮箱</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String email; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 状态 0 - 正常</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Integer userStatus; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 创建时间</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Date createTime; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 更新时间</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Date updateTime; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户角色 0 - 普通用户 1 - 管理员</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Integer userRole; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 星球编号</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String planetCode; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 标签列表 json</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> String tags; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 用户距离</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> Double distance; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">private</span> <span class="ql-token hljs-keyword">static</span> <span class="ql-token hljs-keyword">final</span> <span class="ql-token hljs-type">long</span> <span class="ql-token hljs-variable">serialVersionUID</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-number">1L</span>; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h2>基本业务实现</h2> <h3><br></h3> <h3>导入各个用户经纬度数据</h3> <p><br></p> <p>编写测试类导入各个用户的经纬度信息并且写入Redis中,Redis GEO会根据它计算出一个 score值。进行 Redis GEO 相关操作时可以使用 Spring Data Redis 提供现成的操作 Redis 的模板——StringRedisTemplate,注意其 Key/Value 都是String类型。</p> <p><br></p> <p>stringRedisTemplate.opsForGeo().add() 支持一次一次地传入经纬度信息,可以通过List和Map集合类型传入用户经纬度信息,这里我们用List集合。第一个参数为Redis的key,这不用过多介绍。第二个参数为List类型,泛型为RedisGeoCommands.GeoLocation<String>,其参数为用户id和Point(Point可以理解为是一个圆的一个点吧,经纬度就是x/y坐标)。</p> <p><br></p> <p>stringRedisTemplate.opsForGeo().add()传入的参数:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/z9z8x6xy.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-meta">@Test</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">importUserGEOByRedis()</span> { </div> <div class="ql-code-block"> List<User> userList = userService.list(); <span class="ql-token hljs-comment">// 查询所有用户</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">key</span> <span class="ql-token hljs-operator">=</span> RedisConstant.USER_GEO_KEY; <span class="ql-token hljs-comment">// Redis的key</span> </div> <div class="ql-code-block"> List<RedisGeoCommands.GeoLocation<String>> locationList = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">ArrayList</span><>(userList.size()); <span class="ql-token hljs-comment">// 初始化地址(经纬度)List</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (User user : userList) { </div> <div class="ql-code-block"> locationList.add(<span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">RedisGeoCommands</span>.GeoLocation<>(String.valueOf(user.getId()), <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Point</span>(user.getLongitude(), </div> <div class="ql-code-block"> user.getDimension()))); <span class="ql-token hljs-comment">// 往locationList添加每个用户的经纬度数据</span> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> stringRedisTemplate.opsForGeo().add(key, locationList); <span class="ql-token hljs-comment">// 将每个用户的经纬度信息写入Redis中</span> </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>结果:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/867t1s89.jpeg"></p> <h3><br></h3> <h3>获取用户 id = 1 与其他用户的距离</h3> <p><br></p> <p>编写一个测试类计算用户 id = 1 与其他用户之间的距离。利用stringRedisTemplate.opsForGeo().distance()方法,其主要参数为member1和member2,Metric是计算距离的单位类型。从名称就可以知道member1和member2其实就是用户1和用户2的信息,因为我们在上面用 <a href="http://locationList.add" target="_blank">locationList.add</a>() 添加用户id和用户的经度坐标,所以这两个member就是用户id咯。</p> <p><br></p> <p><span style="color: rgb(152, 118, 170);">stringRedisTemplate</span>.opsForGeo<span style="color: rgb(84, 168, 87);">()</span>.distance()传入的参数:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/couhnkk4.jpeg"></p> <p>所以写个循环就可以算出用户 id = 1 与其他用户的距离</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-meta">@Test</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">getUserGeo()</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">key</span> <span class="ql-token hljs-operator">=</span> RedisConstant.USER_GEO_KEY; </div> <div class="ql-code-block"> List<User> userList = userService.list(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 计算每个用户与登录用户的距离</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (User user : userList) { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">distance</span> <span class="ql-token hljs-operator">=</span> stringRedisTemplate.opsForGeo().distance(key, </div> <div class="ql-code-block"> <span class="ql-token hljs-string">"1"</span>, String.valueOf(user.getId()), RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"> System.out.println(<span class="ql-token hljs-string">"User: "</span> + user.getId() + <span class="ql-token hljs-string">", Distance: "</span> + </div> <div class="ql-code-block"> distance.getValue() + <span class="ql-token hljs-string">" "</span> + distance.getUnit()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>结果:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/c9kg29xy.jpeg"></p> <p><br></p> <h3>搜索附近用户</h3> <p><br></p> <p>利用现成的 stringRedisTemplate.opsForGeo().radius 方法,第一个参数依然是Redis的key,第二个参数是Circle,看代码和名称就知道其是一个圆(传入Point即圆心和圆的半径)。想象搜索附近的用户就是搜索以你为圆心,半径为搜索距离的圆内的用户。理解这些代码就能顺理成章的撸出来了,是不是不算难。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-meta">@Test</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">searchUserByGeo()</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">loginUser</span> <span class="ql-token hljs-operator">=</span> userService.getById(<span class="ql-token hljs-number">1</span>); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">geoRadius</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Distance</span>(<span class="ql-token hljs-number">1500</span>, RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Circle</span> <span class="ql-token hljs-variable">circle</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Circle</span>(<span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Point</span>(loginUser.getLongitude(), loginUser.getDimension()), geoRadius); </div> <div class="ql-code-block"> RedisGeoCommands.<span class="ql-token hljs-type">GeoRadiusCommandArgs</span> <span class="ql-token hljs-variable">geoRadiusCommandArgs</span> <span class="ql-token hljs-operator">=</span> RedisGeoCommands.GeoRadiusCommandArgs </div> <div class="ql-code-block"> .newGeoRadiusArgs().includeCoordinates(); </div> <div class="ql-code-block"> GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().radius(RedisConstant.USER_GEO_KEY, circle, geoRadiusCommandArgs); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (GeoResult<RedisGeoCommands.GeoLocation<String>> result : results) { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (!result.getContent().getName().equals(<span class="ql-token hljs-string">"1"</span>)) { </div> <div class="ql-code-block"> System.out.println(result.getContent().getName()); <span class="ql-token hljs-comment">// // 打印1500km内的用户id</span> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>注意:搜索附近的用户会搜索到自己,所以可以加一个判断以排除自己。</p> <p><br></p> <p>结果:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608639807578177538/z6x9rhao.jpeg"></p> <p><br></p> <h2>应用至伙伴匹配项目</h2> <h3><br></h3> <h3>改写用户推荐接口</h3> <p><br></p> <p>我的recommend接口未从Redis缓存中读取数据,而是直接走数据库读取数据,有能力的可以自己实现,我后续也会实现的。同时注意返回类型是UserVO不是User,因为我的前端展示了推荐用户和自己之间的距离。</p> <p>UserController.recommendUsers:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-meta">@GetMapping("/recommend")</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> BaseResponse<List<UserVO>> <span class="ql-token hljs-title">recommendUsers(long pageSize, long pageNum, HttpServletRequest request)</span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">loginUser</span> <span class="ql-token hljs-operator">=</span> userService.getLoginUser(request); </div> <div class="ql-code-block"> QueryWrapper<User> queryWrapper = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">QueryWrapper</span><>(); </div> <div class="ql-code-block"> queryWrapper.ne(<span class="ql-token hljs-string">"id"</span>, loginUser.getId()); </div> <div class="ql-code-block"> IPage<User> page = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Page</span><>(pageNum, pageSize); </div> <div class="ql-code-block"> IPage<User> userIPage = userService.page(page, queryWrapper); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">redisUserGeoKey</span> <span class="ql-token hljs-operator">=</span> RedisConstant.USER_GEO_KEY; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 将User转换为UserVO</span> </div> <div class="ql-code-block"> List<UserVO> userVOList = userIPage.getRecords().stream() </div> <div class="ql-code-block"> .map(user -> { </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 查询距离</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">distance</span> <span class="ql-token hljs-operator">=</span> stringRedisTemplate.opsForGeo().distance(redisUserGeoKey, </div> <div class="ql-code-block"> String.valueOf(loginUser.getId()), String.valueOf(user.getId()), </div> <div class="ql-code-block"> RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">double</span> <span class="ql-token hljs-variable">value</span> <span class="ql-token hljs-operator">=</span> distance.getValue(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 创建UserVO对象并设置属性</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">UserVO</span> <span class="ql-token hljs-variable">userVO</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">UserVO</span>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 这里可以用BeanUtils.copyProperties(),就没必要重复set了</span> </div> <div class="ql-code-block"> userVO.setId(user.getId()); </div> <div class="ql-code-block"> userVO.setUsername(user.getUsername()); </div> <div class="ql-code-block"> userVO.setUserAccount(user.getUserAccount()); </div> <div class="ql-code-block"> userVO.setAvatarUrl(user.getAvatarUrl()); </div> <div class="ql-code-block"> userVO.setGender(user.getGender()); </div> <div class="ql-code-block"> userVO.setProfile(user.getProfile()); </div> <div class="ql-code-block"> userVO.setPhone(user.getPhone()); </div> <div class="ql-code-block"> userVO.setEmail(user.getEmail()); </div> <div class="ql-code-block"> userVO.setUserStatus(user.getUserStatus()); </div> <div class="ql-code-block"> userVO.setCreateTime(user.getCreateTime()); </div> <div class="ql-code-block"> userVO.setUpdateTime(user.getUpdateTime()); </div> <div class="ql-code-block"> userVO.setUserRole(user.getUserRole()); </div> <div class="ql-code-block"> userVO.setPlanetCode(user.getPlanetCode()); </div> <div class="ql-code-block"> userVO.setTags(user.getTags()); </div> <div class="ql-code-block"> userVO.setDistance(value); <span class="ql-token hljs-comment">// 设置距离值</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> userVO; </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .collect(Collectors.toList()); </div> <div class="ql-code-block"> System.out.println(userVOList); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> ResultUtils.success(userVOList); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h3>改写匹配用户接口</h3> <p><br></p> <p>UserController.matchUsers:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 推荐最匹配的用户</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * @return</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-meta">@GetMapping("/match")</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> BaseResponse<List<UserVO>> <span class="ql-token hljs-title">matchUsers(long num, HttpServletRequest request)</span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (num <=<span class="ql-token hljs-number">0</span> || num > <span class="ql-token hljs-number">20</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">throw</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">BusinessException</span>(ErrorCode.PARAMS_ERROR); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">loginUser</span> <span class="ql-token hljs-operator">=</span> userService.getLoginUser(request); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> ResultUtils.success(userService.matchUsers(num ,loginUser)); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>UserServiceImpl.matchUsers:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-meta">@Override</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> List<UserVO> <span class="ql-token hljs-title">matchUsers(long num, User loginUser)</span> { </div> <div class="ql-code-block"> QueryWrapper<User> queryWrapper = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">QueryWrapper</span><>(); </div> <div class="ql-code-block"> queryWrapper.isNotNull(<span class="ql-token hljs-string">"tags"</span>); </div> <div class="ql-code-block"> queryWrapper.ne(<span class="ql-token hljs-string">"id"</span>, loginUser.getId()); </div> <div class="ql-code-block"> queryWrapper.select(<span class="ql-token hljs-string">"id"</span>,<span class="ql-token hljs-string">"tags"</span>); </div> <div class="ql-code-block"> List<User> userList = <span class="ql-token hljs-built_in">this</span>.list(queryWrapper); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">tags</span> <span class="ql-token hljs-operator">=</span> loginUser.getTags(); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Gson</span> <span class="ql-token hljs-variable">gson</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Gson</span>(); </div> <div class="ql-code-block"> List<String> tagList = gson.fromJson(tags, <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">TypeToken</span><List<String>>() { </div> <div class="ql-code-block"> }.getType()); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 用户列表的下表 => 相似度'</span> </div> <div class="ql-code-block"> List<Pair<User,Long>> list = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">ArrayList</span><>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 依次计算当前用户和所有用户的相似度</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (<span class="ql-token hljs-type">int</span> <span class="ql-token hljs-variable">i</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-number">0</span>; i <userList.size(); i++) { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">user</span> <span class="ql-token hljs-operator">=</span> userList.get(i); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">userTags</span> <span class="ql-token hljs-operator">=</span> user.getTags(); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//无标签的 或当前用户为自己</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (StringUtils.isBlank(userTags) || user.getId() == loginUser.getId()){ </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">continue</span>; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> List<String> userTagList = gson.fromJson(userTags, <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">TypeToken</span><List<String>>() { </div> <div class="ql-code-block"> }.getType()); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//计算分数</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">long</span> <span class="ql-token hljs-variable">distance</span> <span class="ql-token hljs-operator">=</span> AlgorithmUtils.minDistance(tagList, userTagList); </div> <div class="ql-code-block"> list.add(<span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Pair</span><>(user,distance)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//按编辑距离有小到大排序</span> </div> <div class="ql-code-block"> List<Pair<User, Long>> topUserPairList = list.stream() </div> <div class="ql-code-block"> .sorted((a, b) -> (<span class="ql-token hljs-type">int</span>) (a.getValue() - b.getValue())) </div> <div class="ql-code-block"> .limit(num) </div> <div class="ql-code-block"> .collect(Collectors.toList()); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//有顺序的userID列表</span> </div> <div class="ql-code-block"> List<Long> userListVo = topUserPairList.stream().map(pari -> pari.getKey().getId()).collect(Collectors.toList()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//根据id查询user完整信息</span> </div> <div class="ql-code-block"> QueryWrapper<User> userQueryWrapper = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">QueryWrapper</span><>(); </div> <div class="ql-code-block"> userQueryWrapper.in(<span class="ql-token hljs-string">"id"</span>,userListVo); </div> <div class="ql-code-block"> Map<Long, List<User>> userIdUserListMap = <span class="ql-token hljs-built_in">this</span>.list(userQueryWrapper).stream() </div> <div class="ql-code-block"> .map(user -> getSafetyUser(user)) </div> <div class="ql-code-block"> .collect(Collectors.groupingBy(User::getId)); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> List<User> finalUserList = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">ArrayList</span><>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (Long userId : userListVo){ </div> <div class="ql-code-block"> finalUserList.add(userIdUserListMap.get(userId).get(<span class="ql-token hljs-number">0</span>)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">redisUserGeoKey</span> <span class="ql-token hljs-operator">=</span> RedisConstant.USER_GEO_KEY; </div> <div class="ql-code-block"> List<UserVO> finalUserVOList = finalUserList.stream().map(user -> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">distance</span> <span class="ql-token hljs-operator">=</span> stringRedisTemplate.opsForGeo().distance(redisUserGeoKey, String.valueOf(loginUser.getId()), </div> <div class="ql-code-block"> String.valueOf(user.getId()), RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-type">UserVO</span> <span class="ql-token hljs-variable">userVO</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">UserVO</span>(); </div> <div class="ql-code-block"> userVO.setId(user.getId()); </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">// 这里可以用BeanUtils.copyProperties(),就没必要重复set了</span> </div> <div class="ql-code-block"> userVO.setUsername(user.getUsername()); </div> <div class="ql-code-block"> userVO.setUserAccount(user.getUserAccount()); </div> <div class="ql-code-block"> userVO.setAvatarUrl(user.getAvatarUrl()); </div> <div class="ql-code-block"> userVO.setGender(user.getGender()); </div> <div class="ql-code-block"> userVO.setProfile(user.getProfile()); </div> <div class="ql-code-block"> userVO.setPhone(user.getPhone()); </div> <div class="ql-code-block"> userVO.setEmail(user.getEmail()); </div> <div class="ql-code-block"> userVO.setUserStatus(user.getUserStatus()); </div> <div class="ql-code-block"> userVO.setCreateTime(user.getCreateTime()); </div> <div class="ql-code-block"> userVO.setUpdateTime(user.getUpdateTime()); </div> <div class="ql-code-block"> userVO.setUserRole(user.getUserRole()); </div> <div class="ql-code-block"> userVO.setPlanetCode(user.getPlanetCode()); </div> <div class="ql-code-block"> userVO.setTags(user.getTags()); </div> <div class="ql-code-block"> userVO.setDistance(distance.getValue()); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> userVO; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> }).collect(Collectors.toList()); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> finalUserVOList; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h3>添加搜索附近用户接口</h3> <p><br></p> <p>UserController.searchNearby:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 搜索附近用户</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-meta">@GetMapping("/searchNearby")</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> BaseResponse<List<UserVO>> <span class="ql-token hljs-title">searchNearby(int radius, HttpServletRequest request)</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (radius <= <span class="ql-token hljs-number">0</span> || radius > <span class="ql-token hljs-number">10000</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">throw</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">BusinessException</span>(ErrorCode.PARAMS_ERROR); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">user</span> <span class="ql-token hljs-operator">=</span> userService.getLoginUser(request); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">loginUser</span> <span class="ql-token hljs-operator">=</span> userService.getById(user.getId()); </div> <div class="ql-code-block"> List<UserVO> userVOList = userService.searchNearby(radius, loginUser); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> ResultUtils.success(userVOList); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-meta">@Override</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">public</span> List<UserVO> <span class="ql-token hljs-title">searchNearby(int radius, User loginUser)</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">geoKey</span> <span class="ql-token hljs-operator">=</span> RedisConstant.USER_GEO_KEY; </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">userId</span> <span class="ql-token hljs-operator">=</span> String.valueOf(loginUser.getId()); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Double</span> <span class="ql-token hljs-variable">longitude</span> <span class="ql-token hljs-operator">=</span> loginUser.getLongitude(); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Double</span> <span class="ql-token hljs-variable">dimension</span> <span class="ql-token hljs-operator">=</span> loginUser.getDimension(); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (longitude == <span class="ql-token hljs-literal">null</span> || dimension == <span class="ql-token hljs-literal">null</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">throw</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">BusinessException</span>(ErrorCode.NULL_ERROR, <span class="ql-token hljs-string">"登录用户经纬度参数为空"</span>); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">geoRadius</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Distance</span>(radius, RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Circle</span> <span class="ql-token hljs-variable">circle</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Circle</span>(<span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">Point</span>(longitude, dimension), geoRadius); </div> <div class="ql-code-block"> GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo() </div> <div class="ql-code-block"> .radius(geoKey, circle); </div> <div class="ql-code-block"> List<Long> userIdList = <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">ArrayList</span><>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">for</span> (GeoResult<RedisGeoCommands.GeoLocation<String>> result : results) { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">id</span> <span class="ql-token hljs-operator">=</span> result.getContent().getName(); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (!userId.equals(id)) { </div> <div class="ql-code-block"> userIdList.add(Long.parseLong(id)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> List<UserVO> userVOList = userIdList.stream().map( </div> <div class="ql-code-block"> id -> { </div> <div class="ql-code-block"> <span class="ql-token hljs-type">UserVO</span> <span class="ql-token hljs-variable">userVO</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-keyword">new</span> <span class="ql-token hljs-title">UserVO</span>(); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">User</span> <span class="ql-token hljs-variable">user</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-built_in">this</span>.getById(id); </div> <div class="ql-code-block"> BeanUtils.copyProperties(user, userVO); </div> <div class="ql-code-block"> <span class="ql-token hljs-type">Distance</span> <span class="ql-token hljs-variable">distance</span> <span class="ql-token hljs-operator">=</span> stringRedisTemplate.opsForGeo().distance(geoKey, userId, String.valueOf(id), </div> <div class="ql-code-block"> RedisGeoCommands.DistanceUnit.KILOMETERS); </div> <div class="ql-code-block"> userVO.setDistance(distance.getValue()); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> userVO; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> ).collect(Collectors.toList()); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> userVOList; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h2>前端改动</h2> <p><br></p> <p><a href="http://about:blank" target="_blank">Index.vue:</a></p> <div class="ql-code-block-container"> <div class="ql-code-block"> <template> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-search v-model="searchText" placeholder="搜索附近用户" @search="onSearch(searchText)"/></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-cell center title="心动模式"></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><template #right-icon></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-switch v-model="isMatchMode" size="24" /></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></template></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></van-cell></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><user-card-list :user-list="userList" :loading="loading"/></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-empty v-if="!userList || userList.length < 1" description="数据为空" /></span> </div> <div class="ql-code-block"> </template> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><script setup lang="ts"></span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> {ref, watchEffect} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"vue"</span>; </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> {useRoute} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"vue-router"</span>; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> myAxios <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"../plugins/myAxios.ts"</span>; </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> <span class="ql-token hljs-title">UserCardList</span> <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"../components/UserCardList.vue"</span>; </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> {<span class="ql-token hljs-title">UserType</span>} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"../models/user"</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">import</span> {showToast} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"vant"</span>; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> route = <span class="ql-token hljs-title">useRoute</span>(); </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> { tags } = route.<span class="ql-token hljs-property">query</span>; </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> searchText = <span class="ql-token hljs-title">ref</span>(<span class="ql-token hljs-string">''</span>); </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> userList = <span class="ql-token hljs-title">ref</span>([]); </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> isMatchMode = ref<boolean>(<span class="ql-token hljs-literal">false</span>); </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> loading = <span class="ql-token hljs-title">ref</span>(<span class="ql-token hljs-literal">true</span>); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> * 加载数据</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> <span class="ql-token hljs-title">loadData</span> = <span class="ql-token hljs-keyword">async</span> () => { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">let</span> userListData; </div> <div class="ql-code-block"> loading.<span class="ql-token hljs-property">value</span> = <span class="ql-token hljs-literal">true</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//心动模式</span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (isMatchMode.<span class="ql-token hljs-property">value</span>){ </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">const</span> num = <span class="ql-token hljs-number">10</span>; </div> <div class="ql-code-block"> userListData = <span class="ql-token hljs-keyword">await</span> myAxios.<span class="ql-token hljs-title">get</span>(<span class="ql-token hljs-string">'user/match'</span>,{ </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">params</span>: { </div> <div class="ql-code-block"> num, </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .<span class="ql-token hljs-title">then</span>(<span class="ql-token hljs-keyword">function</span> (<span class="ql-token hljs-params">response</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-variable">console</span>.<span class="ql-token hljs-title">log</span>(<span class="ql-token hljs-string">'/user/match succeed'</span>,response); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> response?.<span class="ql-token hljs-property">data</span>; </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .<span class="ql-token hljs-title">catch</span>(<span class="ql-token hljs-keyword">function</span> (<span class="ql-token hljs-params">error</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-variable">console</span>.<span class="ql-token hljs-title">log</span>(<span class="ql-token hljs-string">'/user/match error'</span>,error); </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> }<span class="ql-token hljs-keyword">else</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-comment">//普通用户使用分页查询</span> </div> <div class="ql-code-block"> userListData = <span class="ql-token hljs-keyword">await</span> myAxios.<span class="ql-token hljs-title">get</span>(<span class="ql-token hljs-string">'/user/recommend'</span>,{ </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">params</span>: { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">pageSize</span>: <span class="ql-token hljs-number">8</span>, </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">pageNum</span>: <span class="ql-token hljs-number">1</span>, </div> <div class="ql-code-block"> }, </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .<span class="ql-token hljs-title">then</span>(<span class="ql-token hljs-keyword">function</span> (<span class="ql-token hljs-params">response</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-variable">console</span>.<span class="ql-token hljs-title">log</span>(<span class="ql-token hljs-string">'/user/recommend succeed'</span>, response); </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">return</span> response?.<span class="ql-token hljs-property">data</span>; </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .<span class="ql-token hljs-title">catch</span>(<span class="ql-token hljs-keyword">function</span> (<span class="ql-token hljs-params">error</span>) { </div> <div class="ql-code-block"> <span class="ql-token hljs-variable">console</span>.<span class="ql-token hljs-title">log</span>(<span class="ql-token hljs-string">'/user/recommends error'</span>,error); </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (userListData){ </div> <div class="ql-code-block"> userListData.<span class="ql-token hljs-title">forEach</span>(<span class="ql-token hljs-function">(user: UserType) =></span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (user.<span class="ql-token hljs-property">tags</span>){ </div> <div class="ql-code-block"> user.<span class="ql-token hljs-property">tags</span> = <span class="ql-token hljs-title">JSON</span>.<span class="ql-token hljs-title">parse</span>(user.<span class="ql-token hljs-property">tags</span>); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> userList.<span class="ql-token hljs-property">value</span> = userListData; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> loading.<span class="ql-token hljs-property">value</span> = <span class="ql-token hljs-literal">false</span>; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-title">watchEffect</span>(<span class="ql-token hljs-function">() =></span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-title">loadData</span>(); </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">const</span> <span class="ql-token hljs-title">onSearch</span> = <span class="ql-token hljs-keyword">async</span> (<span class="ql-token hljs-params">searchText</span>) => { </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">let</span> userListData; </div> <div class="ql-code-block"> loading.<span class="ql-token hljs-property">value</span> = <span class="ql-token hljs-literal">true</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">const</span> res = <span class="ql-token hljs-keyword">await</span> myAxios.<span class="ql-token hljs-title">get</span>(<span class="ql-token hljs-string">'/user/searchNearby'</span>, { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">params</span>: { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">radius</span>: searchText </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (res?.<span class="ql-token hljs-property">code</span> === <span class="ql-token hljs-number">0</span>) { </div> <div class="ql-code-block"> userListData = res?.<span class="ql-token hljs-property">data</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (userListData){ </div> <div class="ql-code-block"> userListData.<span class="ql-token hljs-title">forEach</span>(<span class="ql-token hljs-function">(user: UserType) =></span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">if</span> (user.<span class="ql-token hljs-property">tags</span>){ </div> <div class="ql-code-block"> user.<span class="ql-token hljs-property">tags</span> = <span class="ql-token hljs-title">JSON</span>.<span class="ql-token hljs-title">parse</span>(user.<span class="ql-token hljs-property">tags</span>); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> userList.<span class="ql-token hljs-property">value</span> = userListData; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> loading.<span class="ql-token hljs-property">value</span> = <span class="ql-token hljs-literal">false</span>; </div> <div class="ql-code-block"> } <span class="ql-token hljs-keyword">else</span> { </div> <div class="ql-code-block"> <span class="ql-token hljs-title">showToast</span>(<span class="ql-token hljs-string">'搜索失败'</span>); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> loading.<span class="ql-token hljs-property">value</span> = <span class="ql-token hljs-literal">false</span>; </div> <div class="ql-code-block"> }; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"></script></span> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"><style scoped></span> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"></style></span> </div> </div> <p><br></p> <p><a href="http://about:blank" target="_blank">user.d.ts:</a></p> <div class="ql-code-block-container"> <div class="ql-code-block"> <span class="ql-token hljs-keyword">export</span> type <span class="ql-token hljs-title">UserType</span> = { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">id</span>: number; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">username</span>: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">userAccount</span>: string; </div> <div class="ql-code-block"> avatarUrl?: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">gender</span>:number; </div> <div class="ql-code-block"> profile?: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">phone</span>: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">email</span>: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">userStatus</span>: number; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">userRole</span>: number; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">planetCode</span>: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">createTime</span>: <span class="ql-token hljs-title">Date</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">tags</span>: string; </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">distance</span>: number; </div> <div class="ql-code-block"> }; </div> </div> <p><br></p> <p><a href="http://about:blank" target="_blank">UserCardList.vue:</a></p> <div class="ql-code-block-container"> <div class="ql-code-block"> <template> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-skeleton title avatar :row="3" :loading="loading" v-for="user in userList"></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-card</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> :desc="user.profile"</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> :title="`${user.username}(${user.planetCode})`"</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> :price="`${user.distance} km`"</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> currency=""</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> :thumb="user.avatarUrl"</span> </div> <div class="ql-code-block"><span class="ql-token hljs-tag"> ></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><template #tags></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-tag plain type="danger" v-for="tag in user.tags" style="margin-right: 8px; margin-top: 8px"></span> </div> <div class="ql-code-block"> {{ tag }} </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></van-tag></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></template></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><template #footer></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><van-button size="mini" @click="toIntroUser(user)"></span>联系我<span class="ql-token hljs-tag"></van-button></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></template></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></van-card></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></van-skeleton></span> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> </template> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><script setup lang="ts"></span> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">import</span> {<span class="ql-token hljs-title">UserType</span>} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"../models/user"</span>; </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">import</span> {useRouter} <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-string">"vue-router"</span>; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">const</span> router = <span class="ql-token hljs-title">useRouter</span>(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> interface <span class="ql-token hljs-title">UserCardListProps</span>{ </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">loading</span>: boolean </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">userList</span>: <span class="ql-token hljs-title">UserType</span>[]; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> <span class="ql-token hljs-title">withDefaults</span>(defineProps<<span class="ql-token hljs-title">UserCardListProps</span>>(), { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">loading</span>: <span class="ql-token hljs-literal">true</span>, </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-keyword">const</span> <span class="ql-token hljs-title">toIntroUser</span> = (<span class="ql-token hljs-params">user: UserType</span>) => { </div> <div class="ql-code-block"> router.<span class="ql-token hljs-title">push</span>({ </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">path</span>: <span class="ql-token hljs-string">'/user/intro'</span>, </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">query</span>: { </div> <div class="ql-code-block"> <span class="ql-token hljs-attr">userInfoParam</span>: <span class="ql-token hljs-title">JSON</span>.<span class="ql-token hljs-title">stringify</span>(user) </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></script></span> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"><style scoped></span> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> <span class="ql-token hljs-tag"></style></span> </div> </div> <p><br></p> <p>如果有些的不对的地方,还请各位及时指正。</p> <p><br></p> <p><br></p> </div> </body> </html>
如何进行 Github 第三方登录详细讲解 (Java 版本)
<html> <head></head> <body> <div class="content ql-editor"> <h1>如何进行 Github 第三方登录详细讲解 (Java 版本)</h1> <h2>创建一个 Github 应用</h2> <ol> <li data-list="ordered"><span class="ql-ui"></span>点击 Github 的头像</li> <li data-list="ordered"><span class="ql-ui"></span>然后点击设置</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/606s3v2e.jpeg" alt="image.png"> 3. 进入设置后,往下滑,点击开发者设置</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/sl38g041.jpeg" alt="image.png"> 4. 然后点击 Oauth apps</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/33kzh27t.jpeg" alt="image.png"> 5. 随后创建一个新的OAuth App</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/n2uauvfq.jpeg" alt="image.png"> 6. 可以看到的是期中有几个必填选项,下面我们来一一说明<img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/hitcy9bl.jpeg" alt="image.png"> 1. Application name 这个可以随意填写,就是你的一个应用名字而已 2. Homepage URL 填写你自己的前端页面的首页就好了 3. Application description 这个就是一个描述,随意填写就好了 4. Authorization callback URL 这个就是授权了以后进行身份判断的回调地址,用于自己对第三方信息进行获取,处理,鉴权。比较重要 5. 下面是一个填写示例:<img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/vya02cja.jpeg" alt="image.png"> 7. 全部填写好后我们成功创建了一个 Github 授权应用。 8. 这里需要我们进行几个关键信息的记录,一个是 client_id 这个不会变,一个是 client_secret (注意,这个只能查看一次请妥善保管),这些信息我们后面需要用。</p> <h2>定义一个跳转按钮,进行 Github 的授权</h2> <p>首先在前端页面中定义一个跳转到 Github 授权页面的链接,示例如下:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <a href="https://github.com/login/oauth/authorize?scope=user:email&client_id=请填写自己的client_id">Github登录</a> </div> </div> <p>这里可以自己随意快速的开一个前端应用,然后加一个链接标签,把 href 属性的值如上就可以了,只需要注意的是其中的 client_id 需要换成自己的就行,创建好以后,前后端分离的那么前端几乎就没有事情需要做了。这里主要是前后端分离版本。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/auedvua2.jpeg" alt="image.png"></p> <h2>通过授权拿到一个随机的 code</h2> <p>点击上面的 Github 登录以后会出现一个这样的页面</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/wdz5ibdj.jpeg" alt="image.png"></p> <p>点击绿色的按钮就会进行用户的授权,点击后就会重定向到我们上面那个写的回调地址。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/ald5z65a.jpeg" alt="image.png"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/ri46n1u2.jpeg" alt="image.png"></p> <p>通过在创建的应用的时候写的回调 URL 进行后端的处理,用户在点击授权以后,页面会重定向进行一个 Get 请求,后端去接收这个请求,并且进行处理就可以了,重定向的时候,会传递一个参数 code ,这个 code 可以用于获取 access_token , 获取到 token 以后用于获取 用户的信息。</p> <p>接口示例:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThirdLoginController { @GetMapping("/callback") public void getCode(String code){ System.out.println(code); </div> </div> <p>debug 拿值示例</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1645799822881509377/38epbyuv.jpeg" alt="image.png"></p> <p>这一部分只是为了我能够拿到 code 的值,我们一步一步来,看下面 👇</p> <h2>通过 code 进行后端回调处理,拿到access_token</h2> <p>这里我是通过阅读官方文档进行一点一点测试的,官方的文档的语言并不是 Java 这里给出官方文档和我的写法</p> <p>我的写法(这里发起 Http 请求使用的是 Hutool 工具包)</p> <div class="ql-code-block-container"> <div class="ql-code-block"> Map<String, Object > paramMap = new HashMap<>(); paramMap.put("client_id","填写自己的client_id"); paramMap.put("client_secret","填写自己的secret"); paramMap.put("code",code); paramMap.put("accept","json"); String result = HttpUtil.post("https://github.com/login/oauth/access_token",paramMap); </div> </div> <p>拿到的结果(类似👇):</p> <div class="ql-code-block-container"> <div class="ql-code-block"> access_token=gho_lK2Eop11rUH9U7aDTcxUrdORdTPAIS2vVDbh&scope=user%3Aemail&token_type=bearer </div> </div> <p>官方参考</p> <div class="ql-code-block-container"> <div class="ql-code-block"> get '/callback' do # get temporary GitHub code... session_code = request.env['rack.request.query_hash']['code'] # ... and POST it back to GitHub result = RestClient.post('https://github.com/login/oauth/access_token', {:client_id => CLIENT_ID, :client_secret => CLIENT_SECRET, :code => session_code}, :accept => :json) # extract the token and granted scopes access_token = JSON.parse(result)['access_token'] end </div> </div> <h2>拿到授权信息以后可以进行获取用户的信息</h2> <p>拿到 token 信息以后,我们要对信息进行处理,从中提取出 access_token 的值,然后就是发起请求获取用户信息了,这里获取用户信息,也是参考官方文档的,后面一直授权不能通过,通过问 AI 解决了问题。</p> <p>我的示例代码:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> // 处理拿到的信息 String token = result.split("&")[0].split("=")[1]; // 获取用户信息,发起get 请求,拿到用户信息 String finalResult = HttpRequest.get("https://api.github.com/user") .header("Authorization","token "+token) .header("X-GitHub-Api-Version", "2022-11-28") .execute().body(); </div> </div> <blockquote> <p>代码中我出问题的地方在 header 的设置,也就是不知道我获取的 token 怎么传给 github 官方,好像参考的里面说的也不是很明白,后面看了,github 官方关于 授权的文档是要加上两个请求头才能够正常进行请求<code>Authorization</code>(这里需要注意的是,token 值得书写,必须是 token后面空格,写access_token的值) <code>X-GitHub-Api-Version</code> 这个的话就是只需要带上 API 版本的值就好了。</p> </blockquote> <p>信息结果示例:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> { "login": "xwhking", "id": 97931879, "node_id": "U_kgDOBdZSZw", "avatar_url": "https://avatars.githubusercontent.com/u/97931879?v=4", "gravatar_id": "", "url": "https://api.github.com/users/xwhking", "html_url": "https://github.com/xwhking", "followers_url": "https://api.github.com/users/xwhking/followers", "following_url": "https://api.github.com/users/xwhking/following{/other_user}", "gists_url": "https://api.github.com/users/xwhking/gists{/gist_id}", "starred_url": "https://api.github.com/users/xwhking/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/xwhking/subscriptions", "organizations_url": "https://api.github.com/users/xwhking/orgs", "repos_url": "https://api.github.com/users/xwhking/repos", "events_url": "https://api.github.com/users/xwhking/events{/privacy}", "received_events_url": "https://api.github.com/users/xwhking/received_events", "type": "User", "site_admin": false, "name": "xwhking", "company": null, "blog": "", "location": null, "email": null, "hireable": null, "bio": null, "twitter_username": null, "public_repos": 8, "public_gists": 0, "followers": 1, "following": 2, "created_at": "2022-01-18T04:06:06Z", "updated_at": "2024-02-20T02:27:32Z" } </div> </div> <p>从中我们可以获取一些最为直观的信息,比如 name(github用户名),avatar_url (头像地址),html_url(用户的 github 主页)等。</p> <p>还可以以同样的方式获取用户的邮箱,只是请求的url 变化一下而且,就是请求 url 变成 <code>https://api.github.com/user/emails</code></p> <p>请求邮箱结果如下:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> [ { "email": "2837468248@qq.com", "primary": true, "verified": true, "visibility": "private" }, { "email": "97931879+xwhking@users.noreply.github.com", "primary": false, "verified": true, "visibility": null } ] </div> </div> <p>从中可以获取邮箱信息,通过判断 primary 字段判断是否为私有邮箱。</p> <p>参考官方文档:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> # fetch user information auth_result = JSON.parse(RestClient.get('https://api.github.com/user', {:params => {:access_token => access_token}})) # if the user authorized it, fetch private emails if has_user_email_scope auth_result['private_emails'] = JSON.parse(RestClient.get('https://api.github.com/user/emails', {:params => {:access_token => access_token}})) end erb :basic, :locals => auth_result </div> </div> <p>整体的一个后端代码示例:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> @RestController @RequestMapping("/third_login") public class ThirdLoginController { @GetMapping("/callback") public void getCode(String code){ System.out.println(code); Map<String, Object > paramMap = new HashMap<>(); paramMap.put("client_id","XXX"); paramMap.put("client_secret","XXX"); paramMap.put("code",code); paramMap.put("accept","json"); String result = HttpUtil.post("https://github.com/login/oauth/access_token",paramMap); String token = result.split("&")[0].split("=")[1]; // 获取用户信息 String finalResult = HttpRequest.get("https://api.github.com/user") .header("Authorization","token "+token) .header("X-GitHub-Api-Version", "2022-11-28") .execute().body(); System.out.println(finalResult); } } </div> </div> <h2>拿到的用户信息与数据库进行比对进行登录验证(思路)</h2> <p>我们都拿到 Github 上用户的信息了,那么只需要把拿到的信息放入我们自己的数据库,那么是不是就可以让用户进行登录了呀!</p> <p>比如我们可以记录用户的,id,用户名,邮箱,如果用户是第一次登录,就新创建一条记录,如果不是就进行数据库数据的比对进行登录,如果有错误,不让登录就好。没有错误就把页面重定向到对应的前端页面,并且把用户信息返回给前端。这样应该就算是第三方登录了吧!</p> #java# #第三方登录# #github# </div> </body> </html>
二分法(含 gift 演示)
<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <h1><strong style="color: rgb(51, 51, 51);">二分法</strong></h1> <p><br></p> <p>二分法原理很简单,但是细节是魔鬼</p> <p><br></p> <h1><strong style="color: rgb(51, 51, 51);">寻找一个数</strong></h1> <p><br></p> <p>当我们使用 while 循环条件是 left <= right 的时候,我们的最后情况是 left = right + 1 最后的范围就相当于 [right+ 1, right] 这样的区间不存在,所以如果不存在的话,那么我们直接返回 -1 就可以。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> int binarySearch(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> return mid; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> // 当这个小于 target 的时候 </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>但是当我们的搜索区间是 [left,rigfht) 的时候我们最后退出的 left 是等于 right 的,所以最后我们需要判断 nums[left] == target</p> <div class="ql-code-block-container"> <div class="ql-code-block"> class Solution { </div> <div class="ql-code-block"> public int search(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length; </div> <div class="ql-code-block"> while (left < right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> return mid; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> // 注意,因为 mid 已经被搜索过了 </div> <div class="ql-code-block"> right = mid; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 如果索引越界,说明数组中无目标元素,返回 -1 </div> <div class="ql-code-block"> if (left < 0 || left >= nums.length) { </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return nums[left] == target ? left : -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h1><strong style="color: rgb(51, 51, 51);">寻找两个数的边界</strong></h1> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">寻找左边界</strong></h2> <p><br></p> <p>两种写法</p> <p>1)使用,左闭右开的形式</p> <p><strong>gift 演示</strong>:</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608460212774109186/sh5v2ubg.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int left_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length; </div> <div class="ql-code-block"> // 搜索区间为 [left, right) </div> <div class="ql-code-block"> while (left < right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 此时已经排除了这个 mid 位置的值了,因为咱们的区间是 [left, right) </div> <div class="ql-code-block"> right = mid; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 最后退出的时候是 left == right </div> <div class="ql-code-block"> return left; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>后期熟练之当然可以写的更加简单,但是现阶段还是需要自己仔细写</p> <p><br></p> <p>2)使用,左闭右闭的形式</p> <p>下面是进行到一个特殊的情况的时候:</p> <p>这个时候我们会发现,黄色的箭头指的是 mid 的位置,这个时候我们的 left 位置在 0 , right 的位置在 2 位置,那么下一次运行 while 里面的时候,我们的 right = mid - 1,也就是在 0 的位置,此时 mid < target 我们的 left = mid + 1;也就变成了 1 的位置,此时 right < left 也就是 left = right + 1 循环结束!!!所以最后我们判断的都是 left 因为 left 可能会越界,但是 right 就不会越界,right 是不会越过左边界的。这是因为在每一步迭代中,如果 nums[mid] > target,那么搜索区间会变成 [left, mid-1],也就是说,right 会被更新为 mid - 1。就是在最左侧的时候我们这时候 left 都不会越过 index 是 0 的位置</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608460212774109186/hfsg6m93.jpeg"></p> <p><br></p> <p>找到左边的 index 需要 去靠近</p> <p><strong>gift 演示</strong>:</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608460212774109186/tw5yf2ya.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int left_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> // 搜索区间为 [left, right] </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 能够找到边界的关键! </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 检查数组边界 </div> <div class="ql-code-block"> if (left >= nums.length || nums[left] != target) { </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return left; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h2><strong style="color: rgb(51, 51, 51);">寻找右边界</strong></h2> <p><br></p> <p>也是两种写法,和上面寻找左边界一样</p> <p><br></p> <p>1)使用,左闭右开的形式</p> <p><strong>gift 演示</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608460212774109186/ek6g7hec.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int right_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length; </div> <div class="ql-code-block"> // 范围 [) </div> <div class="ql-code-block"> while (left < right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 能够找到边界的关键! </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (left == 0) return -1; </div> <div class="ql-code-block"> // 最后的情况应该是 left == right == 右边界的右边一个位置 </div> <div class="ql-code-block"> return nums[left - 1] == target ? (left - 1) : -1 ; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>2)使用,左闭右闭的形式</p> <p><br></p> <p>gift 演示</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1608460212774109186/s3mf8bw6.jpeg"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int right_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> // 搜索区间为 [left, right] </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 能够找到边界的关键! </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 这⾥改为检查 right 越界的情况,⻅下图 </div> <div class="ql-code-block"> if (right < 0 || nums[right] != target) { </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return right; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h1><strong style="color: rgb(51, 51, 51);">总结</strong></h1> <p><br></p> <p><br></p> <p>最后 labuladong 推荐的是<strong>左闭右闭</strong>的形式,我也感觉这种考虑的还算是比较少的</p> <p>只需要改 nums[mid] == target 条件下的情况 ,还有找哪一个返回哪一个并且判断哪一个的边界条件</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int binarySearch(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> return mid; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> // 当这个小于 target 的时候 </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int right_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> // 搜索区间为 [left, right] </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 能够找到边界的关键! </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 检查数组边界 </div> <div class="ql-code-block"> if (right < 0 || nums[right] != target) { </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return right; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> int left_bound(int[] nums, int target) { </div> <div class="ql-code-block"> int left = 0, right = nums.length - 1; </div> <div class="ql-code-block"> // 搜索区间为 [left, right] </div> <div class="ql-code-block"> while (left <= right) { </div> <div class="ql-code-block"> int mid = left + (right - left) / 2; </div> <div class="ql-code-block"> if (nums[mid] == target) { </div> <div class="ql-code-block"> // 能够找到边界的关键! </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] > target) { </div> <div class="ql-code-block"> right = mid - 1; </div> <div class="ql-code-block"> } else if (nums[mid] < target) { </div> <div class="ql-code-block"> left = mid + 1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> // 检查数组边界 </div> <div class="ql-code-block"> if (left >= nums.length || nums[left] != target) { </div> <div class="ql-code-block"> return -1; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return left; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> </div> </body> </html>
投递 4000+ 份,历时 4 个月,上岸啦~
<html> <head></head> <body> <div class="content ql-editor"> <p>我转正了~</p> <p><br></p> <p>大家好,我是皮蛋侠。</p> <p><br></p> <p>一个民办三本的可怜虫。自7月份毕业之后,一直在找工作。直到11月中旬。才找到一份相对来说过得去的工作。从毕业到工作,共投了4k+份简历。过程中,陆续又收到几家坑爹公司的offer,文章后面细说。</p> <p><br></p> <h2>我与星球的缘分</h2> <p><br></p> <p>我忘记我啥时候加入的星球了,大概是临近毕业那时候吧。springBoot囫囵吞枣学了一遍,springCloud似懂非懂。只知道有这个东西,不知道怎么运用。</p> <p><br></p> <p>在B站刷到鱼皮很多次,但一直不舍得加入星球。因为费用对于还是学生的我来说负担着实不轻。后面了解到就业形势,还是咬咬牙加了星球。</p> <p><br></p> <p>进来之后,用了一个多月的时间做了API项目,并且写了学习笔记。这些学习笔记获得了不少星球小伙伴的支持,给我的博客涨了不少的浏览量,这点虚荣心就写在简历了嘻嘻嘻。</p> <p><br></p> <p>后面又快速做了BI项目,相对于API项目来说,这个项目没有做的这么仔细。就是拉代码下了,看了一下大概,知道大步骤的逻辑就直接部署上线了。着急往简历上添项目,我简历上的项目太少了,能写的只一个API,还有我的毕设瑞吉外卖……</p> <p><br></p> <p>所以我现在有了3个项目了,API、BI、瑞吉外卖</p> <p><br></p> <h2>工作真难找啊</h2> <p><br></p> <p>前面说到我投了有4k+的简历,其中的前2k之前我投的简历都是石沉大海的,完全没有回应。后面就一边改简历一边投简历。现在的行情对没经验的应届生好像真的很不好,我觉得我简历写的东西都很足够了,但是在学校那关就被筛掉了,完全没有面试机会。</p> <p><br></p> <p>那段时间正好是春招时期,不管是从学校还是工作经验来看,我完全没有优势。大学生太多了,虽然说是春招,但是用我自身的亲身经历来说,三本的学生是真的没有校招的。</p> <p><br></p> <p>三本的学生大多是大四一整个学年都出去实习,那我为什么会工作经验上也没有优势呢?因为我只有4个月的实习经历,我考研了,全职考研,而且落榜了。对,送人头了,给新冠送了人头……考试前一天开始发烧,已经不知道烧到多少度了,所幸我从考场里走着出来的,没有被人抬着出来。</p> <p><br></p> <p>一直投简历,一直没有回音。这样的情况一直持续到9月底10月份的时候。我开始陆续收到面试。</p> <p><br></p> <p>牛马公司列表:</p> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>我收到的第一个面试,我准备了很充分,就连在地铁上都还在背面试题!我坐了3个小时的地铁,转半个小时公交,面试半个小时,再原路返回。 公司在一个很偏的地方,是一个财务接待的我。对,财务,管钱的。问我要了双证。看过之后,让我自我介绍,看我简历。我跟他说了API、BI项目。后来知道,他不懂技术,完全不知道我在说什么。 然后老总回来了,领我到老总那里去。再来一次,介绍,我说项目,说技术,老总听不懂。而且这个老总很不尊重人,他明显听不懂我再说什么,他也知道我知道他听不懂,所以他干脆不听了,我在那里说,他在那里干他的事情。“老总,我说完。”,“哦!好,那你能自己独自完成一个项目吗?我们公司的项目”。 我“???”。然后他去电话去了。 回来的时候,跟我说:“你先回去吧,我过两天再让我们公司管技术的面一下你。” 马勒戈壁的你煞笔吧?你们不懂技术,技术主管又不在,让我过来面试个什么几把? 过了几天,我:“你好,请问我什么时候二面呢?”。“过两天吧,已经帮你约了技术主管了”。 过了几天,“你好,不好意思啊,技术主管说还是想要有经验的人尽来,你刚毕业可能有很多事情处理不好。” MD??。</li> <li data-list="ordered"><span class="ql-ui"></span>我收到的第二个面试,是南沙的一家牛马公司,专门招牛马。过去先做几个小时面试题,前后端都要写。题目内容是直接从0到1做一个业务的demo。当天下午还有5、6个人跟我一起从2点到6点……小公司,5k,后来面试题不想做了,没下文。</li> <li data-list="ordered"><span class="ql-ui"></span>之后的面试也都是一些小公司,有一个共通点:<strong>钱少、活多、骗人</strong>。 我挑一个最经典的说说,是我的第一个offer。待遇:番禺区,大小周,6.5k。 为什么说他经典呢?BOSS上写着双休,9~13k。而且没有在BOSS上给我发面试邀请,而是直接打电话让我过去的,电话沟通后,短信给我发时间地点。 先说明,这是一个很危险的行为!没有收到正规面试最好不要去,我当时是找了很久工作没offer没有办法了,着急了。而且我去面试的时候跟我好几个朋友说过,发了定位,规定时间和当天晚上两个时间段没报平安就报警。 一进去给我的感觉就是小,压抑!得知是大小周后8~9k,技术面之后直接见老板,老板各种画饼,公司虽然小,属于起步阶段,你们现在进来都是老员工,后面你们入股一起当老板也可以啊。 听到这里我就感觉不妙了,这个老板是个傻逼,画饼都不会画。后老板开价6.5k,大小周,五险,但没一金,自带电脑。他说大家都是这个价,我们公司有个拿了国家奖金几十万的来我们这也是这个价,后面可以看表现再涨…… 后来回家后面试官说帮我争取到了7k,因为实在是没offer了,就想着要不要去试试。 接着就跟老板电话聊细节,结果老板说,不是7k,还是6.5k,后面看你工作表现才给你涨…… 没办法,先答应着吧。然后进了群,一周后入职。 进去的第二天,就有人在群里@我干活了,让我去做测试的活……我说我一周后才入职。 然后我退群了,已经完全不想在这家公司呆了。</li> <li data-list="ordered"><span class="ql-ui"></span>第二个offer!退群当天,我接到一个面试,报价9k。当场给offer,单休,8k,五险,同样没有一金。因为跟面试官挺聊得来的,所以他好像还对我挺满意的,因为是单休,所以没去。他还挽留了我好多次。</li> <li data-list="ordered"><span class="ql-ui"></span> 第三个offer!也是我现在就职的公司,是双休的,其他待遇就不方便说了。当时面试的时候面试官也对我挺满意的,然后第二天我就催他要结果,说有三家公司同时催我入职,但是我比较倾向于他们公司!然后把截图给他看(本来offer2的公司我还在犹豫的,但是他们一直催我入职,催得紧,所以我就拒绝了)。 听到我这么说之后,当天下午给我发了offer。一直到现在,过了试用期才敢出来分享我的经历。公司同事都挺好的,没有那种勾心斗角,领导也好。只不过是个养老公司,忙的时候很忙,闲的时候也很闲。没用到微服务,感觉技术上不会有太多进步。</li> </ol> <p><br></p> <p>从7月份毕业,到11月中旬,历时4个半月!终于找到工作了!</p> <p><br></p> <h2>经验总结</h2> <p><br></p> <p>我感觉吧,先就业再择业是骗人的。第一份工作往往对以后的影响会很大。个人建议,第一份工作千万不要随随便便就签了。现在回想起来,如果当时还差点去了第一个offer的??公司,真想给自己两个大逼兜!现在??公司真的很多!非常多!!多到你想象不到的情况!!!花半个人的钱,想请一个干三个人活的牛马!</p> <p><br></p> <p>如果你是第一份工作,就算找不到合适工作的话,要不再坚持坚持?我也好几次想放弃,回老家了。甚至我家里人还帮我找了销售的工作(其实就是回老家卖楼)。但是我是真的热爱编程,好在还是坚持过来了。是轻舟已过万重山吗?不!是才刚刚有了上船的资格,而且船还没开始行驶!</p> <p><br></p> <p>当时找不到工作真的很焦虑,经常一两天睡不着觉,48小时没合过眼。不是不想睡,是压根睡不着,压力非常大。从床上整晚躺倒天亮,硬是没睡着。</p> <p><br></p> <p>如果你也热爱编程,如果你也想放弃了,要不,再坚持坚持?起码跟自己说再给自己一次机会?其实用完这次机会再放弃的话也不会晚。</p> <p><br></p> <p>当然只针对热爱编程的人,如果是对编程没什么兴趣的,还以为写代码是高薪工作的,就没必要继续下去了。就算比其他工作高一点点,也是用加班用身体换的,赚来的迟早要在医院花掉,而且还有35岁危机。当然你会说其他工作也有,但是其他工作的35岁跟计算机的35岁是同一个概念吗?、</p> <p><br></p> <p>要不,再坚持坚持吧,哪怕就一次吧?哪怕再面试一个公司再放弃吧?</p> </div> </body> </html>
软件设计师
<html> <head></head> <body> <div class="content ql-editor"> <blockquote> <p>这是我个人根据B站上up主:<a href="https://space.bilibili.com/91286799" target="_blank">zst_2001</a> 的软件设计师视频备考所总结的笔记足足有5万字,相较于笔记,我个人更认为是讲义,因为这个笔记所包含的都是他在视频中的几乎所有的知识,不可谓不全面,也得到了zst本人的肯定。希望大家都能顺利通过软考! @author :xzy 个人网站:www.xzynet.com.cn</p> </blockquote> <h2>第一章 计算机系统</h2> <h3>计算机系统基础知识</h3> <h4>计算机系统硬件基本组成</h4> <p>计算器的基本硬件系统由<strong>运算器、控制器、存储器、输入设备、输出设备</strong>的5大部件组成。</p> <h4>中央处理单元</h4> <p>中央处理单元(CPU)是计算机系统的核心部件,它负责获取程序指令、对指令进行译码并加以执行。</p> <h5>1. CPU的功能</h5> <ol> <li data-list="bullet"><span class="ql-ui"></span>程序控制(控制器)</li> <li data-list="bullet"><span class="ql-ui"></span>操作控制(控制器)</li> <li data-list="bullet"><span class="ql-ui"></span>时间控制(控制器)</li> <li data-list="bullet"><span class="ql-ui"></span>数据处理(运算器)</li> </ol> <h5>2. CPU的组成</h5> <p><strong>CPU主要由运算器、控制器、寄存器组和内部总线等部件组成。</strong></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>运算器:运算器只能完成运算。 <ul> <li data-list="ordered"><span class="ql-ui"></span>算术逻辑单元(ALU):负责处理数据,实现对数据的算数运算和逻辑运算</li> <li data-list="ordered"><span class="ql-ui"></span>累加寄存器(AC):运算器的算数逻辑单元执行算数或逻辑运算时,为ALU提供一个工作区,运算的结果存储在AC中。</li> <li data-list="ordered"><span class="ql-ui"></span>数据缓冲寄存器(DR):作为CPU和内存、外部设备之间的中转站。</li> <li data-list="ordered"><span class="ql-ui"></span>状态条件寄存器(PSW):保存各种条形码内容。</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>控制器:控制器用于控制整个 CPU 的工作,它决定了计算机运行过程的自动化。它不仅要保证程序的正确执行,而且要能够处理异常事件。 <ul> <li data-list="ordered"><span class="ql-ui"></span>指令寄存器(IR):当CPU执行指令时,先把它从内存存储器取到缓冲寄存器中,在送入IR暂存。对用户完全透明</li> <li data-list="ordered"><span class="ql-ui"></span><strong>程序计数器(指令计数器)(PC)</strong>:跟踪指令的地址。PC在顺序执行的时候加1,在转移执行的时候加上一个位移量</li> <li data-list="ordered"><span class="ql-ui"></span>地址寄存器(AR):保存当前CPU所访问的内存单元的地址</li> <li data-list="ordered"><span class="ql-ui"></span>指令译码器(ID):指令包含操作码和地址码。对指令中的操作码进行分析解释</li> </ul></li> </ol> <p><strong>补充:指令 == 操作码 + 地址码</strong></p> <h4>计算机基本单位</h4> <p>| 位(比特) 最小数据单位 | <strong>bit、b</strong> | 8b=1B | | :---------------------: | :--------: | :----------: | | 字节 最小存储单位 | byte、B | 1B = 8b | | 千字节 | KB | 1KB = 1024B | | 兆字节 | MB | 1MB = 1024KB | | 吉字节 | GB | 1GB = 1024MB | | 太字节 | TB | 1TB = 1024GB |</p> <h4>数据表示</h4> <p>| 进制 | 英文简写 | | :------: | :------: | | 二进制 | B | | 八进制 | O | | 十进制 | D | | 十六进制 | H |</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>按权展开求和</strong>:n进制 ————> 十进制 每一位八进制数与三位二进制数对应</li> <li data-list="bullet"><span class="ql-ui"></span><strong>除n取余法</strong>:十进制 ————> n进制 每一位十六进制数与四位二进制数对应</li> <li data-list="bullet"><span class="ql-ui"></span><strong>进制加减法</strong>:加法:逢n进制进一 减法:借一当n</li> <li data-list="bullet"><span class="ql-ui"></span><strong>原码</strong>:总共有n位。最高位是符号位,0表示正号,1表示负号。其余n - 1为位表示数值的绝对值。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>反码</strong>:正数的反码与原码相同,负数的反码则是其绝对值按位求反。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>补码</strong>:正数的补码与其原码和反码相同,负数的补码则是等于其反码的末位加1。补码可以简化计算机运算部件的设计</li> <li data-list="bullet"><span class="ql-ui"></span><strong>移码</strong>:在补码的基础上取反符号位。</li> <li data-list="bullet"><span class="ql-ui"></span>补充 <ol> <li data-list="bullet"><span class="ql-ui"></span>正数的原码补码和反码都相同</li> <li data-list="bullet"><span class="ql-ui"></span>补码再取补码等于源码</li> <li data-list="bullet"><span class="ql-ui"></span>±0的补码、移码相同</li> </ol></li> <h4>各种码制带符号数的范围</h4> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/fzw1fkei.jpeg" alt="image.png"></p> <h4>浮点数</h4> <p>浮点数使用两个定点数来分别表示实数的尾数(F)和阶码(E)。其一般形式为:N = 2^E * F</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>阶码为带符号的纯整数,尾数为带符号的纯小数。</li> <li data-list="bullet"><span class="ql-ui"></span>一个数的浮点表示不是唯一的。小数点位置改变,阶码也随着相应改变。</li> <li data-list="bullet"><span class="ql-ui"></span>浮点数所能表示的数值范围主要由阶码决定,所表示数值的精度则由尾数决定。</li> <li data-list="bullet"><span class="ql-ui"></span>阶数不一样,先对阶小阶向大阶对,浮点数右移</li> <li data-list="bullet"><span class="ql-ui"></span>规格化就是将尾数的绝对值限定在区间[0.5,1]</li> <li data-list="bullet"><span class="ql-ui"></span>一般浮点数阶码用R位的移码表示,尾数用M位的补码表示。这种表示的数值范围为:</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/zlst9zbs.jpeg" alt="image.png"></p> <h4>寻址</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>立即寻址</strong>:操作数就包含在指令中。(最快)</li> <li data-list="bullet"><span class="ql-ui"></span><strong>寄存器寻址</strong>:操作数存放在某一寄存器中,指令中给出存放操作数的寄存器名。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>直接寻址</strong>:操作数存放在内存单元中,指令中直接给出操作数所在存储单元的地址。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>寄存器间接寻址</strong>:操作数存放在内存单元中,操作数所在存储单元的地址在某个寄存器中。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>间接寻址</strong>:指令中给出操作数地址的地址。(最慢)</li> <li data-list="bullet"><span class="ql-ui"></span>相对寻址:指令地址码给出的是一个偏移量(可正可负),操作数地址等于本条指令的地址加上该偏移量。</li> <li data-list="bullet"><span class="ql-ui"></span>变址寻址:操作数地址等于变址寄存器的内容加偏移量。</li> <li data-list="bullet"><span class="ql-ui"></span>补充 <ol> <li data-list="bullet"><span class="ql-ui"></span>指令系统中采用不同的寻址方式的目的是:扩大寻址空间并提高编程灵活性</li> </ol></li> <h4>校验码</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>奇偶校验码 <ul> <li data-list="ordered"><span class="ql-ui"></span>只能检错,不能纠错,码距 = 2。</li> <li data-list="ordered"><span class="ql-ui"></span>只能检测出奇数个数据位出错,不能检测偶数个数据位出错</li> <li data-list="ordered"><span class="ql-ui"></span>水平奇偶校验码、垂直奇偶校验码、水平垂直奇偶校验码。</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>海明码 <ul> <li data-list="ordered"><span class="ql-ui"></span>海明码可以检错也可以纠错。</li> <li data-list="ordered"><span class="ql-ui"></span>码距 = 2 ,检错能力 码距 >= 3,才有可能有纠错能力</li> <li data-list="ordered"><span class="ql-ui"></span>数据位是n,校验位是k,则n和k必须满足一下关系: 2^<em>k</em> − 1≥ <em>n</em>+k</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>循环冗余校验码(CRC) <ul> <li data-list="ordered"><span class="ql-ui"></span>可以检错,但不能纠错,码距 = 2。</li> <li data-list="ordered"><span class="ql-ui"></span>k个数据位后跟r个校验位</li> <li data-list="ordered"><span class="ql-ui"></span>校验位r采用模2运算</li> </ul></li> </ol> <h3>计算机体系结构</h3> <h4>CISC 和 RISC</h4> <p>| | RISC精简指令集计算机 | CISC复杂指令集计算机 | | :--------------: | :------------------------------: | :------------------: | | 指令种类 | 少、精简 | 多、丰富 | | 指令复杂度 | 低(简单) | 高(复杂) | | 指令长度 | 固定 | 变化 | | 寻址方式 | 少 | 复杂多样 | | 实现(译码)方式 | 硬布线控制逻辑(组合逻辑控制器) | 微程序控制技术 | | 通用寄存器数量 | 多、大量 | 一般 | | 流水线技术 | 支持 | 不支持 |</p> <h4>流水线</h4> <p>计算机中的流水线技术是把一个重复的过程分解为若干个子过程,每个子过程与其他子过程并行进行。</p> <p>若要执行n条指令:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>顺序执行总时间:顺序执行总时间 = 单条指令执行的时间 × n</li> <li data-list="bullet"><span class="ql-ui"></span>流水线执行总时间: <ol> <li data-list="bullet"><span class="ql-ui"></span>流水线执行总时间 = 一条指令执行的时间 + 流水线周期 × (<em>n</em>−1)</li> <li data-list="bullet"><span class="ql-ui"></span>流水线(操作)周期为执行时间最长的一段操作的时间。</li> </ol></li> <li><p>连续输入<em>n</em>条指令的吞吐率:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>吞吐率 = n ÷ 总执行时间</li> <li data-list="bullet"><span class="ql-ui"></span>如果是流水线的吞吐率,则总执行时间为流水线执行总时间。 <strong>流水线的吞吐率是最长流水段操作时间的倒数</strong>。即:最长流水段操作时间 = 流水线执行总时间 ÷ n</li> </ol></li> <li><p>加速比:</p> <p>加速比 = 顺序执行总时间 ÷ 流水线执行总时间</p></li> <h4>存储系统</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>SRAM(静态随机存储器):构成Cache(缓存)</li> <li data-list="ordered"><span class="ql-ui"></span>DRAM(动态随机存储器):构成主存 DRAM需要周期性地刷新保持信息。</li> <li data-list="ordered"><span class="ql-ui"></span>RAM(读/写存储器)</li> <li data-list="ordered"><span class="ql-ui"></span>ROM(只读存储器)</li> <li data-list="ordered"><span class="ql-ui"></span>PROM(可编程的只读存储器)</li> </ol> <ol> <li data-list="bullet"><span class="ql-ui"></span>按存储器所处位置可分为: <ol> <li data-list="bullet"><span class="ql-ui"></span>内存(主存):在主机内或主板上,存放机器当前运行所需的程序和数据,以便向CPU提供信息。(相对外存)容量小、速度快。</li> <li data-list="bullet"><span class="ql-ui"></span>外存(辅存):存放当前不参加运行的大量信息,在需要时调入内存。</li> </ol></li> <li><p>按存储器的构成材料分类:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>磁存储器</li> <li data-list="bullet"><span class="ql-ui"></span>半导体存储器</li> <li data-list="bullet"><span class="ql-ui"></span>光存储器</li> </ol></li> <li><p>按存储器工作方式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>读/写存储器(RAM)。</li> <li data-list="bullet"><span class="ql-ui"></span>只读存储器:ROM、PROM、EPROM、EEPROM等。 <ol> <li data-list="bullet"><span class="ql-ui"></span>固定只读存储器(ROM):厂家生产时就写好数据在其中。只能读(用户)不能写。一般用于存放BIOS和微程序控制。</li> <li data-list="bullet"><span class="ql-ui"></span>可编程读只读存储器(PROM):其内容可以由用户一次性地写入,写入后不能再修改。</li> <li data-list="bullet"><span class="ql-ui"></span>EPROM、EEPROM是指带电可擦可编程只读存储器。</li> </ol></li> </ol></li> <li><p>按访问方式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>按地址访问: <ol> <li data-list="bullet"><span class="ql-ui"></span>随机存储器(RAM)</li> <li data-list="bullet"><span class="ql-ui"></span>顺序存储器(SAM)</li> <li data-list="bullet"><span class="ql-ui"></span>直接存储器(DAM)</li> </ol></li> <li><p>按内容访问:例如相联存储器。</p></li> </ol></li> <li><p>高速缓存(Cache)用来存放当前最活跃的程序和数据,其特点是:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>位于CPU与主存之间;容量一般在几千字节到几兆字节之间;</li> <li data-list="bullet"><span class="ql-ui"></span>速度一般比主存快5~10倍,由快速半导体存储器构成;</li> <li data-list="bullet"><span class="ql-ui"></span>其内容是主存局部域的副本,<strong>对程序员来说是透明的。</strong></li> </ol></li> <p>Cache存储器部分用来存放主存的部分拷贝(副本)信息。控制部分的功能是判断CPU要访问的信息是否在Cache存储器中,若在即为命中,若不在则没有命中。命中时直接对Cache存储器寻址;未命中时,要按照替换原则决定主存的一块信息放到Cache存储器的哪一块里。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>补充内容 <ol> <li data-list="bullet"><span class="ql-ui"></span>虚拟存储器由主存与辅存组成。</li> <li data-list="bullet"><span class="ql-ui"></span>闪存(flsah)类似U盘,掉电后信息不会丢失。以块为单位进行删除。闪存是EPROM的一种类型,可以代替ROM存储器。闪存不可以代替主存。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>Cache 与主存地址的映射是由硬件自动完成的</strong></li> <li data-list="bullet"><span class="ql-ui"></span>数据总线:<strong>数地控</strong>即数据总线、地址总线和控制总线</li> <li data-list="bullet"><span class="ql-ui"></span>发生块冲突从多到少的顺序:直接映像 > 组相联映像 > 全相联映像。</li> </ol></li> <h4>中断</h4> <p>计算机在执行程序过程中,当遇到急需处理的事件时,暂停当前正在运行的程序,转去执行有关服务程序,处理完后自动返回源程序,这个过程称为中断。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>中断向量:提供中断服务程序的入口地址</li> <li data-list="bullet"><span class="ql-ui"></span>中断响应时间:发出中断请求开始,到进入中断服务程序</li> <li data-list="bullet"><span class="ql-ui"></span>保存现场:返回执行源程序</li> <li data-list="bullet"><span class="ql-ui"></span>多级中断使用堆栈来保护现场最有效</li> </ol> <h4>输入输出(I/O)控制方式</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>程序查询方式 <ul> <li data-list="ordered"><span class="ql-ui"></span><strong>CPU和 I/O(外设)只能串行工作</strong> ,CPU需要一直轮询检查,长期处于忙等状态。CPU 利用率低 <ul> <li data-list="ordered"><span class="ql-ui"></span>一次只能读/写一个字</li> <li data-list="ordered"><span class="ql-ui"></span>由 CPU 将数放入内存 <img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/kuk112y1.jpeg" alt="image.png"> 中断驱动方式</li> </ul></li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>I/O 设备通过中断信号主动向 CPU 报告 I/O 操作已完成 <ul> <li data-list="ordered"><span class="ql-ui"></span><strong>CPU 和 I/O(外设)可并行工作</strong></li> <li data-list="ordered"><span class="ql-ui"></span>CPU 利用率得到提升</li> <li data-list="ordered"><span class="ql-ui"></span>一次只能 读/写 一个字</li> <li data-list="ordered"><span class="ql-ui"></span>由 CPU 将数据放入内存</li> </ul></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/3qomf34q.jpeg" alt="image.png"> 3. 直接存储器方式(DMA)</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>CPU 和 I/O(外设)可并行工作</li> <li data-list="bullet"><span class="ql-ui"></span>仅在传送数据块的开始和结束时才需要 CPU 的干预</li> <li data-list="bullet"><span class="ql-ui"></span>由外设直接将数据放入内存</li> <li data-list="bullet"><span class="ql-ui"></span>一次读写的单位为”块“而不是字</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/1zyzv48t.jpeg" alt="image.png"></p> <h4>总线</h4> <p>总线是连接计算机有关部件的一组信号线,是计算机中用来传送信息代码的公共通道。</p> <p>采用总线结构主要有以下优点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>简化系统结构,便于系统设计制造;</li> <li data-list="bullet"><span class="ql-ui"></span>大大减少了连线数目,便于布线,减小体积,提高系统的可靠性;</li> <li data-list="bullet"><span class="ql-ui"></span>便于接口设计,所有与总线连接的设备均采用类似的接口;</li> <li data-list="bullet"><span class="ql-ui"></span>便于系统的扩充、更新与灵活配置,易于实现系统的模块化;</li> <li data-list="bullet"><span class="ql-ui"></span>便于设备的软件设计;</li> <li data-list="bullet"><span class="ql-ui"></span>便于故障诊断和维修,同时也降低了成本。</li> </ol> <p>在计算机系统中采用总线结构,便于实现系统的积木化构造,同时可以减少信息传输线的数量。</p> <p>微机中的总线分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>数据总线</li> <li data-list="bullet"><span class="ql-ui"></span>地址总线</li> <li data-list="bullet"><span class="ql-ui"></span>控制总线</li> </ol> <p>常见总线:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>ISA总线。</li> <li data-list="bullet"><span class="ql-ui"></span>EISA总线。</li> <li data-list="bullet"><span class="ql-ui"></span>PCI总线:PCI总线是目前微型机上广泛采用的<strong>并行内总线。</strong></li> <li data-list="bullet"><span class="ql-ui"></span>PCI Express 总线。</li> <li data-list="bullet"><span class="ql-ui"></span>前端总线。</li> <li data-list="bullet"><span class="ql-ui"></span>RS-232C。</li> <li data-list="bullet"><span class="ql-ui"></span>SCSI总线:小型计算机系统接口(SCSI)是一条<strong>并行外总线</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>SATA。</li> <li data-list="bullet"><span class="ql-ui"></span>USB。</li> <li data-list="bullet"><span class="ql-ui"></span>IEEE-1394。</li> <li data-list="bullet"><span class="ql-ui"></span>IEEE-488总线。</li> </ol> <h4>加密技术和认证技术</h4> </ol> </ol> </ol> </ol> </ol> <ul> <li>加密技术: <ol> <li data-list="ordered"><span class="ql-ui"></span>对称加密(私有密钥加密) 加密和解密是同一把密钥,只有一把密钥 密钥分发有缺陷 1. 加密解密速度很快 2. 适合加密大量明文数据</li> <li data-list="ordered"><span class="ql-ui"></span>非对称密钥(公开密钥加密) 加密和解密不是同一把密钥,一共有两把密钥 分别是公钥和私钥 用公钥加密只能用私钥解密 用私钥加密只能用公钥解密 不能通过一把推出另一把 用接收方的公钥加密明文,可以实现防止窃听的效果 密钥分发没有缺陷 1.加密解密速度很慢</li> <li data-list="ordered"><span class="ql-ui"></span>混合加密</li> </ol></li> <li>认证技术: <ol> <li data-list="ordered"><span class="ql-ui"></span>摘要:将发送的明文进行Hash算法后得到的摘要放在密文后一起发送过去,与接收方解密后的明文进行相同的Hash算法得到的摘要进行对比,如果一致,则没有篡改,否则有篡改。</li> <li data-list="ordered"><span class="ql-ui"></span>数字签名:发送方用自己的私钥对摘要进行签名(加密)得到数字签名放在密文后一起发送过去。接收方用发送方的公钥对数字签名进行验证(解密)如果验证成功则该消息没有被假冒且不能否认,否则该消息的真实性为假冒发送。 <ul> <li data-list="ordered"><span class="ql-ui"></span>使用数字签名确保消息不可否认</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>数字证书:用户向CA(权威机构)机构申请数字证书,将个人信息和公钥发给CA机构,CA机构颁给用户数字证书,数字证书用CA的私钥进行签名(加密)用CA的公钥验证(解密)数字证书得到用户的公钥。 <ul> <li data-list="ordered"><span class="ql-ui"></span>使用数字整数确保用户身份</li> <li data-list="ordered"><span class="ql-ui"></span>用户通过CA的公钥验证CA的签名,确定网站的真伪</li> </ul></li> </ol></li> </ul> <h4>加密算法</h4> <p>| 对称密钥(私钥、私有密钥加密)算法(共享密钥加密算法) | 非对称密钥(公钥、公开密钥加密)算法 | | :----------------------------------------------------: | :----------------------------------: | | DES | RSA | | 3DES | ECC | | RC-5 | DSA | | IDEA | | | AES | | | RC4 | |</p> <p>Hash函数</p> <p>MD5 摘要算法(128位散列值)</p> <p>SHA-1 安全散列算法</p> <p><strong>认证是处理主动攻击</strong></p> <h4>可靠性</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>串联系统。假设一个系统由N个子系统组成,当且仅当所有子系统都能正常工作时系统才能正常工作,这样的系统称为串联系统。</li> </ol> <ol> <li data-list="bullet"><span class="ql-ui"></span><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/qnacz11w.jpeg" alt="image.png"></li> <li data-list="bullet"><span class="ql-ui"></span>系统可靠性 R = R₁R₂···Rn</li> </ol> <ol> <li>并联系统。假设一个系统由N个子系统组成,只要有一个子系统正常工作,系统就能正常工作,这样的系统称为并联系统。</li> </ol> <ol> <li data-list="bullet"><span class="ql-ui"></span><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/7kdxwh2d.jpeg" alt="image.png"></li> <li data-list="bullet"><span class="ql-ui"></span>系统可靠性 R = 1 - (1-R₁)(1-R₂)···(1-Rn)</li> </ol> <h3>补充列题说明</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>指令寄存器的位数取决于**<u>指令字长</u>**</li> <li data-list="bullet"><span class="ql-ui"></span>逻辑同或:<strong>相同为一,不同为零</strong></li> <li data-list="bullet"><span class="ql-ui"></span>逻辑异或:<strong>相同为零,不同为一</strong></li> <li data-list="bullet"><span class="ql-ui"></span>计算机采用分层存储体系的主要目的是为了解决:<strong><u>存储容量、成本和速度之间的矛盾</u></strong></li> <li data-list="bullet"><span class="ql-ui"></span>被动攻击:流量分析、会话拦截。 主动攻击:重放、IP地址欺骗、拒绝服务、系统干涉、修改数据命令</li> <li data-list="bullet"><span class="ql-ui"></span>CPU依据 <u><strong>指令周期的不同阶段</strong></u>来区分内存中以二进制编码形式存放的指令和数据</li> </ol> <p>| X | Y | 逻辑与 | 逻辑或 | 逻辑异或 | 逻辑同或 | | :--: | :--: | :----: | :----: | :------: | :------: | | 0 | 0 | 0 | 0 | 0 | 1 | | 0 | 1 | 0 | 1 | 1 | 0 | | 1 | 0 | 0 | 1 | 1 | 0 | | 1 | 1 | 1 | 1 | 0 | 1 |</p> <h2>第二章 程序设计语言</h2> <h3>程序设计语言的基本概念</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>解释器:翻译源程序时不生产独立的目标程序。 <ol> <li data-list="bullet"><span class="ql-ui"></span>解释程序和源程序要参与到程序的运行过程中。</li> </ol></li> <li><p>编译器:翻译时将源程序翻译成独立保存的目标程序。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>机器上运行的是与源程序等价的目标程序,源程序和编译程序都不再参与目标程序的运行过程。</li> </ol></li> <li><p>许多程序设计语言规定,程序中的数据必须具有类型,其作用是:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>便于为数据合理分配存储单元</li> <li data-list="bullet"><span class="ql-ui"></span>便于对参与表达式计算的数据对象进行检查</li> <li data-list="bullet"><span class="ql-ui"></span>便于规定数据对象的取值范围及能够进行的运算</li> </ol></li> <h3>程序设计语言的基本成分</h3> <h4>函数定义</h4> <p>函数的定义包括两部分:函数首部和函数体。函数的定义描述了函数做什么和怎么做。</p> <p>函数定义的一般形式为:</p> <blockquote> <p>返回值的类型 函数名(形式参数表) //函数首部 函数名(实参表);</p> <p>{</p> <p> 函数体;</p> <p>}</p> </blockquote> <p>(1)值调用。若实现函数调用时将实参的值传递给相应的形参, 则称为是传值调用。在这种方式下形参不能向实参传递信息。</p> <p>(2)引用调用。引用是 C++ 中引入的概念,当形式参数为引用类型时,形参名实际上是实参的别名,函数中对形参的访问和修改实际上就是针对相应实参所做的访问和改变。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>传值调用: <ol> <li data-list="bullet"><span class="ql-ui"></span>将实参的值传递给形参,实参可以是变量、常量和表达式。</li> <li data-list="bullet"><span class="ql-ui"></span>不可以实现形参和实参间双向传递数据的效果。</li> </ol></li> <li><p>传引用(地址)调用:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>将实参的地址传递给形参,形参必须有地址,实参不能是常量(值),表达式。可以实现形参和实参间双向传递数据的效果,即改变形参的值同时也改变了实参的值。</li> </ol></li> <h3>编译程序基本原理</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>编译方式:词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成</li> <li data-list="bullet"><span class="ql-ui"></span>解释方式:词法分析、语法分析、语义分析</li> <li data-list="bullet"><span class="ql-ui"></span>编译器和解释器都不可省略词法分析、语法分析、语义分析且顺序不可交换</li> <li data-list="bullet"><span class="ql-ui"></span>即词法分析、语法分析、语义分析是必须的。</li> <li data-list="bullet"><span class="ql-ui"></span>编译器方式中中间代码生成和代码优化不是必要,可省略。</li> <li data-list="bullet"><span class="ql-ui"></span>即编译器方式可以在词法分析、语法分析、语义分析阶段后直接生成目标代码</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/k02h1nyg.jpeg" alt="image.png"><strong>符号表</strong>:不断收集、记录和使用源程序中一些相关符号的类型和特征等信息,并将其存入符号表中。记录源程序中各个字符的必要信息,以辅助语义的正确性检查和代码生成。</p> <p>1)词法分析</p> <p>输入:源程序</p> <p>输出:记号流</p> <p>词法分析阶段的主要作用是:分析构成程序的<strong>字符及由字符按照构造规则构成的符号,是否符合程序语言的规定</strong>。</p> <p>2)语法分析</p> <p>输入:记号流</p> <p>输出:语法树(分析树)</p> <p>语法分析阶段的主要作用是:对各条<strong>语句的结构进行合法性分析</strong>,分析程序中的句子结构是否正确。</p> <p>3)语义分析</p> <p>输入:语法树(分析树)</p> <p>语义分析阶段的主要作用是进行<strong>类型分析和检查</strong></p> <p>语义分析阶段可以发现程序中<strong>所有的语法错误</strong></p> <p><strong>语义分析阶段不能发现程序中所有的语义错误</strong></p> <p>语义分析阶段可以<strong>发现静态语义错误</strong>,不能发现动态语义错误,动态语义错误运行时才能发现(eg:除数为0时只能在运行阶段检查出来)</p> <p>4)中间代码生成</p> <p>常见的中间代码有:后缀式、三地址码、三元式、四元式和树(图)等形式。</p> <p><strong>中间代码与具体的机器无关</strong>(不依赖具体的机器),可以将不同的高级程序语言翻译成同一种中间代码。</p> <p>中间代码可以跨平台。</p> <p>因为与具体的机器无关,使用中间代码有利于进行与机器无关的优化处理和提高编译程序的可移植性。</p> <p>6)目标代码生成</p> <p>目标代码生成阶段的工作与具体的机器密切相关</p> <p>寄存器的分配工作处于目标代码生成阶段</p> <h3>正规式</h3> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/xebs90jl.jpeg" alt="image.png"> ###有限自动机</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>有限自动机是词法分析</strong>的一个工具,它能正确地识别正规集</li> <li data-list="bullet"><span class="ql-ui"></span>确定的有限自动机( DFA ):对每一个状态来说识别字符后转移的状态是唯一的</li> <li data-list="bullet"><span class="ql-ui"></span>不确定的有限自动机( NFA ):对每一个状态来说识别字符后转移的状态是不唯一的</li> </ol> <h3>上下文无关文法</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>程序设计语言的绝大多数<strong>语法规则</strong>可以采用<strong>上下文无关文法</strong>进行描述。</li> <li data-list="bullet"><span class="ql-ui"></span>上下文无关文法属于乔姆斯基定义的2型文法。</li> </ol> <h3>中缀后缀表达式</h3> <p>中缀式:a ? b ===> 后缀式:ab? 后缀式转中缀式可以用:栈</p> <p>中间代码有多种形式,其中树与后缀表示形式适用于解释器,而编译器多采用与机器指令格式较接近的四元式形式。</p> <p>根据生成的语法树,按照不同的方式遍历即可生成形式不同的表达式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>中缀表达式:中序遍历(左-根-右);</li> <li data-list="bullet"><span class="ql-ui"></span>后缀表达式:后序便利(左-右-根)。</li> </ol> <blockquote> <p>逆波兰式其实就是后缀式。</p> </blockquote> <h2>第三章 数据结构</h2> <p>这章我没怎么看,大家自己补充一下</p> <h2>第四章 操作系统</h2> <h3>概述</h3> <p>计算机系统由两部分组成:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>硬件</strong></li> <li data-list="bullet"><span class="ql-ui"></span><strong>软件</strong></li> </ol> <p>通常把未配置软件的计算机称为裸机。</p> <p>操作系统目的是:为了填补人与机器之间的鸿沟,即建立用户与计算机之间的接口,而为裸机配置的一种系统软件。</p> <p>操作系统也包括了系统软件。</p> <p>操作系统在计算机系统中的地位:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/13ojjl4j.jpeg" alt="image.png"> 操作系统是用户与计算机之间的接口,它在计算机系统中占据重要而特殊的地位,所有其他软件,如编辑程序、汇编程序、编译程序、数据库管理系统等系统软件,以及大量的应用软件都是建立在操作系统基础上的,并得到它的支持和取得它的服务。</p> <h3>程序与进程</h3> <p>程序顺序执行时的主要特征包括:顺序性、封闭性和可再现性。</p> <p>程序并发执行时的主要特征包括:失去了程序的封闭性、程序和机器的执行程序的活动不再一一对应、并发程序之间的相互制约性。</p> <h3>三态模型</h3> <p>在多道程序系统中,进程在处理器上交替运行,状态也不断地发生变化,因此进程一般有3种基本状态:运行、就绪和阻塞。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>运行</strong>:当一个进程在处理机上运行时。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>就绪</strong>:一个进程获得了除处理机外的一切所需资源,一旦得到处理机即可运行(还未得到)。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>阻塞</strong>(等待或睡眠):一个进程正在等待某一事件发生而暂时停止运行,这时即使把处理机分配给进程也无法运行。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/er5jgss5.jpeg" alt="image.png"> | 进程 | CPU | 资源 | | :--: | :--: | :--: | | 运行 | √ | √ | | 就绪 | × | √ | | 阻塞 | × | × |</p> <h3>进程间的通信</h3> <p>在多道程序环境的系统中存在多个可以并发执行的进程,故进程间必然存在资源共享和相互合作的问题。进程通信是指各个进程交换信息的过程。</p> <h4>同步和互斥</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>同步</strong>:合作进程间的直接制约问题。进程间的同步:是指在系统中一些需要相互合作,协同工作的进程,这样的相互联系称为进程的同步。例如,进程A向缓冲区送数据,进程B从缓冲区取数据加工,当进程B要取数据加工时,必须是进程A完成了向缓冲区送数据的操作,否则进程B必须停下来等待进程A的操作结束。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>互斥</strong>:申请临界资源进程间的间接制约问题。进程间的互斥:是指系统中多个进程因争用临界资源而互斥执行。 <blockquote> 临界资源:在多道程序系统环境中,那些一次只能供一个进程使用的资源。如打印机、共享变量和表格等。 </blockquote></li> </ol> <p>临界区管理的原则:</p> <blockquote> <p>临界区:是进程中对临界资源实施操作的那段程序。</p> </blockquote> <p>对互斥临界区管理的4条原则如下:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>有空即进</strong>:当无进程处于临界区时,允许进程进入临界区,并且只能在临界区运行有限 的时间。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>无空则等</strong>:当有一个进程在临界区时,其他欲进入临界区的进程必须等待,以保证进程互斥地访问临界资源。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>有限等待</strong>:对于要求访问临界资源的进程,应保证进程能在有限的时间进入临界区,以免陷入“<strong>饥饿</strong>”状态。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>让权等待</strong>:当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入<strong>忙等</strong>状态</li> </ol> <h4>信号量机制</h4> <p>信号量机制是一种有效的进程同步与互斥工具。</p> <p>信号量机制主要有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>整型信号量</li> <li data-list="bullet"><span class="ql-ui"></span>记录型信号量</li> <li data-list="bullet"><span class="ql-ui"></span>信号量集机制</li> </ol> <p>整型信号量:</p> <p>信号量是一个整型变量,根据控制对象的不同被赋予不同的值。信号量分为如下两类:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>公用信号量:实现进程间的互斥,初值为<code>1</code>或资源的数目。</li> <li data-list="bullet"><span class="ql-ui"></span>私用信号量:实现进程间的同步,初值为<code>0</code>或某个正整数。</li> </ol> <p>信号量 S 的物理意义:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>S ≥ 0:表示某资源的可用数,此时<strong>有可用资源</strong>;</li> <li data-list="bullet"><span class="ql-ui"></span>S<0:则其绝对值表示阻塞队列中等待该资源的进程数,此时无可用资源,并且有进程被阻塞。</li> </ol> <h4>PV操作</h4> <p>PV操作:实现<strong>进程同步与互斥</strong>的常用方法。</p> <p>P操作和V操作是低级通信原语,在执行期间不可分割。其中:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>P操作</strong>(减):表示<strong>申请</strong>一个资源;定义:S := S−1(S表示信号量) <ol> <li data-list="bullet"><span class="ql-ui"></span>S ≥ 0:执行P操作的进程继续执行;</li> <li data-list="bullet"><span class="ql-ui"></span>S<0:无可用资源,置该进程为<strong>阻塞</strong>状态,并将其插入阻塞队列。</li> </ol></li> <li><p><strong>V操作</strong>(加):表示<strong>释放</strong>一个资源。</p> <p>定义:S := S+1</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>S ≥ 0:执行V操作的进程继续执行;</li> <li data-list="bullet"><span class="ql-ui"></span>S<0:表示释放前有程序被阻塞,从阻塞状态唤醒一个进程,并将其插入就绪队列,然后执行V操作的进程继续。</li> </ol></li> <blockquote> <p>P减V加,P进V出。</p> </blockquote> <p><strong>利用PV操作实现进程的互斥:</strong></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>令信号量<code>mutex</code>的初始值为1;</li> <li data-list="ordered"><span class="ql-ui"></span>进入临界区:执行P操作;</li> <li data-list="ordered"><span class="ql-ui"></span>推出临界区:执行V操作。</li> </ol> <p><strong>利用PV操作实现进程的同步:</strong></p> <p>实现进程的同步可用一个信号量与消息联系起来。</p> <p>信号量的值:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>为<code>0</code>:表示希望的消息未产生;</li> <li data-list="bullet"><span class="ql-ui"></span>非<code>0</code>:表示希望的消息已经存在。</li> </ol> <p>假定信号量S表示某条消息,进程可以:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>调用P操作:测试消息是否到达;</li> <li data-list="bullet"><span class="ql-ui"></span>调用V操作:通知消息已经准备好。</li> </ol> <h4>死锁</h4> <p>当有 n 个进程,m个资源,且每个进程所需要的资源数为k,并且系统采用的分配策略是轮流地为每个进程分配资源时,判断是否发生死锁的公式如下: $$ m >= n * (k-1)+1 $$ 死锁的处理策略主要有4种:鸵鸟策略(即不理睬策略)、预防策略、<strong>避免策略</strong>和检测与解除死锁。</p> <h4>进程资源图</h4> <p>做题方法:先分配,再申请</p> <h3>线程</h3> <blockquote> <p>传统进程有两个基本属性:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>可拥有资源的独立单位;</li> <li data-list="bullet"><span class="ql-ui"></span>可独立调度和分配的基本单位。</li> </ol> </blockquote> <p>引入线程的原因是,进程的系统必须付出较大的时空开销。引入线程后,将传统进程的两个基本属性分开:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>线程:作为调度和分配的基本单位;</li> <li data-list="bullet"><span class="ql-ui"></span>进程:作为独立分配资源的单位。</li> </ol> <p>线程是进程中的一个实体,是被系统独立分配和调度的基本单位。</p> <p>线程的特点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>线程基本上不拥有资源,只拥有一点运行中必不可少的资源(如程序计数器、一组寄存器和栈),它可与同属一个进程的其他线程共享进程所拥有的全部资源。</li> <li data-list="bullet"><span class="ql-ui"></span>线程也具有就绪、运行和阻塞3种基本状态。</li> <li data-list="bullet"><span class="ql-ui"></span>线程可创建另一个线程。</li> <li data-list="bullet"><span class="ql-ui"></span>同一个进程中的多个线程可并发执行。</li> </ol> <p>线程因其具有许多传统进程所具有的特性,故称为"轻型进程";而传统进程称为"重型进程"。</p> <p>线程分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>用户级线程(User-Level Threads):不依赖于内核,该类线程的创建、撤销和切换都不利用系统调用来实现;</li> <li data-list="bullet"><span class="ql-ui"></span>内核支持线程(Kernel-Supported Threads):依赖于内核,即无论是在用户进程中的线程,还是在系统中的线程,它们的创建、撤销和切换都利用系统调用来实现。</li> </ol> <p>某些系统同时实现了两种类型的线程。</p> <blockquote> <p>与线程不同的是,不论是系统进程还是用户进程,在进行切换时,都要依赖于内核中的进程调度。因此,不论是什么进程都是与内核有关的,是在内核支持下进行切换的。</p> </blockquote> <h3>存储管理</h3> <h4>程序局部性原理</h4> <p>程序在执行时将呈现出局部性规律,即在一段时间内,程序的执行仅局限于某个部分。相应地,它所访问的存储空间也局限于某个区域内。</p> <p>程序的局限性表现在以下两个方面:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>时间局限性</strong>: <ol> <li data-list="bullet"><span class="ql-ui"></span>如果程序中的某条指令一旦执行,则不久的将来该指令可能再次被执行;</li> <li data-list="bullet"><span class="ql-ui"></span>如果某个存储单元被访问,则不久以后该存储单元可能再次被访问。</li> </ol><p>产生时间局限性的典型原因是在程序中存在着<strong>大量的循环操作</strong>。</p></li> <li><p><strong>空间局限性</strong>:指一旦程序访问了某个存储单元,则在不久的将来,其附近的存储单元也最有可能被访问。</p> <p>即程序在一段时间内所访问的地址可能集中在一定的范围内,其典型原因为<strong>程序是顺序执行</strong>。</p></li> <h4>分页存储管理</h4> <p>分页原理:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>页</strong>:将一个进程的地址空间划分成若干个大小相等的区域,称为页。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>块</strong>(<strong>页框</strong>):将主存空间划分成与页相同大小的若干个物理块,称为块或页框。</li> </ol> <p>在为进程分配主存时,将进程中若干页分别装入多个不相邻接的块中。 地址结构: <img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/mgkji9hp.jpeg" alt="image.png"> 其中,页内地址是同一页(页号)中的偏移量。</p> <p>分页的过程是由操作系统完成的,对用户是透明的,所以用户不必关心分页的过程,其优点是能有效地提高主存利用率,其缺点是不易实现共享。</p> <h4>段页式存储管理</h4> <p>结合分页和分段存储管理方式,形成一种新的存储管理方式,即段页式存储管理。段页式系统有两种系统的优点。</p> <p>段页式系统的基本原理是:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>将整个主存划分成大小相等的存储块(页框)。</li> <li data-list="ordered"><span class="ql-ui"></span>将用户程序按程序的逻辑关系分为若干个段,并为每个段赋予一个段名。</li> <li data-list="ordered"><span class="ql-ui"></span>将每个段划分成若干页,以页框为单位离散分配。</li> </ol> <p>段页式地址空间的结构:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/iav8ezgz.jpeg" alt="image.png"></p> <h3>设备管理</h3> <h4>缓冲技术</h4> <p>缓冲技术可提高外设利用率,尽可能使外设处于忙状态。缓冲技术可以采用两种方式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>硬件缓冲:利用专门的硬件寄存器作为缓冲;</li> <li data-list="bullet"><span class="ql-ui"></span>软件缓冲:通过操作系统来管理的。</li> </ol> <h4>单缓冲</h4> <p>单缓冲工作过程图:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/w4entc7g.jpeg" alt="image.png"> 当第1块数据送入用户工作区后(进行数据处理),缓冲区是空闲的,可以传送第2块数据(输入)。即第1块数据的处理C1与第2块数据的输入T2是可以并行的,以此类推:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/jnyg2hcr.jpeg" alt="image.png"></p> <p>$$ (前提是c要小于T)计算公式为:(T + M)* n + c $$</p> <h4>磁盘调度算法</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>先来先服务</strong>(FCFS):根据进程请求访问<strong>磁盘的先后次序</strong>进行调度。 <ol> <li data-list="bullet"><span class="ql-ui"></span>优点:公平、简单,且每个进程的请求都能依次得到处理,不会出现某进程的请求长期得不到满足的情况。</li> <li data-list="bullet"><span class="ql-ui"></span>缺点:此算法由于未对寻道进行优化,致使平均寻道时间可能较长。</li> </ol></li> <li><p><strong>最短寻道时间优先</strong>(SSTF,最短移臂算法):该算法选择这样的进程,其要求访问的磁道与<strong>当前磁头所在的磁道距离最近</strong>,使得每次的寻道时间最短。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>优点:可能会出现饥饿现象。</li> <li data-list="bullet"><span class="ql-ui"></span>缺点:不能保证平均寻道时间最短。</li> </ol></li> <li><p><strong>扫描算法</strong>(SCAN,电梯调度算法):总是从磁头当前位置开始,沿磁头的移动方向去<strong>选择离当前磁头最近</strong>的那个柱面的请求。如果沿磁头的方向无请求访问时,就改变磁头的移动方向。</p> <p>在这种调度方法下磁头的移动类似于电梯的调度,所以它也称为电梯调度算法。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>优点:避免了饥饿现象的出现。</li> <li data-list="bullet"><span class="ql-ui"></span>缺点:当磁头刚从里向外移动过某一磁道时,恰有一进程请求访问此磁道,这时该进程必须等待,待磁头从里向外,再从外向里扫描完所有要访问的磁道后才处理该进程的请求,致使该进程的请求被严重地推迟。</li> </ol></li> <li><p><strong>单向扫描算法</strong>(CSCAN,循环扫描算法):为了减少上述SCAN缺点中存在的这种延迟,算法规定<strong>磁头只做单向移动</strong>。</p> <p>例如,只是自里向外移动,从当前位置开始沿磁头的移动方向去选择离当前磁头最近的那个柱面访问,如果沿磁头的方向无请求访问时,<strong>磁头立即返回到最里面</strong>的欲访问的柱面,再亦即将最小柱面号紧接着最大柱面号构成循环,进行循环扫描。</p></li> <p>总结:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>先来先服务(FCFS) :根据进程请求访问磁盘的先后次序进行调度。</li> <li data-list="ordered"><span class="ql-ui"></span>最短寻道时间优先(SSTF):该算法选择这样的进程,其要求访问的磁道与当前磁头所在的磁道距离最近,使得每次的寻道时间最短。</li> <li data-list="ordered"><span class="ql-ui"></span>扫描算法/电梯调度算法(SCAN):扫描算法不仅考虑到要访问的磁道与当前磁道的距离,更优先考虑的是磁头的当前移动方向。</li> <li data-list="ordered"><span class="ql-ui"></span>单向扫描调度算法(CSCAN):为了减少这种延迟,算法规定磁头只做单向移动。</li> </ol> <h3>文件管理</h3> <h4>双缓冲</h4> <p>双缓冲进一步加快I/O的速度,提高了设备的利用率。其工作基本过程是在设备输入时,先将数据输入到缓冲区1,装满后便转向缓冲2。</p> <p>双缓冲工作过程图:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/tvx8iep6.jpeg" alt="image.png"> 双缓冲的工作特点是,可以实现对缓冲中数据的输入T和提取M,与CPU的计算C,三者并行工作:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/21p6v2cs.jpeg" alt="image.png"></p> <p>$$ (前提是M +c <T)计算公式为:T * n + M + c $$</p> <h4>多级索引结构</h4> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/qh43duoy.jpeg" alt="image.png"></p> <h4>文件目录</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>文件控制块</strong>(FCB):用于文件的描述和控制的数据结构,实现了文件的“按名存取”。文件控制块至少要包括文件名和存放文件的物理地址。文件控制块也称为<strong>文件的说明</strong>或<strong>文件目录项</strong>(简称<strong>目录项</strong>)。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>文件目录</strong>:文件控制块的有序集合。即文件目录是由文件控制块组成的,专门用于文件的检索。</li> </ol> <h4>文件控制块</h4> <p>文件控制块中包含以下信息:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>基本信息类:例如文件名、文件的物理地址、文件长度和文件块数等。</li> <li data-list="bullet"><span class="ql-ui"></span>存取控制信息类:文件的存取权限。UNIX中,用户分成三类: <ol> <li data-list="bullet"><span class="ql-ui"></span>文件主用户</li> <li data-list="bullet"><span class="ql-ui"></span>同组用户</li> <li data-list="bullet"><span class="ql-ui"></span>一般用户</li> </ol><p>以上三类用户对文件的权限为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>读</li> <li data-list="bullet"><span class="ql-ui"></span>写</li> <li data-list="bullet"><span class="ql-ui"></span>执行</li> </ol></li> <li><p>使用信息类:文件建立日期、最后一次修改日期、最后一次访问的日期、当前使用的 信息(如打开文件的进程数、在文件上的等待队列)等。</p></li> <h4>目录结构</h4> <p>组织好文件的目录是设计文件系统的重要环节,文件目录结构的组织方式直接影响到文件的存取速度,关系到文件的共享性和安全性。</p> <p>常见的目录结构有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>一级目录结构</strong>:一级目录的整个目录组织是一个线性结构,在整个系统中只需建立一张目录表,系统为每个文件分配一个目录项。优点:结构简单;缺点:查找速度慢,不允许重名和不便于实现文件共享等。主要用在单用户环境中。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>二级目录结构</strong>:为了克服一级目录结构存在的缺点引入了二级目录结构。二级目录结构的组成为: <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>主文件目录</strong>(MFD):每个用户文件目录都占有一个目录项,其目录项中包括用户名和指向该用户目录文件的指针;</li> <li data-list="bullet"><span class="ql-ui"></span><strong>用户目录</strong>(UFD):由用户所有文件的目录项组成的。</li> </ol><p>优点:提高了检索目录的速度,较好地解决了重名问题。</p> <p>缺点:该结构虽然能有效地将多个用户隔离开(这种隔离在各个用户之间完全无关时是一个优点),但当多个用户之间要相互合作去共同完成一个大任务,且一个用户又需要去访问其他用户的文件时,这种隔离便成为一个缺点,因为这种隔离使诸用户之间不便于共享文件。</p></li> <li><p><strong>多级目录结构</strong>:在多道程序设计系统中常采用多级目录结构。</p> <p>多级目录结构是树型目录结构。从根结点向下,每一个结点是一个目录,叶结点是文件。</p> <p>在采用多级目录结构的文件系统中,用户要访问一个文件,必须指出文件所在的路径名:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>路径名</strong>:从某个目录开始到该文件的通路上所有各级目录名拼起来得到的。在各目录名之间、目录名与文件名之间需要用分隔符隔开。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>绝对路径名</strong>:指从根目录开始的完整路径。<strong>全文件名</strong>:指绝对路径名加上该文件的文件名。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>相对路径名</strong>:从当前所在目录开始到其他目录或文件的路径。</li> </ol></li> <h4>位示图</h4> <p>位示图是一种空闲空间管理方法。通过在外存上建立一张位示图,记录文件存储器的使用情况。</p> <p><strong>位示图用二进制的一位来表示一个物理块的使用情况:</strong></p> <ol> <li data-list="bullet"><span class="ql-ui"></span><code>0</code>:表示空闲;</li> <li data-list="bullet"><span class="ql-ui"></span><code>1</code>:表示占用。</li> </ol> <p>例如:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/1y712yhx.jpeg" alt="image.png"> 位示图的大小由磁盘空间的大小(物理块总数)决定。</p> <p>位示图的描述能力强,适合各种物理结构。</p> <p>做题技巧:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>块号从0开始,字号从1开始,公式:</li> </ol> <p>$$ (n-1)<em>32 ~~~ n</em>32-1 $$</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>块号从0开始,字号从0开始,公式:</li> </ol> <p>$$ n*32 ~~~(n+1)*32-1 $$</p> <h2>第五章 软件工程</h2> <h3>软件过程</h3> <h5>1. 能力成熟度模型(CMM)</h5> <p>CMM 将软件过程改进分为以下5个成熟度级别:</p> <p>1)初始级(最低成熟度)</p> <p>软件过程的特点是杂乱无章,有时甚至很混乱,几乎没有明确定义的步骤,<strong>项目的成功完全依赖个人的努力和英雄式核心人物的作用。</strong></p> <p>2)可重复级</p> <p>建立了基本的项目管理过程和实践来<strong>跟踪项目费用、进度和功能特性</strong>,有必要的过程准则来重复以前在同类项目中的成功。</p> <p>3)已定义级</p> <p>管理和工程两方面的<strong>软件过程已经文档化、标准化</strong>,并综合成整个软件开发组织的<strong>标准软件过程</strong>。</p> <p>4)已管理级</p> <p>制定了软件过程和产品质量的详细度量标准。<strong>软件过程的产品质量都被开发组织的成员所理解和控制。</strong></p> <p>5)优化级(最高成熟度)</p> <p>加强了定量分析,通过来自<strong>过程质量</strong>反馈和来自<strong>新观念、新技术</strong>的反馈使过程能不断持续地改进。</p> <h5>2. 能力成熟度模型集成(CMMI)</h5> <p>CMMI 提供了两种表示方法:</p> <p>1)阶段式模型</p> <p>阶段式模型的结构类似于 CMM,它关注组织的成熟度。</p> <p>有五个成熟度等级:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>初始的:过程不可预测且缺乏控制。</li> <li data-list="bullet"><span class="ql-ui"></span>已管理的:过程为项目服务。</li> <li data-list="bullet"><span class="ql-ui"></span>已定义的:过程为组织服务。</li> <li data-list="bullet"><span class="ql-ui"></span>定量管理的:过程已度量和控制。</li> <li data-list="bullet"><span class="ql-ui"></span>优化的:集中于过程改进。</li> </ol> <p>2)连续式模型</p> <p>连续式模型关注每个过程域的能力,一个组织对不同的过程域可以达到不同的过程域能力。</p> <p>CMMI 中包括6个过程域能力等级:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>CL₀(未完成的):过程域<strong>未执行</strong>或<strong>未得到</strong> CL₁ 中定义的所有目标。</li> <li data-list="bullet"><span class="ql-ui"></span>CL₁(已执行的):其共性目标是过程将可标识的输入工作产品转换成可标识的输出工作产品,以实现支持<strong>过程域的特定目标</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>CL₂(己管理的):其共性目标<strong>集中于己管理的过程的制度化</strong>。根据组织级政策规定过程的运作将使用哪个过程,项目遵循己文档化的计划和过程描述,所有正在工作的人都有权使用足够的资源,所有工作任务和工作产品都被监控、控制和评审。</li> <li data-list="bullet"><span class="ql-ui"></span>CL₃(己定义级的):其共性目标<strong>集中于己定义的过程的制度化</strong>。过程是按照组织的剪裁指南从组织的标准过程集中剪裁得到的,还必须收集过程资产和过程的度量,并用于将来对过程的改进。</li> <li data-list="bullet"><span class="ql-ui"></span>CL₄(定量管理的):<strong>其共性目标集中于可定量管理的过程的制度化</strong>。使用测量和质量保证来控制和改进过程域,建立和使用关于质量和过程执行的定量目标作为管理准则。</li> <li data-list="bullet"><span class="ql-ui"></span>CL₅(优化的):<strong>使用量化(统计学)手段改变和优化过程域</strong>,以满足客户要求的改变和持续改进计划中的过程域的功效。</li> </ol> <h3>软件过程模型</h3> <p>软件开发过程模型是指为了有效地开发、维护和更新软件系统,提出的一系列步骤、阶段和方法的系统框架,以实现提高软件质量、加快开发速度和降低开发成本的目的。</p> <p>常见的软件开发过程模型包括瀑布模型、增量模型、演化模型(原型模型、螺旋模型)和喷泉模型。</p> <h4>瀑布模型</h4> <p>瀑布模型是一种线性的软件开发过程模型,开发流程严格按照顺序依次进行,每个阶段都必须完成后才能进入下一个阶段。瀑布模型包括需求分析、设计、编码、测试和维护五个阶段。</p> <p>瀑布模型:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>优点: <ol> <li data-list="bullet"><span class="ql-ui"></span>容易理解、管理成本低</li> <li data-list="bullet"><span class="ql-ui"></span>每个阶段都有对应的成果产物</li> <li data-list="bullet"><span class="ql-ui"></span>各个阶段有明显的界限划分和顺序需求</li> <li data-list="bullet"><span class="ql-ui"></span>强调开发的阶段性早期计划及需求调查和产品测试</li> </ol></li> <li><p>缺点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>需要客户能够完整、正确和清晰地表达自己的需要</li> <li data-list="bullet"><span class="ql-ui"></span>在开始的两个或3个阶段中,很难评估真正的进度状态</li> <li data-list="bullet"><span class="ql-ui"></span>当接近项目结束时,出现了大量的集成和测试工作</li> <li data-list="bullet"><span class="ql-ui"></span>直到项目结束之前,都不能演示系统的能力</li> <li data-list="bullet"><span class="ql-ui"></span>一旦发生错误,整个项目要推到重新开始。</li> <li data-list="bullet"><span class="ql-ui"></span>需求或设计中的错误往往只有到了项目后期才能够被发现,对于项目风险的控制能力较弱,从而导致项目常常延期完成,开发费用超出预算</li> </ol></li> <p>瀑布模式适合用于:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>开发需求明确的,需求大致固定且不会随意变更的系统</li> <li data-list="bullet"><span class="ql-ui"></span>开发人员对软件的应用领域很熟悉</li> <li data-list="bullet"><span class="ql-ui"></span>开发工作对用户参与的要求很低</li> </ol> <h4>增量模型</h4> <p>增量模型采用了逐步完善的思路,将软件的开发过程划分为一个个的增量,每个增量都能够独立实现某一或多项功能或特性。在逐步实现的过程中,可以不断根据需求变化来进行迭代,从而保证最终的软件达到客户需求和期望。</p> <p>增量模型作为瀑布模型的一个变体,具有瀑布模型的所有优点。此外,它还有以下优点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>第一个可交付版本所需要的成本和时间很少</li> <li data-list="bullet"><span class="ql-ui"></span>开发由增量表示的小系统所承担的风险不大</li> <li data-list="bullet"><span class="ql-ui"></span>由于很快发布了第一个版本,因此可以减少用户需求的变更</li> <li data-list="bullet"><span class="ql-ui"></span>优先级高的功能先交付,使得重要的功能经历更多的测试</li> <li data-list="bullet"><span class="ql-ui"></span>运行增量投资,即在项目开始时,可以仅对一个或两个增量投资</li> </ol> <p>缺点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>如果没有对用户的变更要求进行规划,那么产生的初始增量可能会造成后来增量的不稳定</li> <li data-list="bullet"><span class="ql-ui"></span>如果需求不像早期思考的那样稳定和完整,那么一些增量就可能需要重新开发、重新发布</li> <li data-list="bullet"><span class="ql-ui"></span>管理发生的成本、进度和配置的复杂性可能会超出组织的能力</li> </ol> <p>量模型适合用于:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>需求尚不明确</li> <li data-list="bullet"><span class="ql-ui"></span>需要快速构造可运行的产品的项目(对完成期限严格要求的产品)适宜商业开发</li> <li data-list="bullet"><span class="ql-ui"></span>进行已有产品升级或新版本开发</li> <li data-list="bullet"><span class="ql-ui"></span>对所开发的领域比较熟悉而且已有原型系统。</li> </ol> <h4>演化模型</h4> <p>演化模型是迭代的过程模型,使得软件开发人员能够逐步开发出更完整的软件版本。演化模型特别适用于对软件需求缺乏准确认识的情况。典型的演化模型有原型模型和螺旋模型等。</p> <h5>原型模型</h5> <p>并非所有的需求都能够预先定义。大量的实践表明,在开发初期很难得到一个完整的、准确的需求规格说明。原因有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>客户往往不能准确地表达对未来系统的全面要求,导致形成的需求规格说明不完整、不准确,甚至是有歧义。</li> <li data-list="bullet"><span class="ql-ui"></span>在整个开发过程中,用户可能会产生新的要求,导致需求的变更。</li> </ol> <p>原型模型:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>适合于用户需求不清、需求经常变化的情况</li> <li data-list="bullet"><span class="ql-ui"></span>不适合大规模系统的开发</li> </ol> <p>原型的目的是能快速、低成本地构建原型系统。</p> <p>能够采用原型方法是因为开发工具的快速发展,使得能够迅速地开发出一个让用户看得见、摸得着的系统框架。这样,对于计算机不是很熟悉的用户就可以根据这个框架提出自己的需求。</p> <p>开发原型系统首先确定用户需求,开发初始原型,然后征求用户对初始原型的改进意见,并根据意见修改原型:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/0momdkmk.jpeg" alt="image.png"></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>交流:目的是定义软件的总体目标,标识需求,然后</li> <li data-list="ordered"><span class="ql-ui"></span>快速计划:快速制订原型开发的计划,确定原型的目标和范围</li> <li data-list="ordered"><span class="ql-ui"></span>采用快速设计方式进行建模</li> <li data-list="ordered"><span class="ql-ui"></span>构建原型</li> <li data-list="ordered"><span class="ql-ui"></span>部署交付和反馈:被开发的原型应交付给客户使用,并收集客户的反馈意见,这些反馈意见可在下一轮中对原型进行改进</li> <li data-list="ordered"><span class="ql-ui"></span>下一轮迭代:在前一个原型需要改进,或者需要扩展其范围的时候,进入下一轮原型的迭代开发</li> </ol> <p>根据使用原型的目的不同,原型可以分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>探索型原型:目的是要弄清目标的要求,确定所希望的特性,并探讨多种方案的可行性。</li> <li data-list="bullet"><span class="ql-ui"></span>实验型原型:目的是验证方案或算法的合理性,是在大规模开发和实现前,用于考查方案是否合适、规格说明是否可靠等。</li> <li data-list="bullet"><span class="ql-ui"></span>演化型原型:目的是将原型作为目标系统的一部分,通过对原型的多次改进,逐步将原型演化成最终的目标系统。</li> </ol> <h5>螺旋模型</h5> <p>对于复杂的大型软件,开发一个原型往往达不到要求。</p> <p>螺旋模型将瀑布模型和演化模型结合起来,加入了两种模型均忽略的风险分析(以风险为驱动),弥补了这两种模型的不足。</p> <p>螺旋模型将开发过程分为几个螺旋周期,每个螺旋周期大致和瀑布模型相符合:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/j07pgofn.jpeg" alt="image.png"> 螺旋模型属于面向对象开发模型。</p> <p>螺旋模型适用于:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>庞大、复杂并且具有高风险的系统;</li> <li data-list="bullet"><span class="ql-ui"></span>新近开发,需求不明的情况。</li> </ol> <p>优点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>支持用户需求的动态变化</li> <li data-list="bullet"><span class="ql-ui"></span>螺旋模型强调风险分析,使得开发人员和用户对每个演化层出现的风险有所了解,从而做出应有的反应。</li> <li data-list="bullet"><span class="ql-ui"></span>螺旋模型支持用户需求的动态变化,有助于提高软件的适应能力,降低了软件开发的风险。</li> </ol> <p>缺点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>需要开发人员具有相当丰富的风险评估经验和专门知识。</li> <li data-list="bullet"><span class="ql-ui"></span>过多的迭代次数会增加开发成本,延迟提交时间。</li> </ol> <h4>喷泉模型</h4> <p>喷泉模型:喷泉模型克服了瀑布模型不支持软件重用和多项开发活动集成的局限性</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>以用户需求为动力</li> <li data-list="bullet"><span class="ql-ui"></span>以对象作为驱动</li> <li data-list="bullet"><span class="ql-ui"></span>适合于面向对象</li> </ol> <p>泉模型使开发过程具有以下性质或特点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>迭代性</strong>:意味着模型中的开发活动常常需要重复多次,在迭代过程中不断地完善软件系统。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>无间隙性</strong>:指在开发活动(如分析、设计、编码)之间不存在明显的边界。喷泉不像瀑布模型那样,在需求分析活动结束后才开始设计活动,在设计活动结束后才开始编码活动,而是允许各开发活动交叉、迭代地进行。喷泉模型的各个阶段没有明显的界线,开发人员可以同步进行。</li> <li data-list="bullet"><span class="ql-ui"></span>支持软件重用。</li> </ol> <p>优点:可以提高软件项目的开发效率,节省开发时间。</p> <p>缺点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>由于喷泉模型在各个开发阶段是重叠的,在开发过程中需要大量的开发人员,不利于项目的管理。</li> <li data-list="bullet"><span class="ql-ui"></span>喷泉模型要求严格管理文档,使得审核的难度加大。</li> </ol> <h3>统一过程(UP)模型</h3> <p>| 阶段 | 里程碑 | 关注 | 产生 | | :------: | :----------: | :----------------: | :--------------------------------------: | | 初始阶段 | 生命周期目标 | 项目的初创活动 | 构想文档、业务用例、项目计划、风险评估 | | 精化阶段 | 生命周期架构 | 需求分析和架构演进 | 补充需求分析、软件架构描述、架构原型制品 | | 构建阶段 | 初始运作功能 | 系统的构建 | 具有最初运作能力的软件产品 | | 移交阶段 | 产品发布 | 软件提交方面的工作 | 产品发布版本 |</p> <h3>敏捷方法</h3> <p>敏捷方法是一种反应灵活、拥有高度互动性和以人为本的软件开发方法。它的核心是通过不断地交付成果和及时反馈,来满足客户需求和不断变化的业务环境。以下是敏捷方法中的一些常见实践:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>极限编程(XP)</li> <li data-list="bullet"><span class="ql-ui"></span>水晶法(Crystal)</li> <li data-list="bullet"><span class="ql-ui"></span>并列争求法(Scrum)</li> <li data-list="bullet"><span class="ql-ui"></span>自适应软件开发(ASD)</li> <li data-list="bullet"><span class="ql-ui"></span>敏捷统一过程(AUP)</li> </ol> <h4>极限编程(XP)</h4> <p>极限编程是为了降低需求变更所带来的成本,旨在提高软件质量和对客户需求变化的适应性,期望能够让软件开发达到低成本、低缺陷、高产出、高回报(最小投入得到最大结果)的效果。</p> <p>极限编程(XP)软件开发方式有以下性质:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>轻量级(敏捷)</li> <li data-list="bullet"><span class="ql-ui"></span>高效</li> <li data-list="bullet"><span class="ql-ui"></span>低风险</li> <li data-list="bullet"><span class="ql-ui"></span>柔性</li> <li data-list="bullet"><span class="ql-ui"></span>可预测的</li> <li data-list="bullet"><span class="ql-ui"></span>科学的</li> </ol> <p>XP由价值观、原则、实践和行为4个部分组成,他们之间彼此相互依赖、关联,并通过行为贯穿于整个生存周期:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>4大价值观: <ol> <li data-list="bullet"><span class="ql-ui"></span>沟通</li> <li data-list="bullet"><span class="ql-ui"></span>简单性</li> <li data-list="bullet"><span class="ql-ui"></span>反馈</li> <li data-list="bullet"><span class="ql-ui"></span>勇气</li> </ol></li> <li>5个原则: <ol> <li data-list="bullet"><span class="ql-ui"></span>快速反馈</li> <li data-list="bullet"><span class="ql-ui"></span>简单性假设</li> <li data-list="bullet"><span class="ql-ui"></span>逐步修改</li> <li data-list="bullet"><span class="ql-ui"></span>提倡更改</li> <li data-list="bullet"><span class="ql-ui"></span>优质工作</li> </ol></li> <li>12个最佳实践: <ol> <li data-list="bullet"><span class="ql-ui"></span>计划游戏:快速制定计划、随着细节的不断变化而完善</li> <li data-list="bullet"><span class="ql-ui"></span>小型发布:系统的设计要能够尽可能早地交付</li> <li data-list="bullet"><span class="ql-ui"></span>隐喻:找到合适的比喻传达信息</li> <li data-list="bullet"><span class="ql-ui"></span>简单设计:只处理当前的需求,使设计保持简单</li> <li data-list="bullet"><span class="ql-ui"></span>测试先行:先写测试代码,然后再编写程序</li> <li data-list="bullet"><span class="ql-ui"></span>重构:重新审视需求和设计,重新明确地描述它们以符合新的和现有的需求</li> <li data-list="bullet"><span class="ql-ui"></span>结对编程:非正式的代码审查,以获得质量更高的代码</li> <li data-list="bullet"><span class="ql-ui"></span>集体代码所有制:任何开发人员都可以对系统的任何部分进行改进</li> <li data-list="bullet"><span class="ql-ui"></span>持续集成:可以按日甚至按小时为客户提供可运行的版本</li> <li data-list="bullet"><span class="ql-ui"></span>每周工作40个小时</li> <li data-list="bullet"><span class="ql-ui"></span>现场客户:系统最终用户代表应该全程配合XP团队</li> <li data-list="bullet"><span class="ql-ui"></span>编码标准</li> </ol></li> <h4>水晶法(Crystal)</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>认为每一个不同的项目都需要一套不同的策略、约定和方法论。</li> <li data-list="bullet"><span class="ql-ui"></span>认为人对软件质量有重要的影响。随着项目质量和开发人员素质的提高,项目和过程的质量也随之提高。</li> <li data-list="bullet"><span class="ql-ui"></span>通过更好地交流和经常性的交付,软件生产力得到提高。</li> </ol> <h4>并列争求法(Scrum)</h4> <p>使用迭代的方法。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>把每30天一次的迭代称为一个“冲刺”。</strong></li> <li data-list="bullet"><span class="ql-ui"></span>按需求的优先级别来实现产品。</li> <li data-list="bullet"><span class="ql-ui"></span>多个自组织和自治的小组并行地递增实现产品。</li> <li data-list="bullet"><span class="ql-ui"></span>协调是通过简短的日常情况会议来进行,就像橄榄球中的“并列争球”。</li> </ol> <h4>自适应软件开发(ASD)</h4> <p>有6个基本原则:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>有一个使命作为指导;</li> <li data-list="bullet"><span class="ql-ui"></span>特征被视为客户价值的关键点;</li> <li data-list="bullet"><span class="ql-ui"></span>过程中的等待是很重要的,因此“重做”与“做”同样关键;</li> <li data-list="bullet"><span class="ql-ui"></span>变化不被视为改正,而是被视为对软件开发实际情况的调整;</li> <li data-list="bullet"><span class="ql-ui"></span>确定的交付时间迫使开发人员认真考虑每一个生产的版本的关键需求;</li> <li data-list="bullet"><span class="ql-ui"></span>风险也包含其中。</li> </ol> <h4>敏捷统一过程(AUP)</h4> <p>敏捷统一过程采用以下原理来构建软件系统:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>“在大型上连续”</li> <li data-list="bullet"><span class="ql-ui"></span>“在小型上迭代”。</li> </ol> <p>采用经典的UP阶段性活动(初始、精化、构建和转换),提供了一系列活动,能够使团队为软件项目构想出一个全面的过程流。</p> <p>在每个活动里,一个团队迭代使用敏捷,并将有意义的软件增量尽可能快地交付给最终用户。每个AUP迭代执行以下活动:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>建模</strong>:建立对商业和问题域的模型表述,这些模型“足够好”即可,以便团队继续前进。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>实现</strong>:将模型翻译成源代码。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>测试</strong>:像XP一样,团队设计和执行一系列的测试来发现错误以保证源代码满足需求。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>部署</strong>:对软件增量的交付以及获取最终用户的反馈。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>配置及项目管理</strong>:着眼于变更管理、风险管理以及对团队的任一制品的控制。项目管理追踪和控制开发团队的工作进展并协调团队活动。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>环境管理</strong>:协调标准、工具以及适用于开发团队的支持技术等过程基础设施。</li> </ol> <h3>软件需求</h3> <p>软件需求是指用户对目标软件系统在功能、行为、性能、设计约束等方面的期望。通常,这些需求包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>功能需求</strong>:考虑系统要做什么,在何时做,在何时以及如何修改或升级。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>性能需求</strong>:考虑软件开发的技术性指标。 <ol> <li data-list="bullet"><span class="ql-ui"></span>存储容量限制;</li> <li data-list="bullet"><span class="ql-ui"></span>执行速度;</li> <li data-list="bullet"><span class="ql-ui"></span>响应时间;</li> <li data-list="bullet"><span class="ql-ui"></span>吞吐量。</li> </ol></li> <li><p><strong>用户或人的因素</strong>:考虑用户的类型。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>各种用户对使用计算机的熟练程度,需要接受的训练;</li> <li data-list="bullet"><span class="ql-ui"></span>用户理解、使用系统的难度;</li> <li data-list="bullet"><span class="ql-ui"></span>用户错误操作系统的可能性。</li> </ol></li> <li><p><strong>环境需求</strong>:考虑未来软件应用的环境,包括硬件和软件。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>对硬件设备的需求包括:机型、外设、接口、地点、分布、湿度、磁场干扰等;</li> <li data-list="bullet"><span class="ql-ui"></span>对软件的需求包括:操作系统、网络、数据库等。</li> </ol></li> <li><p><strong>界面需求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>来自其他系统的输入;</li> <li data-list="bullet"><span class="ql-ui"></span>到其他系统的输出;</li> <li data-list="bullet"><span class="ql-ui"></span>对数据格式的特殊规定;</li> <li data-list="bullet"><span class="ql-ui"></span>对数据存储介质的规定。</li> </ol></li> <li><p><strong>文档需求</strong>:考虑需要哪些文档,文档针对哪些读者。</p></li> <li><p><strong>数据需求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>输入、输出数据的格式;</li> <li data-list="bullet"><span class="ql-ui"></span>接收、发送数据的频率;</li> <li data-list="bullet"><span class="ql-ui"></span>数据的准确性和精度;</li> <li data-list="bullet"><span class="ql-ui"></span>数据流量;</li> <li data-list="bullet"><span class="ql-ui"></span>数据需保持的时间。</li> </ol></li> <li><p><strong>资源使用需求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>软件运行时所需要的数据、其他软件、内存空间等资源;</li> <li data-list="bullet"><span class="ql-ui"></span>软件开发、维护时,所需的人力、支撑软件、开发设备。</li> </ol></li> <li><p><strong>安全保密要求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>是否需要对访问系统或系统信息加以控制;</li> <li data-list="bullet"><span class="ql-ui"></span>隔离用户数据的方法;</li> <li data-list="bullet"><span class="ql-ui"></span>用户程序如何与其他程序和操作系统隔离</li> <li data-list="bullet"><span class="ql-ui"></span>系统备份要求。</li> </ol></li> <li><p><strong>可靠性要求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>系统的可靠性要求;</li> <li data-list="bullet"><span class="ql-ui"></span>系统是否必须检测和隔离错误;</li> <li data-list="bullet"><span class="ql-ui"></span>出错后,重启系统允许的时间。</li> </ol></li> <li><p><strong>软件成本消耗与开发进度需求</strong>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>开发是否有规定的时间表;</li> <li data-list="bullet"><span class="ql-ui"></span>软/硬件投资有无限制。</li> </ol></li> <li><p><strong>其他非功能性要求</strong>:</p> <p>如采用某种开发模式,需要确定:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>质量控制标准;</li> <li data-list="bullet"><span class="ql-ui"></span>里程碑和评审;</li> <li data-list="bullet"><span class="ql-ui"></span>验收标准;</li> <li data-list="bullet"><span class="ql-ui"></span>各种质量要求的优先级;</li> <li data-list="bullet"><span class="ql-ui"></span>可维护性方面的要求。</li> </ol></li> <p>软件需求的出处:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>可以来自于用户(实际的和潜在的)、用户的规约、应用领域的专家、相关的技术标准和法规;</li> <li data-list="bullet"><span class="ql-ui"></span>可以来自于原有的系统、原有系统的用户、新系统的潜在用户;</li> <li data-list="bullet"><span class="ql-ui"></span>可以来自于竞争对手的产品</li> </ol> <h3>系统设计</h3> <h4>概要设计</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>设计软件系统总体结构 <ul> <li data-list="ordered"><span class="ql-ui"></span>确定每个模块的功能</li> <li data-list="ordered"><span class="ql-ui"></span>确定模块之间的调用关系</li> <li data-list="ordered"><span class="ql-ui"></span>确定模块之间的接口</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>数据结构及数据库设计</li> <li data-list="ordered"><span class="ql-ui"></span>编写概要设计文档</li> <li data-list="ordered"><span class="ql-ui"></span>评审</li> </ol> <h4>详细设计</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>对每个模块进行详细的算法设计</li> <li data-list="ordered"><span class="ql-ui"></span>对模块内的数据结构进行设计</li> <li data-list="ordered"><span class="ql-ui"></span>对数据库进行物理设计</li> <li data-list="ordered"><span class="ql-ui"></span>其他设计 <ol> <li data-list="ordered"><span class="ql-ui"></span>代码设计</li> <li data-list="ordered"><span class="ql-ui"></span>输入输出格式设计</li> <li data-list="ordered"><span class="ql-ui"></span>用户界面设计</li> </ol></li> <li>编写详细设计说明书</li> <li>评审</li> </ol> <h4>编码</h4> <p>根据详细设计进行代码的编写,得到可以运行的软件,并进行单元测试。</p> <h3>系统测试</h3> <p><strong>意义</strong>:系统测试是为了发现错误而执行程序的过程,成功的测试是发现了至今尚未发现的错误的测试。 <strong>目的</strong>:测试的目的就是希望能以最少的人力和时间发现潜在的各种错误和缺陷。</p> <h4>系统测试原则</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>应尽早并不断地进行测试。</li> <li data-list="ordered"><span class="ql-ui"></span>测试工作应该避免由原开发软件的人或小组承担。</li> <li data-list="ordered"><span class="ql-ui"></span>在设计测试方案时,不仅要确定输入数据,而且要根据系统功能确定预期输出结果。</li> <li data-list="ordered"><span class="ql-ui"></span>在设计测试用例时,不仅要设计有效、合理的输入条件,也要包含不合理、失效的输入条件。</li> <li data-list="ordered"><span class="ql-ui"></span>在测试程序时,不仅要检验程序是否做了该做的事,还要校验程序是否做了不该做的事。</li> <li data-list="ordered"><span class="ql-ui"></span>严格按照测试计划来进行,避免测试的随意性。</li> <li data-list="ordered"><span class="ql-ui"></span>妥善保存测试计划、测试用例。</li> <li data-list="ordered"><span class="ql-ui"></span>测试用例都是精心设计出来的。</li> <li data-list="ordered"><span class="ql-ui"></span>系统测试阶段的测试目标来自于需求分析阶段。</li> </ol> <h3>单元测试(模块测试)</h3> <ol> <li data-list="ordered"><span class="ql-ui"></span>单元测试的测试内容 <ul> <li data-list="ordered"><span class="ql-ui"></span>模块接口 <ul> <li data-list="ordered"><span class="ql-ui"></span>测试模块的输入参数和形式参数在个数、属性、单位上是否一致。</li> <li data-list="ordered"><span class="ql-ui"></span>调用其他模块时,所给出的实际参数和被调用模块的形式参数在个数、属性、单位上是否一致。</li> <li data-list="ordered"><span class="ql-ui"></span>调用标准函数时,所用的参数在属性、数目和顺序上是否正确。</li> <li data-list="ordered"><span class="ql-ui"></span>全局变量在各模块中的定义和用法是否一致。</li> <li data-list="ordered"><span class="ql-ui"></span>输入是否仅改变了形式参数。</li> <li data-list="ordered"><span class="ql-ui"></span>开/关的语句是否正确。</li> <li data-list="ordered"><span class="ql-ui"></span>规定的I/O格式是否与输入/输出语句一致。</li> <li data-list="ordered"><span class="ql-ui"></span>在使用文件之前是否已经打开文件或使用文件之后是否己经关闭文件。</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>局部数据结构</li> <li data-list="ordered"><span class="ql-ui"></span>重要的执行路径</li> <li data-list="ordered"><span class="ql-ui"></span>出错处理</li> <li data-list="ordered"><span class="ql-ui"></span>边界条件</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>单元测试过程 <ul> <li data-list="ordered"><span class="ql-ui"></span>驱动模块:接收测试例子的数据,将这些数据送到测试模块,输出结果。即模拟被测试模块的上一级模块,相当于被测模块的主程序。</li> <li data-list="ordered"><span class="ql-ui"></span>桩模块:代替测试模块中所调用的子模块,其内部可进行少量的数据处理。目的是为了检验入口、输出调用和返回的信息。</li> <li data-list="ordered"><span class="ql-ui"></span>提高模块的内聚度可以简化单元测试。</li> </ul></li> </ol> <h3>集成测试</h3> <p>集成测试是进行一些旨在发现与接口相关的错误的测试,其目标是利用已通过单元测试的构件建立设计中描述的程序结构。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>自顶向下集成测试</li> </ol> <p> 自顶向下集成测试是一种构造软件体系结构的增量方法。</p> <blockquote> <p>自顶向下集成不需要驱动模块,需要桩模块。</p> </blockquote> <ol> <li>自底向上集成测试</li> </ol> <p> 自底向上集成测试就是从原子模块(程序结构的最底层构件)开始进行构造和测试。</p> <blockquote> <p>自底向上集成需要驱动模块,不需要桩模块。</p> </blockquote> <ol> <li>回归测试:重新执行己测试过的某些子集,以确保变更没有传播不期望的副作用。</li> <li>冒烟测试:一种常用的集成测试方法,是时间关键项目的决定性机制,它让软件团队频繁地对项目进行评估。</li> </ol> <h3>测试方法</h3> <p>测试方法分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>静态测试:指被测试程序不在机器上运行,而是采用以下手段对程序进行检测 <ol> <li data-list="bullet"><span class="ql-ui"></span>人工检测:不依靠计算机而是依靠人工审查程序或评审软件。人工检测包括: <ol> <li data-list="bullet"><span class="ql-ui"></span>代码检查</li> <li data-list="bullet"><span class="ql-ui"></span>静态结构分析</li> <li data-list="bullet"><span class="ql-ui"></span>代码质量度量</li> </ol></li> <li>计算机辅助静态分析</li> </ol></li> <li><p><strong>动态测试</strong>:指通过运行程序发现错误。在对软件产品进行动态测试时可以采用以下两种测试方法:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>黑盒测试法</strong></li> <li data-list="bullet"><span class="ql-ui"></span><strong>白盒测试法</strong></li> </ol><p>测试用例由以下组成:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>测试输入数据</li> <li data-list="bullet"><span class="ql-ui"></span>预期输出结果:与测试输入数据对应的预期输出结果</li> </ol><p>在设计测试用例时,应当包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>合理的输入条件</li> <li data-list="bullet"><span class="ql-ui"></span>不合理的输入条件</li> </ol></li> <h4>黑盒测试(功能测试)</h4> <p>黑盒测试在完全不考虑软件的内部结构和特性的情况下,测试软件的外部特性</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>等价类划分</strong>:将程序的输入域划分为若干等价类,然后从每个等价类中选取一个代表性数据作为测试用例。当测试用例全是无效等价类时则不是一个好的测试用例。分类为: <ol> <li data-list="bullet"><span class="ql-ui"></span>有效等价类</li> <li data-list="bullet"><span class="ql-ui"></span>无效等价类</li> </ol></li> <li>边界值分析:输入的边界比中间更加容易发生错误,因此用边界值分析来补充等价类划分的测试用例设计技术。</li> <li>错误推测</li> <li>因果图</li> <h4>McCabe度量法(边 - 节 + 2)</h4> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/nhpcpjef.jpeg" alt="image.png"></p> <h4>白盒测试(结构测试)</h4> <p>白盒测试根据程序的内部结构和逻辑来设计测试用例,对程序的路径和过程进行测试,检查是否满足设计的需要。</p> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> <ul> <li><p><strong>逻辑覆盖</strong>:考察用测试数据运行被测程序时,对程序逻辑的覆盖程度。主要的逻辑覆盖标准有6种,它们的覆盖程度从低到高为:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span><strong>语句覆盖</strong>:指选择足够的测试数据,使被测试程序中的<strong>每条语句至少执行一次</strong>。语句覆盖对程序执行逻辑的覆盖很低,因此一般认为它是很弱的逻辑覆盖。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>判定覆盖(分支覆盖)</strong>:指设计足够的测试用例,使得被测程序中的<strong>每个判定表达式至少获得一次“真”/“假”值</strong>。判定覆盖的判定表达式是指判定表达式整体。判定覆盖要比语句覆盖更强一些。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>条件覆盖</strong>:指构造一组测试用例,使得每一判定语句中每<strong>个逻辑条件的各种可能的值至少满足一次</strong>。条件覆盖的判定语句是指判定表达式下的判定语句(如果有),即用<code>AND</code>、<code>OR</code>等逻辑运算符连接起来的语句(不包含逻辑运算符的语句)。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>判定/条件覆盖</strong>:指设计足够的测试用例,使得判定中每个条件的所有可能取值(真/假)至少出现一次,并使每个判定本身的判定结果(真/假)也至少出现一次。结果取判定覆盖和条件覆盖的并集。判定/条件覆盖同时满足: <ul> <li data-list="ordered"><span class="ql-ui"></span>判定覆盖</li> <li data-list="ordered"><span class="ql-ui"></span>条件覆盖</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span><strong>条件组合覆盖</strong>:指设计足够的测试用例,使得每个<strong>判定中条件的各种可能值的组合都至少出现一次</strong>。满足条件组合覆盖的测试用例一定满足: <ul> <li data-list="ordered"><span class="ql-ui"></span>判定覆盖</li> <li data-list="ordered"><span class="ql-ui"></span>条件覆盖</li> <li data-list="ordered"><span class="ql-ui"></span>判定/条件覆盖</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span><strong>路径覆盖</strong>:指覆盖被测试程序中<strong>所有可能的路径</strong>。</li> </ol></li> <li><p>循环覆盖</p></li> <li><p>基本路径测试</p></li> </ul> <p>白盒测试逻辑覆盖技术总结(<strong>覆盖程度从低到高</strong>):</p> <p>| 逻辑覆盖 | 说明 | | :--------------: | :--------------------------------------------: | | 语句覆盖 | 每条语句执行一次 | | 分支(判定)覆盖 | 每个分支获得一次True/False | | 条件覆盖 | 每个分支中的每个逻辑条件的所有可能取值满足一次 | | 判定/条件覆盖 | 分支覆盖 + 条件覆盖 | | 条件组合覆盖 | 每个判定中条件的各种可能值的组合都出现一次 | | 路径覆盖 | 覆盖被测试程序中所有可能的路径 |</p> <h3>运行和维护</h3> <h4>系统可维护性概念</h4> <p>系统是否能被很好地维护,可以用系统的可维护性这一指标来衡量。</p> <p>系统可维护性的评价指标</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>可理解性</li> <li data-list="bullet"><span class="ql-ui"></span>可测试性</li> <li data-list="bullet"><span class="ql-ui"></span>可修改性</li> </ol> <p><strong>软件文档与软件维护</strong>:</p> <p>软件文档是软件可维护性的决定因素。文档是软件产品的一部分,并且编写高质量的文档可以提高软件开发的质量。</p> <p>软件系统的文档分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>用户文档:主要描述系统功能和使用方法,并不关心这些功能是怎样实现的</li> <li data-list="bullet"><span class="ql-ui"></span>系统文档:描述系统设计、实现和测试等各方面的内容。</li> </ol> <p>可维护性是所有软件都应具有的基本特点,必须在开发阶段保证软件具有可维护的特点。在软件工程的每一个阶段都应考虑并提高软件的可维护性,在每个阶段结束前的技术审查和管理复查中应该着重对可维护性进行复审(如将来要改进的部分和可能会修改的部分)。</p> <p>做题技巧:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>维护应该针对整个软件配置,不应该只修改源程序代码。</li> <li data-list="bullet"><span class="ql-ui"></span>编写高质量文档可以提高软件开发的质量。</li> <li data-list="bullet"><span class="ql-ui"></span>文档也是软件产品的一部分,没有文档的软件就不能称之为软件。</li> <li data-list="bullet"><span class="ql-ui"></span>软件文档的编制在软件开发工作中占有突出的地位和相当大的工作量高质量文档对于软件产品的效益有着重要的意义。</li> <li data-list="bullet"><span class="ql-ui"></span>总的来说,软件文档只好不坏,选项中说软件文档不好的就是不正确的。</li> </ol> <h4>系统维护的内容及类型</h4> <p>软件维护:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>正确性维护。指改正在系统开发阶段已发生而系统测试阶段尚未发现的错误。</li> <li data-list="bullet"><span class="ql-ui"></span>适应性维护。使应用软件适应信息技术变化和管理需求变化而进行的修改。</li> <li data-list="bullet"><span class="ql-ui"></span>完善性维护。为扩充功能和改善性能而进行的修改。</li> <li data-list="bullet"><span class="ql-ui"></span>预防性维护。为了改进应用软件的可靠性和可维护性,为了适应未来的软/硬件环境的变化,应主动增加预防性的新的功能,以使应用系统适应各类变化而不被淘汰。</li> </ol> <h4>软件可靠性、可用性、可维护性</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>可靠性、可用性利可维护性是软件的质量属性,软件工程中,用 0-1 之间的数来度量。</li> <li data-list="bullet"><span class="ql-ui"></span>可靠性是指一个系统对于给定的时间间隔内、在给定条件下无失效运作的概率。可以用 MTTF/(1+MTTF) 来度量,其中 MTTF 为平均无故障时间。</li> <li data-list="bullet"><span class="ql-ui"></span>可用性是在给定的时间点上,一个系统能够按照规格说明正确运作的概率。可以用 MTBF/(1+MTBF) 来度量,其中 MTBF 为平均失效间隔时间。</li> <li data-list="bullet"><span class="ql-ui"></span>可维护性是在给定的使用条件下,在规定的时间间隔内,使用规定的过程和资源完成维护活动的概率。可以用 1/(1+MTTR) 来度量,其中 MTTR 为平均修复时间。</li> </ol> <h3>项目管理</h3> <h4>沟通路径</h4> <p>沟通图是指项目中人员或部门之间的沟通用一条无向边连接起来,所构成图即为沟通图。沟通图中的路径称为沟通路径。</p> <p>软件项目中沟通路径m的计算公式(人数n):</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>沟通图中无主程序员时: $$ m= (n−1)n÷2 $$</li> <li data-list="bullet"><span class="ql-ui"></span>沟通图中有主程序员时: $$ m = n -1 $$</li> </ol> <h4>COCOMO 估算模型</h4> <p>COCOMO模型是一种精确的、易于使用的成本估算模型。COCOMO模型按其详细程度分为:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>基本COCOMO模型:是一个<strong>静态单变量模型</strong>,用于对整个软件系统进行估算。</li> <li data-list="ordered"><span class="ql-ui"></span>中级COCOMO模型:是一个<strong>静态多变量模型</strong>,它将软件系统模型分为系统和部件两个层次,系统由部件构成,它把软件开发所需的人力(成本)看作是程序大小和一系列“成本驱动属性”的函数。</li> <li data-list="ordered"><span class="ql-ui"></span>详细COCOMO模型:将软件系统模型分为系统、子系统和模块3个层次,除包括中级模型所考虑的因素外,还考虑了在需求分析、软件设计等每一步的成本驱动属性的影响。</li> </ol> <h4>COCOMOII模型</h4> <p>和其前身COCOMO一样,COCOMOII也是一种层次结构的估算模型,被分为3个阶段性模型,分别对应三种不同的规模估算选择:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>应用组装模型:在软件工程的前期阶段使用,这时用户界面的原型开发、对软件和系统交互的考虑、性能的评估以及技术成熟度的评价是最重要的。规模估算选择:<strong>对象点</strong>。</li> <li data-list="ordered"><span class="ql-ui"></span>早期设计阶段模型:在需求己经稳定并且基本的软件体系结构己经建立时使用。规模估算选择:<strong>功能点</strong>。功能点可转换为代码行。</li> <li data-list="ordered"><span class="ql-ui"></span>体系结构阶段模型:在软件的构造过程中使用。规模估算选择:<strong>代码行</strong>。</li> </ol> <h3>进度管理</h3> <h4>Gantt图</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>Gantt图优点: <ol> <li data-list="bullet"><span class="ql-ui"></span>能清晰地描述每个任务的开始时间;</li> <li data-list="bullet"><span class="ql-ui"></span>能清晰地描述每个任务的结束时间;</li> <li data-list="bullet"><span class="ql-ui"></span>能清晰地描述任务的进展情况;</li> <li data-list="bullet"><span class="ql-ui"></span>各个任务之间的并行性。</li> </ol></li> <li>Gantt图缺点: <ol> <li data-list="bullet"><span class="ql-ui"></span>不能清晰地反映各任务之间的依赖关系;</li> <li data-list="bullet"><span class="ql-ui"></span>难以确定整个项目的关键所在,即不能清晰地确定影响进度的关键任务;</li> <li data-list="bullet"><span class="ql-ui"></span>不能反映计划中有潜力的部分。</li> </ol></li> <h4>PERT图</h4> <p>PERT图是一个有向图:</p> <p>PERT图的优点:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>给出了每个任务的开始时间、结束时间和完成该任务所需的时间;</li> <li data-list="bullet"><span class="ql-ui"></span>给出了任务之间的关系(依赖关系)。即任务之间的执行顺序。</li> </ol> <p>PERT图不能清晰地描述任务之间的并行情况。</p> <h4>项目活动图</h4> <p>项目活动图是一种有向图(与PERT图十分类似):</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>弧:表示活动。弧的权值表示活动的持续时间。</li> <li data-list="bullet"><span class="ql-ui"></span>顶点:表示项目里程碑。特殊的里程碑: <ol> <li data-list="bullet"><span class="ql-ui"></span>开始里程碑:没有任何活动指向该里程碑</li> <li data-list="bullet"><span class="ql-ui"></span>结束里程碑:没有任何活动从该里程碑指出</li> </ol></li> <p>项目活动图的关键路径:按照PERT图的方法求出松弛时间为0的、从开始里程碑到结束里程碑的路径。</p> <p>关键路径的长度:为结束里程碑的最早时刻(或最晚时刻)。它可以用来表示项目完成的最少时间。</p> <h3>软件配置管理</h3> <p>软件配置管理的主要目标包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>标识变更</li> <li data-list="bullet"><span class="ql-ui"></span>控制变更</li> <li data-list="bullet"><span class="ql-ui"></span>版本控制</li> <li data-list="bullet"><span class="ql-ui"></span>确保变更正确地实现</li> <li data-list="bullet"><span class="ql-ui"></span>报告有关变更</li> </ol> <p>主要内容有两种版本:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span> <ul> <li data-list="ordered"><span class="ql-ui"></span>版本管理</li> <li data-list="ordered"><span class="ql-ui"></span>配置支持</li> <li data-list="ordered"><span class="ql-ui"></span>变更支持</li> <li data-list="ordered"><span class="ql-ui"></span>过程支持</li> <li data-list="ordered"><span class="ql-ui"></span>团队支持</li> <li data-list="ordered"><span class="ql-ui"></span>变化报告</li> <li data-list="ordered"><span class="ql-ui"></span>审计支持</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span> <ul> <li data-list="ordered"><span class="ql-ui"></span>软件配置标识</li> <li data-list="ordered"><span class="ql-ui"></span>变更管理</li> <li data-list="ordered"><span class="ql-ui"></span>版本控制</li> <li data-list="ordered"><span class="ql-ui"></span>系统建立</li> <li data-list="ordered"><span class="ql-ui"></span>配置审核</li> <li data-list="ordered"><span class="ql-ui"></span>配置状态报告</li> </ul></li> </ol> <p>配置数据库分为以下三类:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>开发库</li> <li data-list="bullet"><span class="ql-ui"></span>受控库</li> <li data-list="bullet"><span class="ql-ui"></span>产品库</li> </ol> <h4>风险管理</h4> <p>一般认为软件风险包含两个特性:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>**不确定性:**指风险可能发生也可能不发生;</li> <li data-list="bullet"><span class="ql-ui"></span>**损失:**指如果风险发生,就会产生恶性后果。</li> </ol> <p><strong>项目风险</strong>威胁到项目计划。项目风险是指以下各方面的潜在问题以及它们对软件项目的影响:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>预算</li> <li data-list="bullet"><span class="ql-ui"></span>进度</li> <li data-list="bullet"><span class="ql-ui"></span>人员:聘用职员及组织</li> <li data-list="bullet"><span class="ql-ui"></span>资源</li> <li data-list="bullet"><span class="ql-ui"></span>利益相关者</li> <li data-list="bullet"><span class="ql-ui"></span>需求</li> </ol> <p>以下方面的不确定性也属于项目风险因素:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>项目复杂度</li> <li data-list="bullet"><span class="ql-ui"></span>项目规模</li> <li data-list="bullet"><span class="ql-ui"></span>项目结构</li> </ol> <p><strong>技术风险</strong>威胁到要开发软件的质量及交付时间。技术风险是指以下方面的潜在问题:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>设计</li> <li data-list="bullet"><span class="ql-ui"></span>实现</li> <li data-list="bullet"><span class="ql-ui"></span>接口</li> <li data-list="bullet"><span class="ql-ui"></span>验证</li> <li data-list="bullet"><span class="ql-ui"></span>维护</li> <li data-list="bullet"><span class="ql-ui"></span>规格说明的歧义性</li> <li data-list="bullet"><span class="ql-ui"></span>技术的不确定性</li> <li data-list="bullet"><span class="ql-ui"></span>技术陈旧</li> <li data-list="bullet"><span class="ql-ui"></span>“前沿”技术</li> </ol> <p><strong>商业风险</strong>威肋到要开发软件的生存能力,且常常会危害到项目或产品。5个主要的商业风险如下:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>市场风险:开发了一个没有人真正需要的优良产品或系统。</li> <li data-list="bullet"><span class="ql-ui"></span>策略风险:开发的产品不再符合公司的整体商业策略。</li> <li data-list="bullet"><span class="ql-ui"></span>销售风险:开发了一个销售部门不知道如何去销售的产品。</li> <li data-list="bullet"><span class="ql-ui"></span>管理风险:由于重点的转移或人员的变动而失去了高级管理层的支持。</li> <li data-list="bullet"><span class="ql-ui"></span>预算风险:没有得到预算或人员的保证。</li> </ol> <h4>风险识别</h4> <p>风险识别试图系统化地指出对项目计划(估算、进度、资源分配等)的威胁。识别出已知风险和可预测风险后,项目管理者首先要做的是:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>在可能时回避这些风险;</strong></li> <li data-list="bullet"><span class="ql-ui"></span><strong>在必要时控制这些风险。</strong></li> </ol> <p>识别风险的一种方法是建立风险条目检查表(未考察),主要用来识别下列几种类型中的一些已知风险和可预测风险:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>产品规模:与要开发或要修改的软件的总体规模相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>商业影响:与管理者或市场所施加的约束相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>客户特性:与客户的素质以及开发者和客户定期沟通的能力相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>过程定义:与软件过程定义的程度以及该过程被开发组织遵守的程度相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>开发环境:与用来开发产品的工具的可得性及质量相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>开发技术:与待开发软件的复杂性及系统所包含技术的“新奇性”相关的风险。</li> <li data-list="bullet"><span class="ql-ui"></span>人员才干及经验:与软件工程师的总体技术水平及项目经验相关的风险。</li> </ol> <p>与上述每个主题相关的问题可以针对每一个软件项目来回答。根据这些问题的答案,项目管理者就可以估计风险产生的影响。另一种风险条目检查表格式:仅仅列出与每一种类型有关的特性,最终给出一组风险因素和驱动因子以及它们发生的概率。</p> <p>风险因素(未考察)包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>性能:性能风险是指产品能够满足需求且符合其使用目的的不确定程度。</li> <li data-list="bullet"><span class="ql-ui"></span>成本:成本风险是指能够维持项目预算的不确定程度。</li> <li data-list="bullet"><span class="ql-ui"></span>支持:支特风险是指开发出的软件易于纠错、修改及升级的不确定程度。</li> <li data-list="bullet"><span class="ql-ui"></span>进度:进度风险是指能够维持项目进度且按时交付产品的不确定程度。</li> </ol> <h4>风险预测</h4> <p>风险预测又称风险估计,它试图从两个方面评估一个风险:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>风险发生的可能性或概率;</li> <li data-list="bullet"><span class="ql-ui"></span>发生风险所产生的后果。</li> </ol> <p>通常,项日计划人员与管理人员、技术人员一起进行以下4步风险预测活动:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>建立一个尺度或标准,以反映风险发生的可能性。</li> <li data-list="ordered"><span class="ql-ui"></span>描述风险产生的后果。</li> <li data-list="ordered"><span class="ql-ui"></span>估算风险对项目和产品的影响。</li> <li data-list="ordered"><span class="ql-ui"></span>标注风险预测的整体精确度,以免产生误解。</li> </ol> <p>一种简单的风险预测技术是建立风险表:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>第1列:列出所有的风险(由风险识别活动得到);</li> <li data-list="bullet"><span class="ql-ui"></span>第2~4列:列出每个风险的: <ol> <li data-list="bullet"><span class="ql-ui"></span>种类</li> <li data-list="bullet"><span class="ql-ui"></span>发生的概率</li> <li data-list="bullet"><span class="ql-ui"></span>所产生的影响</li> </ol><p>风险所产生的影响可用一个数字来表示:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>“1”:表示灾难性的;</li> <li data-list="bullet"><span class="ql-ui"></span>“2”:表示严重的;</li> <li data-list="bullet"><span class="ql-ui"></span>“3”:表示轻微的;</li> <li data-list="bullet"><span class="ql-ui"></span>“4”:表示可忽略的。</li> </ol></li> <p>评估风险影响:发生风险时,有3个因素可能会影响风险所产生的后果:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>风险的本质:指当风险发生时可能带来的问题。</li> <li data-list="bullet"><span class="ql-ui"></span>风险的范围: <ol> <li data-list="bullet"><span class="ql-ui"></span>风险的严重性;</li> <li data-list="bullet"><span class="ql-ui"></span>风险的整体分布情况:项目中有多少部分受到影响或有多少客户受到损害。</li> </ol></li> <li><p>风险的时间:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>何时能够感受到风险的影响;</li> <li data-list="bullet"><span class="ql-ui"></span>风险的影响会持续多长时间。</li> </ol></li> <li><p>整体的风险暴露度(RE):</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>$$ RE = P * C $$</li> <li data-list="bullet"><span class="ql-ui"></span>P是风险发生的概率,C是风险发生时带来的项目成本</li> </ol></li> <h4>风险评估</h4> <p>一种对风险评估很有用的技术就是定义风险参照水准。对于大多数软件项目来说,有3种典型的风险参照水准</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>成本:成本是否超支</li> <li data-list="bullet"><span class="ql-ui"></span>进度:进程是否延期</li> <li data-list="bullet"><span class="ql-ui"></span>性能:性能是否下降</li> </ol> <h4>风险控制</h4> <p>风险控制的目的是辅助项目组建立处理风险的策略。一个有效的策略必须考虑以下3个问题:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>风险避免:应对风险的最好办法是主动地避免风险,即在风险发生前分析引起风险的原因,然后采取措施,以避免风险的发生。</li> <li data-list="bullet"><span class="ql-ui"></span>风险监控:项目管理者应监控某些因素,这些因素可以提供风险是否正在变高或变低的指示。</li> <li data-list="bullet"><span class="ql-ui"></span>RMMM计划:风险管理策略可以包含在软件项目计划中,或者风险管理步骤也可以组织成一个独立的风险缓解、监控和管理计划(RMMM计划)。RMMM计划将所有风险分析工作文档化,并由项目管理者作为整个项目计划中的一部分来使用。建立了RMMM计划,而且项目己经启动之后,风险缓解及监测步骤也就开始了: <ol> <li data-list="bullet"><span class="ql-ui"></span>风险缓解:一种问题规避活动。</li> <li data-list="bullet"><span class="ql-ui"></span>风险监测:一种项目跟踪活动。这种监测活动有3个主要目的: <ol> <li data-list="bullet"><span class="ql-ui"></span>评估所预测的风险是否真的发生了;</li> <li data-list="bullet"><span class="ql-ui"></span>保证正确地实施了各风险的缓解步骤;</li> <li data-list="bullet"><span class="ql-ui"></span>收集能够用于今后风险缝隙的信息。</li> </ol></li> <p>风险监测的另一个任务就是试图找到“起源”(在整个项目中是哪些风险引起了哪些问题)。</p> </ol></li> <h3>软件质量</h3> <h4>软件质量特性</h4> <p>讨论软件质量首先要了解软件的质量特性,目前己经有多种软件质量模型来描述软件质量特性,如:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>ISO/IEC 9126 软件质量模型</li> <li data-list="bullet"><span class="ql-ui"></span>Me Call 软件质量模型。</li> </ol> <h4>ISO/IEC 9126</h4> <p>ISO/IEC 9126软件质量模型由3个层次组成:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>第一层:<strong>质量特性</strong></li> <li data-list="ordered"><span class="ql-ui"></span>第二层:<strong>质量子特性</strong></li> <li data-list="ordered"><span class="ql-ui"></span>第三层:<strong>度量指标</strong></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/l7hqan8l.jpeg" alt="image.png"><strong>质量子特性</strong>的含义:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>功能性: <ol> <li data-list="bullet"><span class="ql-ui"></span>适合性:与对规定任务能否提供一组功能以及这组功能是否<strong>适合</strong>有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>准确性:与能够得到<strong>正确</strong>或相符的结果或效果有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>互用性:与其他指定系统进行<strong>交互</strong>操作的能力相关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>依从性:使软件<strong>服从</strong>有关的标准、约定、法规及类似规定的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>安全性:与避免对程序及数据的非授权故意或意外访问的能力有关的软件属性。</li> </ol></li> <li>可靠性: <ol> <li data-list="bullet"><span class="ql-ui"></span>成熟性:由软件故障引起失效的频度有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>容错性:在软件错误或违反指定接口的情况下维持指定的性能水平的能力有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易恢复性:故障发生后重新建立性能水平并恢复直接受影响数据的能力,以及为达到此目的所需的时间和努力有关的软件属性。</li> </ol></li> <li>易使用性: <ol> <li data-list="bullet"><span class="ql-ui"></span>易理解性:与用户为理解逻辑概念及其应用所付出的劳动有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易学性:与用户为学习其应用(例如操作控制、输入、输出)所付出的努力相关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易操作性:与用户为进行操作和操作控制所付出的努力有关的软件属性。</li> </ol></li> <li>效率: <ol> <li data-list="bullet"><span class="ql-ui"></span>时间特性:与响应和处理时间以及软件执行其功能时的吞吐量有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>资源特性:与软件执行其功能时,所使用的资源量以及使用资源的持续时间有关的软件属性。</li> </ol></li> <li>可维护性: <ol> <li data-list="bullet"><span class="ql-ui"></span>易分析性:与为诊断缺陷或失效原因,或为判定待修改的部分所需努力有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易改变性:与进行修改、排错或适应环境变换所需努力有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>稳定性:与修改造成未预料效果的风险有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易测试性:为确认经修改软件所需努力有关的软件属性。</li> </ol></li> <li>可移植性: <ol> <li data-list="bullet"><span class="ql-ui"></span>适应性:与软件转移到不同环境时的处理或手段有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易安装性:与在指定环境下安装软件所需努力有关的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>一致性:使软件服从与可移植性有关的标准或约定的软件属性。</li> <li data-list="bullet"><span class="ql-ui"></span>易替换性:与一软件在该软件环境中用来替代指定的其他软件的可能和努力有关的软件属性。</li> </ol></li> <p>简单记忆:</p> <p>| 功能性 | 可靠性 | 易用性 | 效率 | <strong>可维护性</strong> | <strong>可移植性</strong> | | ---------------------- | ---------- | :--------: | ------ | -------------- | ------------------ | | 适合、安全、准、互、依 | 成、容、恢 | 理、学、操 | 时、资 | 分、改、稳、测 | 适应、安装、一、替 |</p> <h4>Mc Call 软件质量模型</h4> <p>Mc Call也给出了一个三层模型框架:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>第一层:质量特性</li> <li data-list="ordered"><span class="ql-ui"></span>第二层:评价准则</li> <li data-list="ordered"><span class="ql-ui"></span>第三层:度量指标</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/ni3etk89.jpeg" alt="image.png"></p> <h3>软件评审</h3> <p>通常,把“质量”理解为“用户满意程度”。为了使得用户满意,有以下两个必要条件:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>设计质量</strong>:设计的规格说明书符合用户的要求。设计质量的评审对象: <ol> <li data-list="bullet"><span class="ql-ui"></span>软件需求规格说明</li> <li data-list="bullet"><span class="ql-ui"></span>数据需求规格说明</li> <li data-list="bullet"><span class="ql-ui"></span>软件概要设计说明</li> </ol></li> <li><p><strong>程序质量</strong>:程序按照设计规格说明所规定的情况正确执行。程序质量的评审通常是从开发者的角度进行,与开发技术直接相关。</p> <p>程序质量的评审对象:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>软件结构: <ol> <li data-list="bullet"><span class="ql-ui"></span>功能结构: <ol> <li data-list="bullet"><span class="ql-ui"></span>数据结构</li> <li data-list="bullet"><span class="ql-ui"></span>功能结构</li> <li data-list="bullet"><span class="ql-ui"></span>数据结构和功能结构之间的对应关系</li> </ol></li> <li>功能的通用性</li> <li>模块的层次</li> <li>模块结构: <ol> <li data-list="bullet"><span class="ql-ui"></span>控制流结构</li> <li data-list="bullet"><span class="ql-ui"></span>数据流结构</li> <li data-list="bullet"><span class="ql-ui"></span>模块结构与功能结构之间的对应关系</li> </ol></li> <li>处理过程的结构</li> </ol></li> <li>与运行环境的接口: <ol> <li data-list="bullet"><span class="ql-ui"></span>与硬件的接口</li> <li data-list="bullet"><span class="ql-ui"></span>与用户的接口</li> </ol></li> <li>变更带来的影响</li> </ol></li> <p>软件的规格说明分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>外部规格说明</strong>:从用户角度来看的规格,包括硬件/软件系统设计、功能设计;设计质量是由外部规格说明决定的</li> <li data-list="bullet"><span class="ql-ui"></span><strong>内部规格说明</strong>:为了实现外部规格的更详细的规格,即软件模块结构与模块处理过程的设计。内部规格说明是从开发者角度来看的规格说明。程序是由内部规格说明决定的。</li> </ol> <h3>软件容错技术</h3> <p>提高软件质量和可靠性的技术大致可分为两类:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>避开错误:在开发的过程中不让差错潜入软件的技术;</li> <li data-list="bullet"><span class="ql-ui"></span>容错技术:对某些无法避开的差错,使其影响减至最小的技术。</li> </ol> <p>实现容错的主要手段是冗余。冗余是指对于实现系统规定功能是多余的那部分资源,包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>硬件</li> <li data-list="bullet"><span class="ql-ui"></span>软件</li> <li data-list="bullet"><span class="ql-ui"></span>信息</li> <li data-list="bullet"><span class="ql-ui"></span>时间</li> </ol> <p>由于加入了这些资源,有可能使系统的可靠性得到较大的提高。通常,冗余技术分为4类:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>结构冗余:结构冗余是通常采用的冗余技术,按其工作方法可以分为: <ol> <li data-list="bullet"><span class="ql-ui"></span>静态冗余:静态冗余通过表决和比较来屏蔽系统中出现的错误。 <ol> <li data-list="bullet"><span class="ql-ui"></span>三模冗余(Triple Module Redundancy,TR)</li> <li data-list="bullet"><span class="ql-ui"></span>多模冗余</li> </ol></li> <li><p>动态冗余:动态冗余的主要方式是多重模块待机储备。当系统测试到某工作模块出现错误时,就用一个备用模块来顶替它并重新运行。这里包括以下过程:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>检测</li> <li data-list="bullet"><span class="ql-ui"></span>切换</li> <li data-list="bullet"><span class="ql-ui"></span>恢复</li> </ol><p>动态冗余有以下两种方式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>热备份系统:每当一个出错模块被其他备用模块顶替后,冗余系统相当于进行了一次重构。在热备份系统中,备用模块在待机过程中的失效率为0。</li> <li data-list="bullet"><span class="ql-ui"></span>冷备份系统:各备用模块在其待机时可与主模块一同工作,也可不工作。</li> </ol></li> <li><p><strong>混合冗余</strong>:兼有静态元余和动态冗余的长处。</p></li> </ol></li> <li><p><strong>信息冗余</strong>:指为检测或纠正信息在运算或传输中的错误需外附加的一部分信息。</p></li> <li><p><strong>时间冗余</strong>:指以重复执行指令或程序来消除瞬时错误带来的影响。</p></li> <li><p><strong>冗余附加技术</strong>:指为实现上述冗余技术所需的资源和技术,包括:程序、指令、数据、存放和调动它们的空间和通道等。在屏蔽硬件错误的容错技术中,冗余附加技术包括:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>关键程序和数据的冗余存储及调用。</li> <li data-list="ordered"><span class="ql-ui"></span>检测、表决、切换、重构、纠错和复算的实现。</li> </ol><p>在屏蔽软件错误的容错系统中,冗余附加技术的构成包括:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>冗余备份程序的存储及调用。</li> <li data-list="ordered"><span class="ql-ui"></span>实现错误检测和错误恢复的程序。</li> <li data-list="ordered"><span class="ql-ui"></span>实现容错软件所需的固化程序。</li> </ol></li> <h3>软件工具</h3> <h4>软件开发工具</h4> <p>对应于软件开发过程的各种活动,软件开发工具通常有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>需求分析工具</li> <li data-list="bullet"><span class="ql-ui"></span>设计工具</li> <li data-list="bullet"><span class="ql-ui"></span>编码与排错工具</li> <li data-list="bullet"><span class="ql-ui"></span>测试工具</li> </ol> <h4>软件维护工具</h4> <p>辅助软件维护过程中活动的软件称为软件维护工具,它辅助维护人员对软件代码及其文档进行各种维护活动。软件维护工具主要有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>版本控制工具</li> <li data-list="bullet"><span class="ql-ui"></span>文档分析工具</li> <li data-list="bullet"><span class="ql-ui"></span>开发信息库工具</li> <li data-list="bullet"><span class="ql-ui"></span>逆向工程工具</li> <li data-list="bullet"><span class="ql-ui"></span>再工程工具</li> </ol> <h4>软件管理和软件支持工具</h4> <p>软件管理和软件支持工具用来辅助管理人员和软件支持人员的管理活动和支持活动,以确保软件高质量地完成。常用的铺助软件管理和软件支持的工具有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>项目管理工具</li> <li data-list="bullet"><span class="ql-ui"></span>配置管理工具</li> <li data-list="bullet"><span class="ql-ui"></span>软件评价工具</li> </ol> <h2>第六章 结构化开发</h2> <h3>模块独立</h3> <h4>耦合</h4> <p><strong>耦合是模块之间的相对独立性(互相连接的紧密程度)的度量</strong>。</p> <p>耦合取决于各个模块之间接口的<strong>复杂程度、调用模块的方式以及通过接口的信息类型</strong>等。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/fjsoxyq5.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>无直接耦合:指两个模块之间<strong>没有直接</strong>的关系,属于不同模块</li> <li data-list="bullet"><span class="ql-ui"></span>数据耦合:指两个模块之间有调用关系,传递的是<strong>简单的数据值</strong></li> <li data-list="bullet"><span class="ql-ui"></span>标记耦合:指两个模块之间传递的是<strong>数据结构</strong></li> <li data-list="bullet"><span class="ql-ui"></span>控制耦合:指一个模块调用另一个模块时,传递的是<strong>控制变量</strong></li> <li data-list="bullet"><span class="ql-ui"></span>外部耦合:模块间通过软件之外的环境联结</li> <li data-list="bullet"><span class="ql-ui"></span>公共耦合:通过一个<strong>公共数据环境</strong>相互作用</li> <li data-list="bullet"><span class="ql-ui"></span>内容耦合:当一个模块直接使用另一个模块的<strong>内部数据</strong>,或通过非正常入口转入另一个<strong>模块内部</strong></li> </ol> <h4>内聚</h4> <p>内聚是对一个模块内部各个元素彼此结合的紧密程度的度量。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/9bfnv6rf.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>偶然内聚(巧合内聚):各处理元素之间<strong>没有任何联系</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>逻辑内聚:模块内执行若干个<strong>逻辑上</strong>相似的功能。</li> <li data-list="bullet"><span class="ql-ui"></span>时间内聚:把需要<strong>同时执行的动作组合</strong>在一起。</li> <li data-list="bullet"><span class="ql-ui"></span>过程内聚:指定的过程执行。</li> <li data-list="bullet"><span class="ql-ui"></span>通信内聚:模块内的所有处理元素都在<strong>同一个数据结构</strong>上操作。</li> <li data-list="bullet"><span class="ql-ui"></span>顺序内聚:指一个模块中的各个处理元素都密切相关于同一功能且<strong>必须顺序执行</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>功能内聚:最强的内聚,指模块内的所有元素共同作用完成一个功能,缺一不可。</li> </ol> <p>总结:耦合性和内聚性是模块独立性的两个定性标准,在将软件系统划分模块时,应尽量做到高内聚、低耦合,提高模块的独立性。</p> <h3>系统结构设计原则</h3> <ol> <li data-list="ordered"><span class="ql-ui"></span>分解-协调原则(考的少)</li> <li data-list="ordered"><span class="ql-ui"></span>自顶向下的原则(考的少)</li> <li data-list="ordered"><span class="ql-ui"></span>信息隐蔽、抽象的原则(考的少)</li> <li data-list="ordered"><span class="ql-ui"></span>一致性原则:统一的规范、统一的标准和统一的文件模式。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>明确性原则</strong>:功能明确、接口明确、消除多重功能和无用接口、避免病态连接、降低接口复杂度。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>模块之间的耦合尽可能小,模块的内聚度尽可能高。(高内聚、低耦合)</strong></li> <li data-list="ordered"><span class="ql-ui"></span>模块的扇入系数和扇出系数要合理。(扇入扇出适中)</li> <li data-list="ordered"><span class="ql-ui"></span>模块的规模适当。</li> <li data-list="ordered"><span class="ql-ui"></span>模块的作用范围应该在其控制范围之内。</li> </ol> <p><strong>结构化设计主要包括:</strong></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>体系结构设计:定义软件的主要结构元素及其关系。</li> <li data-list="ordered"><span class="ql-ui"></span>数据设计:基于实体联系图确定软件涉及的文件系统的结构及数据库的表结构。</li> <li data-list="ordered"><span class="ql-ui"></span>接口设计:描述用户界面,软件和其他硬件设备、其他软件系统及使用人员的外部接口,以及各种构件之间的内部接口。</li> <li data-list="ordered"><span class="ql-ui"></span>过程设计:确定软件各个组成部分内的算法及内部数据结构,并选定某种过程的表达形式来描述各种算法。</li> </ol> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>结构化方法的分析结果的组成:</strong></li> <li data-list="bullet"><span class="ql-ui"></span>一套分层的数据流图</li> <li data-list="bullet"><span class="ql-ui"></span>一本数据字典(词典)</li> <li data-list="bullet"><span class="ql-ui"></span>一组小说明(加工逻辑说明)</li> <li data-list="bullet"><span class="ql-ui"></span>补充材料(实体联系图)</li> </ol> <p><strong>结构图的基本成分包括:模块、调用和数据</strong></p> <p>黄金准则:用户操纵控制、减轻用户的记忆负担、保持界面一致</p> <p>软件系统的可维护性评价指标包括:可理解性、可测试性、可修改性、可靠性、可移植性、可使用性和效率。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>构造分层DFD时需要注意: <ol> <li data-list="bullet"><span class="ql-ui"></span>适当命名</li> <li data-list="bullet"><span class="ql-ui"></span>画数据流而不是控制流</li> <li data-list="bullet"><span class="ql-ui"></span>避免一个加工有过多的数据流</li> <li data-list="bullet"><span class="ql-ui"></span>分解尽量均匀</li> <li data-list="bullet"><span class="ql-ui"></span>先考虑确定状态,忽略琐碎的细节</li> <li data-list="bullet"><span class="ql-ui"></span>随时准备重画</li> </ol></li> <li><p>软件维护的内容包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>正确性:正确性维护是指<strong>改正</strong>在系统开发阶段已发生而系统测试阶段尚未发现的错误。</li> <li data-list="bullet"><span class="ql-ui"></span>适应性:适应性维护是指使应用软件适应信息技术变化和管理需求变化而进行的修改。</li> <li data-list="bullet"><span class="ql-ui"></span>完善性:完善性维护是为<strong>扩展功能和改善性能</strong>而进行的修改。</li> <li data-list="bullet"><span class="ql-ui"></span>预防性:预防性维护是改变系统的某些方面,以预防失效发生的修改行为。</li> </ol></li> <h4>系统文档</h4> <p>对文档在系统开发人员、项目管理人员、系统维护人员、系统评价人员以及用户之间的多种作用总结如下:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>用户与系统分析人员</strong>在系统规划和系统分析阶段通过文档进行沟通。这里的文档主要包括: <ol> <li data-list="bullet"><span class="ql-ui"></span>可行性研究报告</li> <li data-list="bullet"><span class="ql-ui"></span>总体规划报告</li> <li data-list="bullet"><span class="ql-ui"></span>系统开发合同</li> <li data-list="bullet"><span class="ql-ui"></span>系统方案说明书</li> </ol></li> <li><p><strong>系统开发人员与项目管理人员</strong>通过文档在项目期内进行沟通。这里的文档是指项目管理文件,主要有:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>系统开发计划 <ol> <li data-list="bullet"><span class="ql-ui"></span>工作任务分解表</li> <li data-list="bullet"><span class="ql-ui"></span>PERT图</li> <li data-list="bullet"><span class="ql-ui"></span>甘特图</li> <li data-list="bullet"><span class="ql-ui"></span>预算分配表</li> </ol></li> <li>系统开发月报</li> <li>系统开发总结报告</li> <p>有了这些文档可以:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>不同阶段开发人员工作的顺利交接;</li> <li data-list="bullet"><span class="ql-ui"></span>降低因为人员流动带来的风险。</li> </ol> </ol></li> <li><p><strong>系统测试人员与系统开发人员</strong>通过文档进行沟通。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>系统方案说明书</li> <li data-list="bullet"><span class="ql-ui"></span>系统开发合同</li> <li data-list="bullet"><span class="ql-ui"></span>系统设计说明书</li> <li data-list="bullet"><span class="ql-ui"></span>测试计划 系统测试人员再将评估结果撰写成<strong>系统测试报告</strong>。</li> </ol></li> <li><p><strong>系统开发人员与用户</strong>在系统运行期间进行沟通。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>用户手册</li> <li data-list="bullet"><span class="ql-ui"></span>操作指南</li> </ol></li> <li><p><strong>系统开发人员与系统维护人员</strong>通过文档进行沟通。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>系统开发总结报告</li> <li data-list="bullet"><span class="ql-ui"></span>系统设计说明书</li> </ol></li> <li><p><strong>用户与维修人员</strong>在运行维护期间进行沟通。</p> <p>用户在使用信息系统的过程中,将运行过程中的问题进行记载,形成:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>系统运行报告</li> <li data-list="bullet"><span class="ql-ui"></span>维护修改建议</li> </ol><p>系统维护人员根据以下文档对系统进行维护和升级:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>维护修改建议</li> <li data-list="bullet"><span class="ql-ui"></span>系统开发人员留下的技术手册等文档</li> </ol></li> <h3>数据流图</h3> <p><a href="http://#article" target="_blank">数据流图</a></p> <h3>数据字典</h3> <p>数据字典(DD)是为数据流图中的以下成分做出说明:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>数据流</li> <li data-list="bullet"><span class="ql-ui"></span>文件</li> <li data-list="bullet"><span class="ql-ui"></span>加工:对加工的描述称为“小说明”或“加工逻辑说明”</li> <li data-list="bullet"><span class="ql-ui"></span>组成数据流或文件的<strong>数据项</strong>。</li> </ol> <h4>数据字典的条目</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span><strong>数据流</strong>条目:对DFD中数据流的定义,通常列出该数据流的各组成数据项。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>数据项</strong>条目:组成数据流和数据存储的最小元素,是不可再分解的数据单位。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>数据存储</strong>条目:对DFD中数据存储的定义。</li> <li data-list="ordered"><span class="ql-ui"></span><strong>基本加工</strong>条目:用来说明DFD中(下层)基本加工的处理逻辑(加工逻辑)。</li> </ol> <p>外部实体不包括在数据字典的条目中</p> <h4>加工逻辑的描述</h4> <p>加工逻辑也称为“小说明”。加工逻辑描述方法有结构化语言、判定表(决策表)和判定树。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>对数据流图的每一个基本加工,必须有一个基本加工逻辑说明</li> <li data-list="ordered"><span class="ql-ui"></span>基本加工逻辑说明必须描述基本加工如何把输入输出数据流变换为输出数据流的加工规则</li> <li data-list="ordered"><span class="ql-ui"></span>加工逻辑说明必须描述加工实现的策略而不是实现加工的细节</li> <li data-list="ordered"><span class="ql-ui"></span>加工逻辑说明中包含的信息是充足的,完备的,有用的,无冗余的。</li> </ol> <h2>第七章 面向对象</h2> <h3>面向对象基础</h3> <p><strong>注意事项</strong>:</p> <p>成员变量 == 数据 == 属性 == 状态</p> <p>成员函数 == 操作 == 行为 == 方法 == 函数</p> <h4>面向对象的基本概念</h4> <p>面向对象 = 对象(Object)+ 分类(Classification)+ 继承(Inheritance)+通过消息的通信</p> <h4>类</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>实体类:是应用领域中的核心类。实体类的对象表示现实世界中的真实的实体。</li> <li data-list="bullet"><span class="ql-ui"></span>接口类(边界类):的对象为用户提供一种与系统合作交互的方式,是系统内对象和系统外参与者的联系媒介</li> <li data-list="bullet"><span class="ql-ui"></span>控制类:控制类的对象用来控制活动流,充当协调者</li> </ol> <h4>对象</h4> <p>对象通常可由对象名、属性和方法 3 个部分组成。</p> <h4>方法的重载</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>方法名相同 参数个数不同</li> <li data-list="ordered"><span class="ql-ui"></span>方法名相同,参数类型不同</li> <li data-list="ordered"><span class="ql-ui"></span>方法名相同,参数类型的顺序不同</li> </ol> <h4>封装</h4> <p>一个对象把属性和行为封装为一个整体。封装是一种信息隐藏的技术</p> <h4>继承</h4> <p>继承是父类和子类之间共享属性和方法的机制。</p> <p>继承关系中的子类将全部拥有父类的全部属性和方法,但只能用非私有化的属性和方法</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>子类可以继承父类属性和方法</li> <li data-list="bullet"><span class="ql-ui"></span>子类可以有自己特殊的属性和方法</li> <li data-list="bullet"><span class="ql-ui"></span>子类可以重写父类属性和方法</li> </ol> <h4>多态</h4> <p>同类型的对象,表现出的不同形态。(对象的多种形态)</p> <p>绑定在编译时进行的,叫做静态绑定,动态绑定是在运行时进行的(动态绑定支持多态)</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>通用的: <ol> <li data-list="bullet"><span class="ql-ui"></span>参数多态:应用比较广泛的多态,被称为最纯的多态。</li> <li data-list="bullet"><span class="ql-ui"></span>包含多态:在许多语言中都存在,最常见的例子就是子类型化,即一个类型是另一个类型的子类型。</li> </ol></li> <li>特定的: <ol> <li data-list="bullet"><span class="ql-ui"></span>过载多态:同一个名字在不同的上下文中所代表的含义不同。</li> <li data-list="bullet"><span class="ql-ui"></span>强制多态:通过强制类型转换(也称为强制转型)将一个对象或变量视为另一个类型的操作。</li> </ol></li> <h4>面向对象设计的原则</h4> <p>面向对象方法中的五大原则:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>单一责任原则:就一个类而言,应该仅有一个引起它变化的原因。</li> <li data-list="ordered"><span class="ql-ui"></span>开放-封闭原则:软件实体应该是可以扩展的,即开发的;但是不可修改的,即封闭的。<strong>(扩展开放、修改关闭)</strong></li> <li data-list="ordered"><span class="ql-ui"></span>里氏替换原则: 子类型必须能够替换掉他们的基类型。<strong>(基类出现的地方,子类一定可以出现)</strong></li> <li data-list="ordered"><span class="ql-ui"></span>依赖倒置原则:抽象不应该依赖于细节,细节应该依赖于抽象。<strong>(依赖于抽象,而不依赖于细节[实现])</strong></li> <li data-list="ordered"><span class="ql-ui"></span>接口分离原则:不应该强迫客户依赖于它们不用的方法。<strong>(依赖于抽象,不依赖于具体)</strong></li> </ol> <p>共同封闭原则:包中的所有类对于同一类性质的变化应该是共同封闭的。<strong>一个变化若对一个包产生影响,则将对该包中的所有类产生影响,而对于其他的包不造成任何影响</strong>。</p> <p>共同重用原则:一个包中的所有类应该是共同重用的。<strong>如果重用了包中的一个类,那么就要重用包中的所有类</strong>。</p> <h4>1.面向对象分析</h4> <p>面向对象分析是为了获得对应用问题的理解,其主要任务是抽取和整理用户需求并建立问题域精确模型。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>认定对象</li> <li data-list="ordered"><span class="ql-ui"></span>组织对象</li> <li data-list="ordered"><span class="ql-ui"></span>描述对象间的相互作用</li> <li data-list="ordered"><span class="ql-ui"></span>确定对象的操作</li> <li data-list="ordered"><span class="ql-ui"></span>定义对象的内部信息。</li> </ol> <h4>2.面向对象设计</h4> <p>面向对象设计是采用协作的对象、对象的属性和方法说明软件解决方案的一种方式,强调的是定义软件对象和这些软件对象如何协作来满足需求,延续了面向对象分析。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>识别类及对象</li> <li data-list="ordered"><span class="ql-ui"></span>定义属性</li> <li data-list="ordered"><span class="ql-ui"></span>定义服务</li> <li data-list="ordered"><span class="ql-ui"></span>识别关系</li> <li data-list="ordered"><span class="ql-ui"></span>识别包</li> </ol> <h4>3.面向对象程序设计</h4> <p>面向对象程序设计的实质:选用一种<strong>面向对象程序设计语言</strong>(<strong>OOPL</strong>):</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>采用对象、类及其相关概念所进行的程序设计;</li> <li data-list="bullet"><span class="ql-ui"></span>关键在于加入了类和继承性,从而进一步提高了抽象程度。</li> </ol> <p>特定的OOP概念一般是通过OOPL中特定的语言机制来体现的。</p> <p>OOP现在已经扩展到系统分析和软件设计的范畴,出现了面向对象分析和面向对象设计的概念。</p> <h4>4.面向对象测试</h4> <p>面向对象测试是根据规范说明来验证系统设计的正确性。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>算法层:测试类中定义的每个方法,基本上相当于传统软件测试中的单元测试。</li> <li data-list="ordered"><span class="ql-ui"></span>类层:测试封装在同一个类中的所有方法与属性之间的相互作用。在面向对象软件中类是基本模块,因此可以认为这是面向对象测试中所特有的模块测试。</li> <li data-list="ordered"><span class="ql-ui"></span>模板层:测试一组协同工作的类之间的相互作用,大体上相当于传统软件测试中的集成测试,但是也有面向对象软件的特点(例如,对象之间通过发送消息相互作用)。</li> <li data-list="ordered"><span class="ql-ui"></span>系统层:把各个子系统组装成完整的面向对象软件系统,在组装过程中同时进行测试。</li> </ol> <h4>事物</h4> <p>UML中有4中事物:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>结构事物:结构事物是UML模型中的<strong>名词</strong>,通常是模型的<strong>静态部分</strong>,描述概念或物理元素。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/wnx0dkoj.jpeg" alt="image.png"> 2. 行为事物:行为事物是UML模型的<strong>动态部分</strong>,它们是模型中的<strong>动词</strong>,描述了跨越时间和空间的行为。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/2pktv44i.jpeg" alt="image.png"></p> <ol> <li>分组事物:分组事物是UML模型的<strong>组织部分</strong>,是一些由模型分解成“盒子”。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/p0w1ugt4.jpeg" alt="image.png"> 4. 注释事物:注释事物是UML模型的解释部分。这些注释事物用来描述、说明和标注模型的任何元素。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/whnkvjq2.jpeg" alt="image.png"></p> <h4>关系</h4> <p>UML中有4种关系:依赖、关联、泛化和实现。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>依赖:依赖是两个事物间的<strong>语义关系</strong>,其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/rmuiv690.jpeg" alt="image.png"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/64n6gst0.jpeg" alt="image.png"> 2. 关联:关联是一种<strong>结构关系</strong>,它描述了一组链,链是对象之间的连接。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/yad53g5g.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>聚合:部分和整体的生命周期不一致,整体消失了,部分仍然存在,部分可以脱离整体存在。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/y0uzrqxk.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>组合:部分和整体的生命周期一致,整体消失了,部分也消失了,部分不可以脱离整体存在。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/q4u8j6s4.jpeg" alt="image.png"> 3. 泛化:泛化是一种特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素)的对象。子元素共享了父元素的结构和行为。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/j787ag3c.jpeg" alt="image.png"></p> <ol> <li>实现(了解):实现是类元之间的语义关系,其中一个类元指定了由另一个类元保证执行的契约。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/attrcmmh.jpeg" alt="image.png"></p> <h3>UML</h3> <h4>事物</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>结构事物。结构事物是UML模型中的名词。它通常是模型的静态部分</li> <li data-list="bullet"><span class="ql-ui"></span>行为事物。行为事物是UML模型的动态部分。</li> <li data-list="bullet"><span class="ql-ui"></span>分组事物。分组事物是UML模型的组织部分,是一些由模型分解成的“盒子”。</li> <li data-list="bullet"><span class="ql-ui"></span>注释事物。注释事物是UML模型的解释部分。这些注释事物用来描述、说明和标注模型的任何元素。</li> </ol> <h4>[关 系](#UML 的关系)</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>依赖</li> <li data-list="bullet"><span class="ql-ui"></span>关联 <ol> <li data-list="bullet"><span class="ql-ui"></span>聚合</li> <li data-list="bullet"><span class="ql-ui"></span>组合</li> </ol></li> <li>泛化</li> <li>实现</li> <h4>[类图](#UML 的图)</h4> <p>类图(Class Diagram)展现了一组<strong>对象、接口、协作</strong>和它们之间的关系。</p> <p>符号:</p> <p><code>+</code> : public 公有的</p> <p><code>-</code> : private 私有的</p> <p><code>#</code> : protected 受保护的</p> <p><code>~</code> : package 包的</p> <p>类图用于对系统的<strong>静态设计视图建模</strong>。通常以下述3种方式之一使用类图:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>对系统的词汇建模。</li> <li data-list="ordered"><span class="ql-ui"></span>对简单的协作建模。</li> <li data-list="ordered"><span class="ql-ui"></span>对逻辑数据库模式建模。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/wvakgyai.jpeg" alt="image.png"></p> <h4>对象图</h4> <p>对象图展现了<strong>某一时刻一组对象以及它们之间的关系</strong>,描述了在类图中所建立的事物的实例的<strong>静态快照</strong>。</p> <p>对象图给出系统的<strong>静态设计视图</strong>或<strong>静态进程视图</strong>。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/3p77nu9e.jpeg" alt="image.png"></p> <h4>用例图</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>用例图展现了一组用例、参与者以及它们之间的关系。</strong></li> <li data-list="bullet"><span class="ql-ui"></span>一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例。</li> <li data-list="bullet"><span class="ql-ui"></span>参与者:参与者是与系统交互的<strong>外部实体</strong>。可能是使用者,也可能是与系统交互的外部系统、基础设备等。</li> <li data-list="bullet"><span class="ql-ui"></span>用例:用例是从用户角度描述系统的行为,它将系统的一个功能描述成<strong>一系列的事件</strong>,这些事件最终对操作者产生有价值的观测结果。用例是一个类,它代表一类功能而不是使用该功能的某一具体实例。</li> <li data-list="bullet"><span class="ql-ui"></span>之间的关系: <ol> <li data-list="bullet"><span class="ql-ui"></span>包含关系(<>)(用例之间):一个用例包含另一个用例</li> <li data-list="bullet"><span class="ql-ui"></span>扩展关系(<>)(用例之间):一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例</li> <li data-list="bullet"><span class="ql-ui"></span>关联关系(参与者和用例之间)</li> <li data-list="bullet"><span class="ql-ui"></span>泛化关系(用例与用例以及参与者与参与者之间)</li> </ol></li> <li><p>用例图用于对系统的<strong>静态用例视图</strong>进行建模。可用以下两种方式来使用用例图:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>对系统的语境建模。</li> <li data-list="bullet"><span class="ql-ui"></span>对系统的需求建模。</li> </ol></li> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/lezvu1et.jpeg" alt="image.png"></p> <h4>交互图(序列图、通信图)</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>交互图用于对系统的<strong>动态方面</strong>进行建模。一张交互图表现的是一个交互,由一组对象和它们之间的关系组成。包含它们之间可能传递的消息。</li> <li data-list="bullet"><span class="ql-ui"></span>交互图一般包括对象、链和消息</li> </ol> <ol> <li data-list="ordered"><span class="ql-ui"></span><strong>序列图</strong>(顺序图、时序图) :序列图是场景的图形化表示,描述了对象之间的时间顺序。序列图用于展示系统中一个用例和多个对象的行为。序列图有两个不同于通信图的特征: <ul> <li data-list="ordered"><span class="ql-ui"></span>序列图有对象生命线</li> <li data-list="ordered"><span class="ql-ui"></span>序列图有控制焦点</li> </ul></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/yr5i7y5s.jpeg" alt="image.png"></p> <ol> <li><p><strong>通信图</strong>(协作图):通信图强调<strong>收发消息的对象的结构组织</strong>,在早期的版本中也被称作协作图。</p> <p><strong>通信图展现了对象之间的消息流及其顺序。</strong></p> <p>通信图有两个不同于序列图的特性:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>通信图有路径</li> <li data-list="bullet"><span class="ql-ui"></span>通信图有顺序号</li> </ol></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/itqt8erf.jpeg" alt="image.png"> 3. 交互概览图 (不考) 4. 计时图 (不考)</p> <h4>状态图</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>状态图展现了一个<strong>状态机</strong>,它由状态、转换、事件和活动组成。</li> <li data-list="bullet"><span class="ql-ui"></span>状态图展现了对象的状态转换及事件顺序</li> <li data-list="bullet"><span class="ql-ui"></span>可以用状态图对系统的<strong>动态方面</strong>建模。当对系统、类或用例的动态方面建模时,通常是对<strong>反应型对象建模</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>定义的状态主要有:初态(即初始状态)、终态(即最终状态)和中间状态。</li> <li data-list="bullet"><span class="ql-ui"></span>三种标准事件:entry、exit、do <ol> <li data-list="bullet"><span class="ql-ui"></span>entry:入口动作,进入状态立即执行</li> <li data-list="bullet"><span class="ql-ui"></span>exit:出口动作,退出状态立即执行</li> <li data-list="bullet"><span class="ql-ui"></span>do:内部活动,占有限时间,并可以中断的工作</li> </ol></li> <li><p>事件是在某个特定时刻发生的事情,它是对引起系统做动作或(和)从一个状态转换到另一个状态的外界事件的抽象。</p></li> <li><p>转换包括两个状态(源状态,目标状态)</p></li> <p>事件,监护条件,动作</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/muxmjrdy.jpeg" alt="image.png"><strong>事件触发转换(迁移)</strong></p> <p>活动(动作)可以在状态(迁移)内执行,也可以在状态转换时执行。</p> <p>监护条件是一个布尔表达式。</p> <h5>活动图</h5> <ol> <li data-list="bullet"><span class="ql-ui"></span>活动图是一种特殊的状态图,它展现了在系统内从<strong>一个活动到另一个活动的流程</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>活动图一般包括活动状态和动作状态、转换和对象。</li> <li data-list="bullet"><span class="ql-ui"></span>通常有两种使用活动图的方式: <ol> <li data-list="bullet"><span class="ql-ui"></span>对工作流建模。</li> <li data-list="bullet"><span class="ql-ui"></span>对操作建模。</li> </ol></li> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/84wm1m2x.jpeg" alt="image.png"></p> <h4>构件图(组件图)</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>构件图展现了一组构件之间的组织和依赖。</strong></li> <li data-list="bullet"><span class="ql-ui"></span>构件图专注于系统的静态<strong>实现</strong>试图。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/1vg6ta5k.jpeg" alt="image.png"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/d8xwl8pa.jpeg" alt="image.png"></p> <h4>部署图</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>部署图是用来对面向对象系统的<strong>物理方面</strong>建模的方法,展现了运行时处理结点以及其中构件(制品)的配置。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>部署图展现了系统的软件和硬件之间的关系,在实施阶段使用。</strong></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/fnk04su2.jpeg" alt="image.png"></p> <h4>UML图汇总</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>静态建模:类图、对象图、用例图</li> <li data-list="bullet"><span class="ql-ui"></span>动态建模:序列图(顺序图、时序图)、通信图(协作图)、状态图、活动图</li> <li data-list="bullet"><span class="ql-ui"></span>物理建模:构件图(组件图)、部署图</li> <li data-list="bullet"><span class="ql-ui"></span>交互图:序列图(顺序图、时序图)、通信图(协作图)</li> </ol> <h2>第八章 设计模式</h2> <h3>设计模式的要素</h3> <p>设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便地<strong>复用成功的设计和体系结构</strong>。</p> <p>设计模式基本要素:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>模式名称(Pattern Name)</li> <li data-list="bullet"><span class="ql-ui"></span>问题(Problem)</li> <li data-list="bullet"><span class="ql-ui"></span>解决方案(Solution)</li> <li data-list="bullet"><span class="ql-ui"></span>效果(Consequences)</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/vgjjqgug.jpeg" alt="image.png"></p> <h2>第九章 算法设计与分析</h2> <h4>时间复杂度</h4> <p>算法时间复杂度以算法中基本操作重复执行的次数(简称为频度)作为算法的时间度量。一般不必要精确计算出算法的时间复杂度,<strong>只要大致计算出相应的数量级</strong>即可,如O(1)、O(㏒₂n)、O(n)或O(n²)等。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/1cgncnfq.jpeg" alt="image.png"></p> <p>递归式时间复杂度:递归的次数 x 每次递归的时间复杂度</p> <p>主方法。主方法也称为主定理,给出了求解以下形式的递归式的快速方法。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/urtcu6ig.jpeg" alt="image.png"></p> <h4>空间复杂度</h4> <p>非递归:O(1) O(n) O(n²)</p> <h4>回溯法</h4> <h5>n皇后问题</h5> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/mkeo39p2.jpeg" alt="image.png"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> #include<stdio.h> #include"stdlib.h" int Place(int *Column,int index){ int i; for(i=1;i<index;i++){ int Column_differ = abs(Column[index] - Column[i]); int Row_differ = abs(index - i); if(Column[i] == Column[index] || Column_differ == Row_differ) return 0; } return 1; } void N_Queue(int n){ int Column_Num[n+1]; int index = 1; int i; int answer_num = 0; for(i=1;i<=n;i++) Column_Num[i] = 0; while(index>0){ Column_Num[index]++; while(Column_Num[index] <= n && !Place(Column_Num,index)) Column_Num[index]++; if(Column_Num[index] <=n){ if (index == n) { answer_num++; printf("方案%d:",answer_num); for(i=1;i<= n;i++){ printf("%d ",Column_Num[i]); } printf("\n"); }else { index++; Column_Num[index]=0; } }else { index--; } } } int main(){ N_Queue(6); return 0; } </div> </div> <h4>分治法</h4> <p>递归有两个基本要素:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>边界条件,即确定递归到何时终止,也称为递归出口</li> <li data-list="bullet"><span class="ql-ui"></span>递归模式,即大问题是如何分解为小问题的,也称为递归体</li> </ol> <p>分支算法在每一层递归上都有 3 个步骤:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>分解。将原问题分解成一系列子问题。</li> <li data-list="ordered"><span class="ql-ui"></span>求解。递归地求解各子问题。若子问题足够小,则直接求解。</li> <li data-list="ordered"><span class="ql-ui"></span>合并。将子问题的解合并成原问题的解。</li> </ol> <h5>归并排序算法</h5> <div class="ql-code-block-container"> <div class="ql-code-block"> include<stdio.h> #define INT_MAX 2147483647 /** * 归并排序 **/ void Merge(int A[],int p,int q,int r){ int n1 = q - p + 1,n2 = r -q,i,j,k; int L[50],R[50]; for(i=0;i<n1;i++) L[i] = A[p+i]; for(j=0;j<n2;j++) R[j] = A[q+j+1]; L[n1] = INT_MAX; R[n2] = INT_MAX; i=0; j=0; for(k=p;k<r+1;k++){ if(L[i] < R[j]){ A[k] = L[i]; i++; }else{ A[k]=R[j]; j++; } } } void MergeSort(int A[],int p,int r){ int q; if(p < r){ q = (p+r) / 2; MergeSort(A, p, q); MergeSort(A, q+1, r); Merge(A, p, q,r); } } int main(){ int A[] = {4,1,3,6,7,5,2,9}; MergeSort(A, 0, 7); int i; for (i = 0; i<8; i++) { printf("%d ",A[i]); } printf("\n"); return 0; } </div> </div> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/nednmnny.jpeg" alt="image.png"></p> <p><strong>最大字段和问题</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/l3cx1hgs.jpeg" alt="image.png"></p> <div class="ql-code-block-container"> <div class="ql-code-block"> #include<stdio.h> #include<stdlib.h> int MaxSubSum(int * Array,int left,int right){ int sum = 0; int i; if(left == right){ /*分解到单个整数,不可继续分解*/ if(Array[left] > 0) sum = Array[left]; else sum = 0; }else{ /*从 left 和 right 的中间分解数组*/ int center = (left + right)/2; /*划分的位置*/ int leftsum = MaxSubSum(Array, left, center); int rightsum = MaxSubSum(Array, center+1, right); /*计算包括 center 的最大值,判断是情形1、情形2还是情形3*/ int s1 = 0; int lefts = 0; for(i = center;i >= left;i--){ lefts = lefts + Array[i]; if(lefts > s1) s1 = lefts; } int s2 = 0; int rights = 0; for(i = center + 1;i<= right;i++){ rights = rights + Array[i]; if(rights > s2) s2 = rights; } sum = s1 + s2; /*情形1*/ if (sum < leftsum) { sum = leftsum; } /*情形2*/ if(sum < rightsum){ sum = rightsum; } } return sum; } int main(){ int *Array = (int *)malloc(6*sizeof(int)); Array[0] = -2; Array[1] = 11; Array[2] = -4; Array[3] = 13; Array[4] = -5; Array[5] = -2; int result = MaxSubSum(Array, 0, 5); printf("%d\n",result); return 0; } </div> </div> <p>{ return a > b ? a : b; } int main(){ int v[] = {0,2,4,5,6}; // 物品价值数组 int w[] = {0,1,2,3,4}; // 物品重量数组 int f[N + 1][W + 1] = {}; // 子问题解数组 int i,j; for(i=1;i<=N;i++){ for(j=1;j<=W;j++){ if(j >= w[i]){ // 选第 i 个物品的前提条件 // 等于不选第 i 个物品 和 选第 i 个物品 两者的较大值 f[i][j] = max(f[i-1][j],f[i-1][j-w[i]] + v[i]); }else{ // 不选第 i 个物品 f[i][j] = f[i - 1][j]; // 等于从前 i-1 个物品中选,背包容量为 j 时的最大价值 } } } printf("%d\n",f[N][W]); return 0; } </div> </div> <p>时间复杂度:O(N*W) N:物品数量 W:背包容量</p> <h5>矩阵连乘问题</h5> <ol> <li data-list="bullet"><span class="ql-ui"></span>时间复杂度:O(n³)</li> <li data-list="bullet"><span class="ql-ui"></span>空间复杂度O(n²)</li> </ol> <h4>贪心法</h4> <h5>部分背包问题</h5> <div class="ql-code-block-container"> <div class="ql-code-block"> #include<stdio.h> #define N 5 // 物品数量 #define W 100 // 背包容量 // 显示物品价值、重量、单位重量价值数组 void show(int v[],int w[],double vw[]){ int i; printf("物品价值数组:"); for(i = 1;i<=N;i++) printf("%d ",v[i]); printf("\n"); printf("物品重量数组:"); for(i = 1;i<=N;i++) printf("%d ",w[i]); printf("\n"); printf("物品单位重量价值数组:"); for(i = 1;i<=N;i++) printf("%0.1lf ",vw[i]); printf("\n"); } double Max_Value(int v[],int w[],double vw[]){ double result = 0.0; int i; int w_temp = W; for(i=1;i<=N;i++){ if(w_temp >= w[i]){ result = result + v[i]; w_temp = w_temp - w[i]; }else{ break; } } if(w_temp > 0 && i<=N){ result = result + w_temp * vw[i]; } return result; } int main(){ int v[] = {0,65,20,30,60,40}; // 物品价值数组 int w[] = {0,30,10,20,50,40}; // 物品重量数组 double vw[N + 1]; // 物品单位重量价值数组 int i; // 初始化 物品单位重量价值数组 for(i = 1;i<=N;i++) vw[i] = (double) v[i] / w[i]; show(v, w, vw); double result =Max_Value(v, w, vw); printf("\nreslut %.1lf\n",result); return 0; } </div> </div> <h2>第十章 数据库</h2> <h3>概念数据模型(E-R图)</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>实体:客观存在并且可以相互区别的事物称为实体。如一个单位,一个职工</li> <li data-list="bullet"><span class="ql-ui"></span>属性:描述实体的特征称为属性。如学生实体用若干属性(学号、姓名、性别、出生日期、班级号)来描述。</li> <li data-list="bullet"><span class="ql-ui"></span>码:唯一标识实体的属性称为码。如学号是学生实体的码。</li> <li data-list="bullet"><span class="ql-ui"></span>域:属性的取值范围称为该属性的域。如性别域为(男、女)</li> <li data-list="bullet"><span class="ql-ui"></span>联系:实体之间的对应的关系称为联系。 <ol> <li data-list="bullet"><span class="ql-ui"></span>实体内部个属性之间的联系</li> <li data-list="bullet"><span class="ql-ui"></span>实体之间的联系 <ol> <li data-list="bullet"><span class="ql-ui"></span>一对一联系(1:1)。如班和班长之间是一对一联系。</li> <li data-list="bullet"><span class="ql-ui"></span>一对多联系(1:n)。班和学生之间是一对多联系。</li> <li data-list="bullet"><span class="ql-ui"></span>多对多联系(m:n)。课程和学生之间是多对多联系。</li> </ol></li> </ol></li> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/dg1pu5gy.jpeg" alt="image.png"></p> <h3>三级模式结构</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>外模式 ——> 视图</li> <li data-list="bullet"><span class="ql-ui"></span>概念模式(模式) ——> 基本表</li> <li data-list="bullet"><span class="ql-ui"></span>内模式 ——> 存储文件</li> </ol> <h4>两级映像</h4> <p>数据库系统在三级模式之间提供了两级映像:模式/内模式映像、外模式/模式映像。 保证了数据库中的数据具有较高的逻辑独立性和物理独立性。</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>模式/内模式映像:实现了概念模式和内模式之间的相互转换。</li> <li data-list="ordered"><span class="ql-ui"></span>外模式/模式映像:实现了外模式和概念模式之间的相互转换。</li> <li data-list="ordered"><span class="ql-ui"></span>数据的物理独立性:需要修改概念模式和内模式之间的映像。</li> <li data-list="ordered"><span class="ql-ui"></span>数据的逻辑独立性:需要修改外模式和概念模式之间的映像。</li> </ol> <h3>关系型模式的基本术语</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>关系</strong>:一个关系就是一张<strong>二维表</strong>,每个关系有一个关系名</li> <li data-list="bullet"><span class="ql-ui"></span><strong>元组</strong>:表中的<strong>一行即为一个元组</strong>,对应存储文件中的一个记录值。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>属性</strong>:表中的<strong>列</strong>称为属性,每一列有一个属性名。属性值相当于记录中的数据项或者字段值。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>域</strong>:属性的<strong>取值范围</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>关系模式</strong>:对关系的描述称为关系模式,由关系名和其属性集合构成,关系模式的格式为:关系名(属性名1,属性名2,…,属性名n)</li> <li data-list="bullet"><span class="ql-ui"></span><strong>候选码</strong>(候选健):属性或属性组合其值能够<strong>唯一地标识</strong>一个元组。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>主码</strong>(主键):在一个关系中可能有多个候选码,从中选择一个作为主码。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>主属性</strong>:所有候选键都称为主属性,其他的属性都称为非主属性。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>外码</strong>(或外键):如果一个关系中的属性或属性组并非该关系(二维表)的码,但它们是另外一个关系(二维表)的码,则称其为该关系的外码。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>全码</strong>:关系模式的所有属性组是这个关系模式的候选码,称为全码。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>超码</strong>(超键):一个包含码的属性集称为超码。例如学号是码,则(学号,姓名)就是一个超码。</li> </ol> <h3>完整性约束</h3> <p>完整性规则保证用户对数据库做修改时不会破坏数据的一致性。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>实体完整性</strong>:关系中主码的值不能为空或部分为空。即主属性不能为空。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>参照完整性</strong>:如果关系R2的外码X与关系R1的主码相对应,则外码X的每个值必须在关系R1中的主码中找到,或者为空值。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>用户定义完整性</strong>:指用户对某一具体数据指定的约束条件进行检验。(如手机号码为11位)</li> </ol> <h3>关系代数运算符</h3> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/20m7r0rk.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>投影:投影运算是从关系的<strong>垂直方向</strong>进行运算,在关系R中选出若干属性列A组成新的关系,记作<strong>πA(R)</strong></li> <li data-list="bullet"><span class="ql-ui"></span>选择:选择运算是从关系的<strong>水平方向</strong>进行运算,是从关系R中选择满足给定条件的的元组,记作<strong>σF(R)</strong></li> <li data-list="bullet"><span class="ql-ui"></span>连接:连接运算是从两个关系<em>R</em>和S的笛卡儿积中选取满足条件的元组。 <ol> <li data-list="bullet"><span class="ql-ui"></span>θ连接:从<em>R</em>与S的笛卡儿积中选取属性间满足一定条件的元组。</li> <li data-list="bullet"><span class="ql-ui"></span>等值连接:当 θ 为 “=” 时,称之为等值连接。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>自然连接</strong>:自然连接是一种特殊的等值连接,它要求两个关系中进行比较的分量必须是相同的属性组,并且在结果集中<strong>将重复属性列去掉</strong>。</li> </ol> <blockquote> <p>可以认为笛卡儿积是无条件连接,其他的连接操作认为是有条件连接。</p> </blockquote></li> <li><p>外连接:外连接运算是连接运算的扩展,可以处理由于连接运算而缺失的信息。(p27集)</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>左外连接(⟕):取出左侧关系中所有与右侧关系中任一元组都不匹配的元组,用空值null充填所有来自右侧关系的属性,构成新的元组,将其加入自然连接的结果中。</li> <li data-list="bullet"><span class="ql-ui"></span>右外连接(⟖):取出右侧关系中所有与左侧关系中任一元组都不匹配的元组,用空值null填充所有来自左侧关系的属性,构成新的元组,将其加入自然连接的结果中。</li> <li data-list="bullet"><span class="ql-ui"></span>全外连接(⟗)。完成左外连接和右外连接的操作。即填充左侧关系中所有与右侧关系中任一元组都不匹配的元组,并填充右侧关系中所有与左侧关系中任一元组都不匹配的元组,将产生的新元组加入自然连接的结果中。</li> </ol></li> <h3>SQL语言</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>SQL不支持列的序号</li> <li data-list="bullet"><span class="ql-ui"></span>投影、选择转SQL语言</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/0k84s94v.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>笛卡尔积转SQL语言</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/8u9yoofp.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>自然连接转SQL语言</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/kkmg8isl.jpeg" alt="image.png"></p> <h4>SQL语言的分类</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>DDL</strong>(<strong>Data Definition Language</strong>,<strong>数据定义语言</strong>):用来定义数据库对象:数据库,表,列等。关键字:<code>CREATE</code>、<code>DROP</code>、<code>ALTER</code> 等。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>DML</strong>(<strong>Data Manipulation Language</strong>,<strong>数据操作语言</strong>):用来对数据库中表的数据进行增删改。关键字:<code>INSERT</code>、<code>DELETE</code>、<code>UPDATE</code> 等。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>DQL</strong>(<strong>Data Query Language</strong>,<strong>数据查询语言</strong>):用来查询数据库中表的记录。关键字:<code>SELECT</code>等。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>DCL</strong>(<strong>Data Control Language</strong>,<strong>数据控制语言</strong>):用来定义数据库的访问权限和安全级别,及创建用户。</li> </ol> <h4>SQL控制语句</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>插入权限:insert on</li> <li data-list="bullet"><span class="ql-ui"></span>允许将该权限授予他人:WITH GRANT OPTION</li> <li data-list="bullet"><span class="ql-ui"></span>视图:WITH CHECK IPTION</li> </ol> <h3>关系模式的范式</h3> <p>候选码中包含的属性称为主属性、不包含在候选码中的属性称为非主属性。若候选码多于一个,可以选定其中的一个为主码。</p> <h4>范式之间的关系</h4> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/g6l0hu9c.jpeg" alt="image.png"></p> <h4>1NF(第一范式)</h4> <p>定义:若关系模式 R 每一个分量是不可再分的数据项,则关系模式 R 属于第一范式。</p> <p>1NF可能存在的问题:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>数据冗余</strong>:数据的冗余度大,R中可能出现多个元组在多个属性集中值相同</li> <li data-list="bullet"><span class="ql-ui"></span><strong>修改异常</strong>:引起修改操作的不一致性,修改一条记录可能需要修改其他多条记录才能保持数据一致性(由数据冗余引起)</li> <li data-list="bullet"><span class="ql-ui"></span><strong>插入异常</strong>:主码不能取空值或部分空值,会出现插入异常</li> <li data-list="bullet"><span class="ql-ui"></span><strong>删除异常</strong>:本该删除但又客观存在的元组,会出现删除异常。即,删除掉的数据可能包含着其他本不该被删除的属性</li> </ol> <p>问题的原因是1NF中可能存在部分函数依赖。</p> <h4>2NF(第二范式)</h4> <p>定义:若关系模式 R∈1NF,且每一个非主属性完全依赖于码,则关系模式 R∈2NF。</p> <p>当 1NF 消除了非主属性对码的部分函数依赖,则称为 2NF。</p> <p>可能存在数据冗余和更新异常等问题。</p> <h4>3NF(第三范式)</h4> <p>2NF 消除了非主属性对码的传递函数依赖,则称为 3NF。</p> <p>属于 3NF 的关系模式 R 可能存在主属性对候选码的部分依赖和传递依赖。</p> <h4>BC范式(BCNF)</h4> <p>定义:R是一个关系模式,F是它的依赖集,R属于BCNF,当且仅当其F中的每个依赖的决定因素必定包含R的某个候选码。</p> <p>BC范式已经消除了插入和删除异常</p> <h4>规范化步骤</h4> <p>NF到4NF之间的转换关系:</p> <p>| 范式 | 转换关系 | | :--: | :---------------------------------------------------: | | 1NF | 每一个分量是不可再分的数据项 | | 2NF | 1NF消除了部分函数依赖后满足2NF | | 3NF | 2NF消除了非主属性对候选码的传递函数依赖后满足3NF | | BCNF | 3NF消除了主属性对候选码的部分和传递函数依赖后满足BCNF | | 4NF | BCNF消除非平凡且非函数依赖的多值依赖后满足4NF |</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/itf5s5cq.jpeg" alt="image.png"></p> <h3>数据库设计</h3> <p>新奥尔良法是目前公认的数据库设计方法,它将数据库设计分为以下几个阶段:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>用户需求分析:收集用户需求,确定系统边界</li> <li data-list="ordered"><span class="ql-ui"></span>概念结构设计:E—R图</li> <li data-list="ordered"><span class="ql-ui"></span>逻辑结构设计:E—R图转换成关系模式、规范化</li> <li data-list="ordered"><span class="ql-ui"></span>物理结构设计</li> </ol> <h4>需求分析</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>需求分析是后面几个阶段,逻辑结构设计、物理结构设计以及应用程序设计的依据。</li> <li data-list="bullet"><span class="ql-ui"></span>需求分析将收集到的零碎的、局部的数据分析整理后,建立<strong>需求说明文档、数据字典和数据流程图</strong>。</li> <li data-list="bullet"><span class="ql-ui"></span>需求分析阶段的成果:系统需求说明书、数据流图、数据字典、需求说明文档、各种说明性白表格</li> </ol> <h4>概念结构设计</h4> <p>对需求分析阶段所得到的数据进行分类、聚集和概括,确定实体、属性和联系。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>选择局部应用</li> <li data-list="bullet"><span class="ql-ui"></span>逐一设计分 E-R 图</li> <li data-list="bullet"><span class="ql-ui"></span>E-R 图合并</li> </ol> <p>E-R 图之间的冲突</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>属性冲突。同一属性可能会存在于不同的分 E-R 图。</li> <li data-list="ordered"><span class="ql-ui"></span>命名冲突。异名同义、同名异议</li> <li data-list="ordered"><span class="ql-ui"></span>结构冲突。同一实体在不同的分E—R图中有不同的属性,同一对象在某一个E—R图中被抽象为实体,而在另一个E—R图中被抽象为属性,需要统一。</li> </ol> <h4>逻辑结构设计</h4> <p>逻辑结构设计阶段的主要工作步骤包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>确定数据模型</li> <li data-list="bullet"><span class="ql-ui"></span>将 E-R 图转换成为指定的数据模型</li> <li data-list="bullet"><span class="ql-ui"></span>确定完整性约束和确定用户视图</li> <li data-list="bullet"><span class="ql-ui"></span>关系规范化</li> </ol> <h3>事物管理</h3> <p>事务是一个操作序列,这些操作“要么都做,要么都不做”。</p> <p>事务和程序是两个不同的概念,一般一个程序可包含多个事务。</p> <p>事务的ACID性质:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>原子性</strong>:事务是原子的,要么都做,要么都不做。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>一致性</strong>:事务执行的结果必须保证数据库从一个一致性状态变到另一个一致性状态。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>隔离性</strong>:事务相互隔离。当多个事务并发执行时,任一事务的更新操作直到其成功提交的整个过程,对其他事务都是不可见的。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>持久性</strong>:一旦事务成功提交,即使数据库崩溃,其对数据库的更新操作也将永久有效。</li> </ol> <h3>分布式数据库</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>分片</strong>透明:指用户或应用程序不需要知道逻辑上访问的表具体是怎么分块存储的</li> <li data-list="bullet"><span class="ql-ui"></span><strong>复制</strong>透明:指采用复制技术的分布方法,用户不需要知道数据是复制到哪些节点,如何复制的。</li> <li data-list="bullet"><span class="ql-ui"></span><strong>位置</strong>透明:指用户无须知道数据存放的物理位置</li> <li data-list="bullet"><span class="ql-ui"></span><strong>逻辑</strong>透明:指用户或应用程序无需知道局部场地使用的是哪种数据模型</li> <li data-list="bullet"><span class="ql-ui"></span><strong>共享</strong>性:指数据存储在不同的结点数据共享</li> <li data-list="bullet"><span class="ql-ui"></span><strong>自治</strong>性:指每结点对本地数据都能独立管理</li> <li data-list="bullet"><span class="ql-ui"></span><strong>可用</strong>性:指当某一场地故障时,系统可以使用其他场地上的副本而不至于使整个系统瘫痪</li> <li data-list="bullet"><span class="ql-ui"></span><strong>分布</strong>性:指数据在不同场地上的存储</li> </ol> <h2>第十一章 计算机网络</h2> <h3>网络的设备</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>物理层:中继器、集线器(可以看作成一种特殊的<strong>多路或多端口</strong>的中继器)</li> <li data-list="bullet"><span class="ql-ui"></span>数据链路层:网桥、交换机(可以看成<strong>多端口</strong>的网桥)</li> <li data-list="bullet"><span class="ql-ui"></span>网络层:路由器、ICMP(控制报文协议)</li> <li data-list="bullet"><span class="ql-ui"></span>应用层:网关</li> </ol> <p>| | 广播域 | 冲突域 | | :------------------: | :----: | :----: | | 物理层(集线器) | × | × | | 数据链路层(交换机) | × | √ | | 网络层(路由器) | √ | √ |</p> <h3>协议簇</h3> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/d9dnc2wt.jpeg" alt="image.png"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/39b560a8.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>FTP的端口号:20(数据端口)、21(控制端口)</li> <li data-list="bullet"><span class="ql-ui"></span>http的端口号:80</li> <li data-list="bullet"><span class="ql-ui"></span>https的端口号:443</li> <li data-list="bullet"><span class="ql-ui"></span>DNSD的端口号:53</li> </ol> <h3>TCP和UDP</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>TCP(传输控制协议):在IP提供的不可靠数据服务的基础上为应用程序<strong>提供了可靠的、面向连接</strong>的、全双工的数据传输服务。采用<strong>三次握手</strong>来确认建立和关闭连接是否成功。TCP的功能或服务有: <ol> <li data-list="bullet"><span class="ql-ui"></span>可靠传输</li> <li data-list="bullet"><span class="ql-ui"></span>连接管理</li> <li data-list="bullet"><span class="ql-ui"></span>差错校验和重传</li> <li data-list="bullet"><span class="ql-ui"></span>流量控制:采用可变大小的滑动窗口协议</li> <li data-list="bullet"><span class="ql-ui"></span>拥塞控制</li> <li data-list="bullet"><span class="ql-ui"></span>端口寻址</li> </ol></li> <li>UDP(用户数据报协议):<strong>是一种不可靠的、无连接的协议</strong>,可以保证应用程序进程间的通信。UDP上的应用有VoIP等。 <ol> <li data-list="bullet"><span class="ql-ui"></span>UDP的首部8B,TCP的首部20B,UDP相比TCP来说,开销较小。</li> <li data-list="bullet"><span class="ql-ui"></span>TCP和UDP均提供了端口寻址功能。</li> </ol></li> <p>补充:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>TCP和UDP都是应用于<strong>传输层</strong>的网络协议。</li> <li data-list="ordered"><span class="ql-ui"></span>TCP有助于提供可靠性,UDP有助于提高传输的高速率性。</li> </ol> <h3>电子邮件协议 SMTP 和 POP3</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>SMTP和POP3都使用TCP端口传输和接收邮件。</li> <li data-list="bullet"><span class="ql-ui"></span>SMTP:仅传输 ASCII 文本,不能传输非文字性附件。主要用来发邮件,可以使用MIME(邮件附件k扩展协议),添加其他类型的附件。TCP端口号:25。</li> <li data-list="bullet"><span class="ql-ui"></span>POP3:邮件接收协议;TCP端口号:110。</li> <li data-list="bullet"><span class="ql-ui"></span>POP3基于C/S模式(Client/Server模式,客户端/服务器模式)。</li> <li data-list="bullet"><span class="ql-ui"></span>PEM:私密邮件</li> </ol> <h3>ARP和RARP</h3> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> </ol> <ul> <li><p>地址解析协议(ARP)和反地址解析协议(RARP)是<strong>网络层</strong>的一个重要的协议</p></li> <li><p>ARP(地址解析协议):将IP地址转换为MAC地址(物理地址)。</p> <p>IP到MAC地址的转换过程:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>查询ARP高速缓存中是否有该IP地址</li> <li data-list="ordered"><span class="ql-ui"></span>如果该IP地址在ARP高速缓存中,便使用与它对应的MAC地址,将数据报发送给所需的物理网卡</li> <li data-list="ordered"><span class="ql-ui"></span>如果ARP高速缓存中没有该IP地址,ARP便在局域网上以<strong>广播</strong>方式发送一个ARP请求包</li> <li data-list="ordered"><span class="ql-ui"></span>如果局域网上IP地址与某台计算机中的IP地址相一致,那么该计算机便生成一个ARP应答信息(单播),信息中包含对应的MAC地址。(广播传送请求,单播传送响应)</li> </ol></li> <li><p>RARP(反地址解析协议):将MAC地址转换为IP地址,主要用于无盘工作站。</p></li> </ul> <h3>DHCP(动态主机配置协议)</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>DHCP协议的功能和作用是: <ol> <li data-list="bullet"><span class="ql-ui"></span>集中的管理、自动分配IP地址</li> <li data-list="bullet"><span class="ql-ui"></span>使网络环境中的主机动态地获得IP地址、Gateway地址、DNS服务器地址等信息</li> <li data-list="bullet"><span class="ql-ui"></span>提升地址的使用率</li> </ol><p>DHCP客户端可以从DHCP服务器获得以下内容:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>本机IP地址</li> <li data-list="bullet"><span class="ql-ui"></span>DNS服务器地址</li> <li data-list="bullet"><span class="ql-ui"></span>DHCP服务器地址</li> <li data-list="bullet"><span class="ql-ui"></span>默认网关的地址</li> </ol><p>无效地址:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>Windows无效地址:169.254.X.X169.254.X.X是Windows系统在DHCP信息租用失败时自动给客户机分配的IP地址</li> <li data-list="bullet"><span class="ql-ui"></span>Linux无效地址:0.0.0.0</li> </ol></li> <h3>URL</h3> <p><code>协议名://主机名.域名.域名后缀.域名分类/目录/网页文件</code></p> <p>| 组织模式 | 含义 | 地理模式 | 含义 | | :------: | :--------------: | :------: | :------: | | com | 商业机构 | cn | 中国 | | edu | 教育机构 | hk | 中国香港 | | gov | 政府机构 | mo | 中国澳门 | | mil | 军事部门 | tw | 中国台湾 | | net | 主要网络支持中心 | us | 美国 | | org | 上述以外组织 | uk | 英国 | | int | 国际组织 | jp | 日本 |</p> <p><strong>IPv6 128位地址空间、IPv4 32位地址空间。</strong></p> <h3>浏览器</h3> <ol> <li data-list="bullet"><span class="ql-ui"></span>DNS域名查询次序是:本地的hosts文件——本地DNS缓存——本地DNS服务器——根域名服务器</li> <li data-list="bullet"><span class="ql-ui"></span>主域名服务器在接受域名请求后查询顺序是:本地缓存——本地hosts文件——本地数据库——转发域名服务器</li> </ol> <h3>internet地址</h3> <p>Internet地址格式主要有两种书写形式:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>域名</li> <li data-list="bullet"><span class="ql-ui"></span>IP地址</li> </ol> <h4>IP地址</h4> <p>IP地址:Internet中的主机地址实际上是用IP地址来唯一标识的。</p> <p>IP地址有两种:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>IPv4</li> <li data-list="bullet"><span class="ql-ui"></span>IPv6</li> </ol> <blockquote> <p>通常IP地址是指IPv4。</p> </blockquote> <h4>IPv4</h4> <p>每个IPv4地址都由4个小于256的数字组成(每个数字8位,共32位),数字之间用<code>.</code>分开,可分为5类:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/koau2y97.jpeg" alt="image.png"></p> <p>补充:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>在IPv4中,全<code>0</code>代表的是网络,全<code>1</code>代表的是广播。</li> <li data-list="bullet"><span class="ql-ui"></span>IPv4能表示的地址个数为:2^32^≈40亿。</li> </ol> <h4>子网掩码</h4> <p>网络软件和路由器使用<strong>子网掩码</strong>来识别报文是仅存放在网络内部还是被路由转发到其他地方。</p> <p>子网掩码是用来指明特定的IP地址中的网络号和主机号部分。子网掩码的格式与IP地址相同:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>所有对应网络号的部分用<code>1</code>填上;</li> <li data-list="bullet"><span class="ql-ui"></span>所有对应主机号的部分用<code>0</code>填上。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/9i85tsa9.jpeg" alt="image.png"></p> <h4>IPv6</h4> <p>IPv6具有长达128位的地址空间,可以彻底解决IPv4地址不足的问题。</p> <p>IPv6理论上能表示的地址个数:2^128^=3.4×10^38^</p> <blockquote> <p>在想象得到的将来,IPv6的地址空间是不可能用完的。</p> </blockquote> <h4>无线通信技术</h4> <p>流行的无线通信技术有WiFi、蓝牙等。其中,蓝牙覆盖范围最小、通信距离最短。</p> <h4>ipconfig 命令</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>ipconfig用于查询本机IP信息。</li> <li data-list="bullet"><span class="ql-ui"></span>traceroute:是Linux下的命令,对应windows命令为tracert路由跟踪命令</li> <li data-list="bullet"><span class="ql-ui"></span>netstat:是一个监控TCP/IP网络的非常有用的工具。它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息</li> <li data-list="bullet"><span class="ql-ui"></span>nslookup:域名查询命令,用于查询DNS解析域名记录</li> </ol> <p>ipconfig命令的用法如下:</p> <p>| 参数 | 说明 | 示例 | | :------------: | :----------------------------------------------------------: | :---------------------: | | 无参数 | 显示所有网络适配器的IP地址、子网掩码和缺省网关值 | <code>ipconfig</code> | | <code>/all</code> | 显示所有网络适配器的完整TCP/IP配置信息,包括DHCP服务是否已启动 | <code>ipconfig /all</code> | | <code>/displaydns</code> | 显示本地DNS内容 | <code>ipconfig /displaydns</code> | | <code>/flushdns</code> | 清除本地DNS缓存内容 | <code>ipconfig /flushdns</code> | | <code>/registerdns</code> | DNS客户端手工向服务器进行注册 | <code>ipconfig /registerdns</code> | | <code>/release</code> | DHCP客户端手工释放IP地址/释放所有连接 | <code>ipconfig /release</code> | | <code>/renew</code> | DHCP客户端手工向服务器刷新请求(重新申请IP地址)/更新所有适配器 | <code>ipconfig /renew</code> |</p> <h3>信息安全</h3> <h4>防火墙技术</h4> <p>防火墙是建立在<strong>内外网络边界上的过滤封锁机制</strong>。</p> <p>补充内容</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>防火墙工作层次越低,工作效率越高,安全性越低</li> <li data-list="bullet"><span class="ql-ui"></span>DMZ的作用是保存一些公共服务器(Web服务器、Eamil服务器)</li> <li data-list="bullet"><span class="ql-ui"></span>防火墙的安全性从高到底:内网-------DMZ--------外网</li> <li data-list="bullet"><span class="ql-ui"></span>防火墙不能提供漏洞扫描,查毒等功能</li> </ol> <ol> <li data-list="ordered"><span class="ql-ui"></span>包过滤防火墙 <ul> <li data-list="ordered"><span class="ql-ui"></span>过滤型的防火墙通常是直接转发报文,它对用户完全透明,速度较快。对网络层的数据报文进行检查。处在网络层(TCP)和数据链路层(IP)</li> <li data-list="ordered"><span class="ql-ui"></span>优点:防火墙对每条传出网络的包实行低水平控制。每个IP字段都被检查,如:源地址、目标地址、协议和端口号</li> <li data-list="ordered"><span class="ql-ui"></span>缺点:不能防范黑客攻击、不支持应用层协议、不能处理新的安全威胁。</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>应用代理网关防火墙 <ul> <li data-list="ordered"><span class="ql-ui"></span>内网用户对外网的访问变成防火墙对外网的访问,然后再由防火墙转发给内网用户。处在应用层、传输层和网络层。</li> <li data-list="ordered"><span class="ql-ui"></span>优点:检查应用层、传输层和网络层的协议特征对数据包的检测能力比较强</li> <li data-list="ordered"><span class="ql-ui"></span>缺点:难以配置、处理速度非常慢(网络整体性能低)</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>状态检测技术防火墙 <ul> <li data-list="ordered"><span class="ql-ui"></span>状态检测技术防火墙结合了代理防火墙的安全性和包过滤防火墙的高速度等优点。在不损失安全性的基础上,提高了代理防火墙的性能</li> </ul></li> </ol> <h4>病毒</h4> <p>计算机病毒可以分为:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>文件型计算机病毒:可以感染可执行文件(exe和com文件)</li> <li data-list="bullet"><span class="ql-ui"></span>引导型计算机病毒:影响软盘或硬盘的引导扇区</li> <li data-list="bullet"><span class="ql-ui"></span>宏病毒:感染的对象是使用某些程序创建的文本文档(docx)、数据库、电子表格等文件,</li> <li data-list="bullet"><span class="ql-ui"></span>目录型计算机病毒:可以修改硬盘上储存的所有文件的地址</li> </ol> <p>计算机病毒的特征包括:传播性、破坏性、隐蔽性、感染性、潜伏性、触发性等。</p> <p>worm表示蠕虫病毒、Trojan表示特洛伊木马(秘密潜伏且能够通过远程网络进行控制的恶意程序)、Backdoor表示后门病毒、Macro表示宏病毒</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>木马软件: 冰河、X卧底</li> <li data-list="bullet"><span class="ql-ui"></span>蠕虫病毒: 欢乐时光、熊猫烧香、红色代码、爱虫病毒、震网</li> </ol> <p>补充病毒与木马的区别</p> <p>【病毒】:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>破坏者——人为的,可自我复制的</li> <li data-list="bullet"><span class="ql-ui"></span>具有:潜伏性、传染性、隐蔽性、破坏性、多样性、条件触发性</li> </ol> <p>【木马】:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>盗窃者</li> <li data-list="bullet"><span class="ql-ui"></span>不产生图标、伪装成“系统服务”的方式、自动运行、具备自动恢复、能自动开启端口,建立网络连接</li> </ol> <h4>网络攻击</h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>主动攻击:拒绝服务攻击(Dos)、分布式拒绝服务(DDos)、认证处理、信息篡改、资源使用、欺骗、伪装、重放等</li> <li data-list="ordered"><span class="ql-ui"></span>被动攻击:嗅探、信息收集等</li> </ol> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>拒绝服务攻击 (Dos攻击) : 目的是使计算机或网络无法提供正常的服务(拒绝服务攻击是不断向计算机发起请求来实现的)。</strong></li> <li data-list="bullet"><span class="ql-ui"></span><strong>重放攻击: 攻击者发送一个目的主机已经接受过的报文来达到攻击目的。攻击者利用网络监听或者其他方式盗取认证凭据,之后再重新发送给认证服务器。主要用于身份认证过程,目的是破坏认证的正确性。</strong></li> <li data-list="bullet"><span class="ql-ui"></span>口令入侵攻击:使用某些合法用户的账号和口令登录到目的主机,然后再实施攻击活动。</li> <li data-list="bullet"><span class="ql-ui"></span>特洛伊木马:被伪装成程序或游戏,当用户下载了带有木马的软件或附件时,这个程序就会向黑客发起连接请求,建立连接后黑客就实施攻击活动。</li> <li data-list="bullet"><span class="ql-ui"></span>端口欺骗攻击: 采用端口扫描找到系统漏洞从而实施攻击。</li> <li data-list="bullet"><span class="ql-ui"></span>网络监听: 攻击者可以接收某一网段在同一条物理通道上传输的所有信息使用网络监听可以轻松截取包括账号和口令在内的信息资料。</li> <li data-list="bullet"><span class="ql-ui"></span>IP欺骗攻击: 产生的IP数据包为伪造的源IP地址,以便冒充其他系统或发件人的身份。(ARP)</li> <li data-list="bullet"><span class="ql-ui"></span>sql注入攻击: 是黑客对数据库进行攻击的常用手段之一。没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。攻击者可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据首先获取数据库的权限,就可获取用户账号和口令信息,以及对某些数据修改等。</li> <li data-list="bullet"><span class="ql-ui"></span>入侵检测(用户的非授权访问)技术: 专家系统、模型检测、简单匹配。</li> </ol> <h4>网络安全</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>SSL是传输层安全协议<strong>安全加密</strong>,用于实现web的安全通信,端口号443。</li> <li data-list="bullet"><span class="ql-ui"></span>TLS是IETF制定的协议,是SSL的后续版本</li> <li data-list="bullet"><span class="ql-ui"></span><strong>安全连接</strong>的协议是SSH,也是传输层基础上的安全协议,SSH终端设备与远程站点之间建立安全连接</li> <li data-list="bullet"><span class="ql-ui"></span>HTTPS是使用SSL加密算法的HTTP</li> <li data-list="bullet"><span class="ql-ui"></span>MIME多用途互联网<strong>电子邮件附件扩展类型</strong>。扩展了电子邮件的标准,使其能够支持更多的文本</li> <li data-list="bullet"><span class="ql-ui"></span>PGP是一个基于RSA公钥加密体系的<strong>邮件加密软件</strong></li> <li data-list="bullet"><span class="ql-ui"></span>IPSes为IP数据报文进行加密</li> <li data-list="bullet"><span class="ql-ui"></span>RFB远程登录图形化界面,IGMP因特网组管理器</li> <li data-list="bullet"><span class="ql-ui"></span>内部网关协议:RIP、OSPF 外部网关协议:BGP</li> </ol> <h4>安全需求</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>物理线路安全:机房安全</li> <li data-list="bullet"><span class="ql-ui"></span>网络安全:入侵检测</li> <li data-list="bullet"><span class="ql-ui"></span>系统安全:漏洞补丁</li> <li data-list="bullet"><span class="ql-ui"></span>应用安全:数据库安全</li> </ol> <h2>第十二章 知识产权</h2> <p><strong>著作权(也称为版权):是指作者对其创作的作品享有的人身权和财产权。</strong></p> <p>人身权包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>发表权(时限是作者终身及其死亡后50年)</li> <li data-list="bullet"><span class="ql-ui"></span>署名权(不受时间限制)</li> <li data-list="bullet"><span class="ql-ui"></span>修改权(不受时间限制)</li> <li data-list="bullet"><span class="ql-ui"></span>保护作品完整权(不受时间限制)</li> </ol> <p>财产权(受时间限制)包括:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>作品的使用权</li> <li data-list="bullet"><span class="ql-ui"></span>获得报酬</li> </ol> <p>职务作品开发人员(软件设计师)只享有<strong>署名权(不能被继承)</strong></p> <h1>应用技术</h1> <h2>试题一</h2> <p><strong>题目形式:文字说明 + 两张数据流图 + 问题</strong></p> <p><a id="article" target="_blank"></a></p> <hr> <h5>数据流图(DFD)</h5> <p>数据流图的基本图形元素包括数据流、加工、数据存储和外部实体。</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/sdftrn10.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>外部实体(实体只能是唯一的)</strong>:当前系统之外的<strong>人、物、外部系统</strong> <ol> <li data-list="bullet"><span class="ql-ui"></span><strong>人</strong>:学生、老师、员工、主管、医生、客户、供应商......</li> <li data-list="bullet"><span class="ql-ui"></span><strong>物</strong>:传感器、控制器、单车、车辆、采购部门......</li> <li data-list="bullet"><span class="ql-ui"></span><strong>外部系统</strong>:支付系统、车辆交易系统、库存管理系统、道闸控制系统......</li> </ol></li> <li><p><strong>数据存储</strong>:存储数据和提供数据,存储加工的输出数据和提供加工的输入数据</p></li> <li><p>例子:客户表、订单表、学生表</p></li> <li><p>巴士列表文件、维修记录文件、课表文件</p></li> <li><p><strong>加工</strong>:将输入数据处理后得到输出数据</p></li> <li><p><strong>一个加工至少有一个输入数据流和一个输出数据流</strong></p></li> <li><p>加工只有输入没有输出称为:黑洞</p></li> <li><p>加工只有输出没有输入称为:白洞</p></li> <li><p>加工的输入数据不足以产生输出数据:灰洞</p></li> <li><p><strong>数据流的起点或终点必须至少有一个是加工</strong></p></li> <h5>问题一(易)</h5> <p>找实体名称</p> <p>写法: E1 :病人 E2:护理人员 E3:医生</p> <h5>问题二(易)</h5> <p>找数据存储名称</p> <p>写法:D1:XX 表 XX 文件</p> <p>D1:销售订单表 D2:库存表 D3:生产计划表 D 4:配方表 D5:采购订单表</p> <h5>问题三(15~20分钟)</h5> <p>题型:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>根据说明和图中术语,补充图1-2中缺失的数据流及其起点和终点(三条即可)。</li> <li data-list="bullet"><span class="ql-ui"></span>图1-2中缺失了4条数据流,使用说明、图1-1和图1-2中的术语,给出数据流的名称及其起点和终点。</li> <li data-list="bullet"><span class="ql-ui"></span>根据说明和图中词语,补充图1-2中缺失的数据流及其起点和终点。</li> </ol> <p><strong>三个解题方法</strong></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>父图子图平衡</li> <li data-list="ordered"><span class="ql-ui"></span>加工既有输入数据流也有输出数据流</li> <li data-list="ordered"><span class="ql-ui"></span>数据守恒</li> </ol> <p><strong>注意事项</strong>:数据流的起点和终点必须有一个是加工</p> <p>答题格式:</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/rsnod26f.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>如何保持数据流图平衡? <ol> <li data-list="bullet"><span class="ql-ui"></span>父图中加工的输入数据流必须与子图中的输入输出数据流在数量上和名字上相同;</li> <li data-list="bullet"><span class="ql-ui"></span>父图中的一个输入(输出)数据流对应子图中几个输入(输出)数据流,而子图中组成这些数据流的数据项全体正好是父图中的这一条数据流。</li> </ol></li> <h2>试题二</h2> <p>数据库设计:</p> </ol> </ol> </ol> <ul> <li>需求分析</li> <li><strong>概念模型 问题1 :E-R图</strong></li> <li><strong>逻辑结构 问题2:关系模式</strong></li> <li>题型结构 <ol> <li data-list="ordered"><span class="ql-ui"></span>说明</li> <li data-list="ordered"><span class="ql-ui"></span>需求分析</li> <li data-list="ordered"><span class="ql-ui"></span>概念模型(E-R图)</li> <li data-list="ordered"><span class="ql-ui"></span>逻辑结构</li> <li data-list="ordered"><span class="ql-ui"></span>问题</li> </ol></li> </ul> <h4>实体</h4> <p>实体用矩形表示,通常矩形框内写明实体名。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>弱实体:使用<strong>双线矩形框</strong>表示。将需要依赖其他实体存在的实体。即一个实体的存在必须以另一个实体为前提。</li> </ol> <p>例如职工与家属的联系,家属总是属于某职工的(在关系模式中需要依赖职工而存在),所以家属是弱实体。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>子实体:又称为子类,它会有一个超类,并且能继承超类的属性,超类的属性是实体集中所有子实体的相同属性。</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/76nyquyk.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>超类和子类的转化超类、子类实体都可以转换为一个关系,并将超类实体的主码加到子类实体中</li> </ol> <h4>联系</h4> <p>联系用菱形表示,通常菱形框内写明联系名。 <code>1:1</code>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>转关系模式</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/rm3eebpo.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>归并到任一方实体</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/e97ev003.jpeg" alt="image.png"></p> <p><code>1:*</code>:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>转关系模式,关系的码是多方实体的码</li> <li data-list="bullet"><span class="ql-ui"></span>归并到多方实体,把一方实体主键加到多方实体的关系模式属性,归并后多方实体的码保持不变</li> </ol> <p><code>*:*</code>:只能转化成一个独立的关系模式,关系的码是多方实体的码构成的<strong>属性组</strong></p> <h4>属性</h4> <p>用<strong>椭圆形</strong>表示,并用无向边将其与相应的实体连接起来</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>简单属性和复合属性。简单属性是原子的、不可再分的复合属性可以细分为更小的部分(即划分为别的属性)。</li> <li data-list="ordered"><span class="ql-ui"></span>单值属性和多值属性。单独的一个值。</li> <li data-list="ordered"><span class="ql-ui"></span>派生属性。派生属性可以从其他属性得来。</li> </ol> <h2>试题三</h2> <h4>[UML 的关系](#关 系)</h4> <ol> <li data-list="bullet"><span class="ql-ui"></span>依赖</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/rz6x2xfa.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>关联:是一种结构关系,描述了一个组链,链是对象之间的连接</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/gn41mbza.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span> <ol> <li data-list="bullet"><span class="ql-ui"></span>聚合:部分和整体的生命周期不一样,整体消失了,部分仍存在,部分可以脱离整体存在。</li> </ol></li> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/7p6ogk4f.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span> <ol> <li data-list="bullet"><span class="ql-ui"></span>组合 :部分和整体的生命周期一样,整体消失了,部分也消失,部分不可以脱离整体存在。</li> </ol></li> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/qj25n8xb.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>泛化(继承)父类泛化子类 :</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/ehc4bbyb.jpeg" alt="image.png"></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>实现:类实现一个接口</li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/oljzrzu3.jpeg" alt="image.png"></p> <h4><a href="http://#%E7%B1%BB%E5%9B%BE" target="_blank">UML 的图</a></h4> <ol> <li data-list="ordered"><span class="ql-ui"></span>UML 类图 <ul> <li data-list="ordered"><span class="ql-ui"></span>类</li> <li data-list="ordered"><span class="ql-ui"></span>依赖、泛化、关联关系、实现(少见)</li> <li data-list="ordered"><span class="ql-ui"></span>接口(不考)</li> <li data-list="ordered"><span class="ql-ui"></span>协作(不考)</li> </ul></li> <li data-list="ordered"><span class="ql-ui"></span>UML 用例图 <ul> <li data-list="ordered"><span class="ql-ui"></span>用例。</li> <li data-list="ordered"><span class="ql-ui"></span>参与者。</li> <li data-list="ordered"><span class="ql-ui"></span>用例之间的扩展关系(<>)和包含关系(<>),参与者和用例之间的关联关系,用例与用例以及参与者与参与者之间的泛化关系。</li> </ul></li> </ol> <p><img src="https://pic.code-nav.cn/planet_post_image/1651588874549374978/xiauyrhu.jpeg" alt="image.png"></p> #软考# #学习总结# </ol> </ol> </div> </body> </html>
前端项目分享:Q Maker——仿问卷星低代码项目
<html> <head></head> <body> <div class="content ql-editor"> <h2>项目来源</h2> <p><br></p> <p>该项目来自慕课网双越老师课程(具体项目介绍和演示慕课网上都有,很详细不再赘述)👇:</p> <p><br></p> <p><a href="https://coding.imooc.com/class/646.html" target="_blank">React18+TS4+Next.js13,B端+C端完整业务+技术双闭环 - 慕课网 (</a><a href="http://imooc.com" target="_blank">imooc.com</a><a href="https://coding.imooc.com/class/646.html" target="_blank">)</a></p> <p><br></p> <p>双越老师(王福朋)是开源项目 wangEditor(富文本编辑器)的作者</p> <p><br></p> <p>相信如果在开发中有遇到富文本需求时,调研的过程中一定会了解到这个项目,而且最近老师也在公共平台上宣布该项目没有精力再去维护(开源不易)</p> <p><br></p> <p><strong>所以为了表示对老师的尊重就不直接把课程网盘放出来了</strong>,如果真的看过这个项目介绍并且十分感兴趣的话可以留言,我再单独私发</p> <p><br></p> <h2>项目背景</h2> <p><br></p> <p>这是我去年大三下学期学校实训期间做的一个项目,因为那时候的我还是主 Vue,但是明显感觉单修在面试当中已经没有很大的竞争力了,必须得再学学 React 😶</p> <p><br></p> <p>之前只对 React 的基本使用有了解但缺乏实战经验,趁着学校的实训机会赶紧找一个项目来做做</p> <p><br></p> <p>具体做什么样的项目也是纠结了好久,中后台项目是真不太想做了,C 端的一些项目样式可能复杂些又懒得写CSS,最终让我调研到了这个低代码相关的项目</p> <p><br></p> <p>包括自己最近在做毕设也是低代码相关的,突然想到之前也做过低代码相关的业务,又把这个项目给翻出来了,所以在这里给球友分享一下</p> <p><br></p> <p>因为那时候实训的时间比较紧只把核心功能实现了(大概),印象里<strong style="font-family: inherit; font-size: inherit;font-style;font-variant-ligatures;font-variant-caps;">没有实现的功能</strong>:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>物料组件拖拽功能</li> <li data-list="ordered"><span class="ql-ui"></span>操作快捷键(删除、撤销等)</li> <li data-list="ordered"><span class="ql-ui"></span>课程最后针对于 React 框架对整个项目的优化</li> <li data-list="ordered"><span class="ql-ui"></span>课程最后针对于组件的单元测试</li> </ol> <p><br></p> <p>不过整个功能流程是打通的(不然糊弄不了实训老师🤣):</p> <p><br></p> <p>b 端注册账号 -> 创建问卷 -> 编辑问卷 -> 发布问卷 -> c 端填写问卷提交 -> b 端查看问卷统计信息</p> <p><br></p> <p>为了方便球友学习我就把之前已经做完的项目发布上线了,可以按照上面的功能流程体验👇:</p> <p><br></p> <p>地址:<a href="https://qmaker-b.fasyncsy.com.cn" target="_blank">https://qmaker-b.fasyncsy.com.cn/</a></p> <p><br></p> <p>源码:<a href="https://github.com/DrssXpro/Q-Maker" target="_blank">DrssXpro/Q-Maker: 一个简易的问卷调查项目 (</a><a href="http://github.com" target="_blank">github.com</a><a href="https://github.com/DrssXpro/Q-Maker" target="_blank">)</a></p> <p><br></p> <p>测试账号:async 测试密码:123456(想自己注册体验也可以)</p> <p><br></p> <p>与课程里不一样的一点是由于我们实训要求项目的完整性,因此自己用 express 连接数据库写了几个接口,课程中是通过 <a href="http://mock.js" target="_blank">mock.js</a> 模拟的假数据,当然无伤大雅,毕竟是一个重在前端的项目</p> <p><br></p> <p>虽然项目的整体难度不是很大,但是学习完后就对最基本的低代码业务功能实现有个大体了解(组件展示、属性绑定、undo、redo这些)</p> <p><br></p> <p>据我观察发现校招生简历上选择现在比较火的低代码相关项目还是不少的,如果你也想要在简历上加一个低代码项目但现在又无从下手的话,这个项目一定是你的不二之选😏</p> <p><br></p> <p><br></p> <p><br></p> </div> </body> </html>
携手 2023,逐梦 2024:我的年终总结与新年规划
<html> <head></head> <body> <div class="content ql-editor"> <p>#2023年度总结#</p> <p><br></p> <p>年度总结感悟:</p> <p><br></p> <p>很久之前就准备写年度总结了,由于是第一次写年度总结,出现了很多问题,导致自己的年终总结一直没有写出来。</p> <p><br></p> <p>第一个问题是写的太细,自己把之前记录的每天做的内容都看一遍后,然后做总结。这样出现的问题就是一是抓不住重点;</p> <p><br></p> <p>二是需要耗费的时间太长,之前总结一个星期的内容,就需要花费近一个小时,实在是搞得我没有动力去做了。</p> <p><br></p> <p>第二个问题是,由于 2023 年前半年,并没有记录每天做了什么,导致自己都忘了自己具体都了些什么,始终不知道该写些什么。</p> <p><br></p> <p>这份年终总结,算是自己对 2023 年自己印象比较深的一些事情做一次梳理,也是对 2024 年的一些规划!</p> <p><br></p> <p>2023 年总结:</p> <p><br></p> <p>一、工作与学习</p> <p>从去年三月份到今年一月份,自己一直都在做同一个项目。这个项目所用的技术栈也是自己在工作中学习的。并没有把所有的技术栈都找对应的课程系统的学习,但目前都能达到工作的水平。</p> <p><br></p> <p>对于学习来说,需要改进的就是需要做笔记,而且应该把这些笔记发到一些公共平台上。自己没有发到公共平台上,所以对自己的笔记要求不是特别高。</p> <p><br></p> <p>新的一年需要改进的是:</p> <p>1、需要对学习的内容做笔记;</p> <p>2、发到公共的平台上去检验自己学到的内容。</p> <p><br></p> <p>二、工作中的重复内容</p> <p>对工作中的重复内容,是否有做对应的工具类来解决这方面的问题。这方面其实是有做的,但落实的不彻底。自己其实有写了几个用来生成前端、后端的一些重复性代码的工具类。</p> <p><br></p> <p>当时处于开发的后期才写出来这些工具类,当时其实已经不太能用的上了。后来又需要做一些开发任务的时候,由于项目依赖的问题,一直迟迟没有用上。</p> <p><br></p> <p>后来解决了依赖相关的问题后,发现自己又不需要用了。不过这些工具类还是非常有用的,在之后的工作中一定要用上。</p> <p><br></p> <p>这方面需要改进的是:</p> <p>1、持续思考有哪些工作内容是重复性、且有具体的流程,需要把这类内容尽快做成一个个小工具;</p> <p>2、小工具制作后,一定要想好,放在哪里,才能方便下次使用。</p> <p><br></p> <p>三、工作中难点问题</p> <p>去年在做开发时,一些工作流相关的内容,卡住了自己,卡了挺长时间的。</p> <p><br></p> <p>一方面是对于工作流的各种配置,自己并不熟悉,后来知道怎么配置了,也没有理解的很透彻;</p> <p><br></p> <p>另一方面是代码相关的问题,开始遇到某个问题,然后解决了,但没有记录下来,下次别人遇到的时候,自己都忘记了自己当时是怎么解决的。</p> <p><br></p> <p>这方面需要改进的是:</p> <p>1、使用思维导图来分析一些比较难的问题,将复杂问题拆解,然后逐个解决;</p> <p>2、解决后,一定要记录文档,把整个流程重新梳理一遍,可以把解决方案讲给同伴听,加深自己的印象;</p> <p>3、对于一些固定化的内容,像流程配置,一定要使用语雀整理成文档;</p> <p>4、对于一些不确定的内容,还是应该进行各种尝试各种探索,或虚心请教他人,这些问题之后还有可能会遇到。</p> <p><br></p> <p>四、工作之外做项目的总结</p> <p>工作之外,做项目遇到的问题。当时跟着鱼总一起做代码生成系统,当时只做到了第四期,就彻底卡住了。</p> <p><br></p> <p>当时自己看了好几次文档,视频又重头看了一下,由于工作比较忙,可能每天只能投入一个小时左右,连续解决了三四天都没有彻底解决,后来就直接放弃了。</p> <p><br></p> <p>其实当时应该,把这个问题发到对应的群里,或者发到星球里,请求别人的帮助。这样的话,也许就不会因为一个卡点导致半途而废。</p> <p><br></p> <p>明确自己确实非常有限,之后在学习或者做项目遇到自己花了一两个小时都没有解决的问题,一定要虚心的向别人请教。</p> <p><br></p> <p>五、副业相关的总结</p> <p>学习和副业方面。23 年 AI 特别火,我也一直在这个方面观望,时不时的都会投入一些时间来学习一下,或者研究一下。</p> <p><br></p> <p>这导致我其实是有一些分心的,原来可能准备用来学技术的时间,后来可能就用来搞 AI 了。</p> <p><br></p> <p>瞎折腾一番,对一些 AI 内容有了一些初步的认识,也认识了一些伙伴,在他们身上学到了一些东西。</p> <p><br></p> <p>新的一年我可能还是会做副业相关的探索,想办法减少自己的娱乐时间,以及不断反思,提升自己做事情的效率。</p> <p><br></p> <p>2024 年目标规划:</p> <p><br></p> <p>一、阅读:至少读 6 本书</p> <p>提升认知,扩展视野</p> <p><br></p> <p>二、运动:每两周一次 3 公里</p> <p>身体是革命的本钱,去年胖了 10 斤,今年可不能再胖了</p> <p><br></p> <p>三、工作:每天 1 篇总结</p> <p>一方面,复盘当天的内容,寻找可以提升的点;另一方面,把当天做的内容形成文档,方便下次使用。</p> <p><br></p> <p>四、学习:每天至少学 45 min</p> <p>作为一名技术人员,只有持续的学习,才能保持住自己的核心竞争力,为自己争取到更多的资源。</p> <p><br></p> <p>五、副业:找到 1 个可持续发展的方向</p> <p>至少坚持 3 个月,每天花 45 min来进行学习或输出</p> <p><br></p> <p>2023 年有太多想感谢却一直没有说出口的人:</p> <p><br></p> <p>感谢我的项目经理宋老师,没有他我就无法参与到现在的这个项目。在做项目的过程中,给予我有很多的指导与包容。</p> <p><br></p> <p>感谢我的同事睿睿,在做项目过程中,难免都会遇到一些卡点,在他的帮助下都能快速的发现问题并解决。</p> <p><br></p> <p>感谢鱼皮哥,提供了这么好一个编程社群,非常的专业,社群学习氛围一级棒。</p> <p><br></p> <p>感谢小林哥,自己还是需要多多向小林哥学习。</p> <p><br></p> <p>感谢广哥,在我对于工作还是考研再次迷茫时,帮我进行了细致的分析。让我很快的找到了方向。</p> <p><br></p> <p>感谢 Aaron 潇哥,给我讲解他的成长之路,也给我提了很多宝贵的建议。</p> <p><br></p> <p>最后,感谢曾经帮过我的每一个人,或大或小都铭记在心,新的一年一起加油ヾ(◍°∇°◍)ノ゙!</p> <p><br></p> <p><br></p> </div> </body> </html>
什么是 MySQL 执行计划?如何分析
<html> <head></head> <body> <div class="content ql-editor"> <p><strong>如下图🔽:</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1612254411675926529/uqp9ubbh.jpeg" width="896"></p> <p><strong>MySQL 执行计划是指 MySQL 查询优化器生成的一份详细的查询执行计划,它展示了 MySQL 在执行查询时所采取的具体执行计划,包括表的访问顺序、数据读取方式、使用的索引、使用的排序方式等等。通过分析执行计划,可以帮助我们找出查询性能瓶颈所在,进而进行优化,提高查询效率。</strong></p> <p><br></p> <p>要获取执行计划,可以在执行 SQL 语句时在前面添加 explain 关键字,例如🌰:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> explain <span class="ql-token hljs-keyword">select</span> <span class="ql-token hljs-operator">*</span> <span class="ql-token hljs-keyword">from</span> <span class="ql-token hljs-keyword">table</span> <span class="ql-token hljs-keyword">where</span> id <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-number">1</span>; </div> </div> <p><br></p> <p>这样,MySQL 会输出该查询语句的执行计划。 执行计划中的各个字段含义如下🔽:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span> <strong>id</strong>:每个 Select 子句或者是一个操作符或者是一个查询语句【id不同,则越大越先执行,id相同则按照从上到下执行】。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>select_type</strong>:查询类型,表示查询的类型(简单查询、联合查询、子查询等等)【simple代表普通查询,primary代表主查询,即外部的查询,subquery代表子查询,union代表union之后的第二个往后的查询】。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>table</strong>:查询涉及的表。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>partitions</strong>:匹配的分区。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>type✨</strong>:访问类型,表示 MySQL 在表中找到所需行的方式【性能由高到低分别是NULL,system,const,eq_ref,ref,range,index,all】。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>possible_keys</strong>:表示查询可能使用到的索引。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>key✨</strong>:实际使用到的索引。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>key_len</strong>:使用的索引长度。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>ref</strong>:列与索引的比较。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>rows</strong>:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>filtered</strong>:返回结果的行数占总行数的比例。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>Extra✨</strong>:包含 MySQL 解决查询的详细信息。</li> </ol> <p><br></p> <p>分析执行计划时,需要注意以下几个方面🔽:</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span> <strong>扫描行数</strong>:rows 字段,表示查询所需扫描的行数,如果该值过大,说明查询效率不高,需要优化。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>使用索引</strong>:key 字段,表示查询所使用的索引,如果没有使用索引或者使用的不是最优索引,需要考虑优化。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>排序</strong>:Extra 字段,如果查询需要使用 filesort 排序,说明查询效率不高,需要优化。</li> <li data-list="bullet"><span class="ql-ui"></span> <strong>嵌套循环</strong>:如果查询类型是 nested loop,说明查询中包含嵌套循环,也需要考虑优化。</li> </ol> <p><br></p> <p>通过分析执行计划,可以确定查询优化的方向和方法,提高查询效率。</p> <p>【我们可以着重对key,type,extra进行分析,判断他有没有走索引,走的是哪一个索引,是否创建了中间表等等,然后具体sql具体修改】</p> </div> </body> </html>
简单工厂设计模式
<html> <head></head> <body> <div class="content ql-editor"> <p>最近学了一下简单工厂设计模式,简单做了一个笔记</p> <p><br></p> <h4>一 设计模式的三大分类以及关键点</h4> <p><br></p> <p>1 创建型模式</p> <p>对象实例化的模式,创建型模式用于解耦对象实例化的过程</p> <p>(1) 单例模式: 某个类只能有一个实例,提供一个全局的访问点</p> <p>(2) 工厂模式: 一个工厂类根据传入的参数决定创建哪种实例</p> <p>(3) 抽象工厂模式: 创建相关或依赖对象的家族,而无需明确指定类</p> <p>(4) 建造者模式: 封装一个复杂对象的创建过程,并可以按步骤构造</p> <p>(5) 原型模式: 通过复制现有的实例来创建新的实例</p> <p>2 结构型模式</p> <p>把类或对象结合在一起形成一个更大的结构</p> <p>(1) 装饰器模式:动态的给对象添加新的功能</p> <p>(2) 代理模式:为其它对象提供一个代理以便控制这个对象的访问</p> <p>(3) 桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化</p> <p>(4) 适配器模式:将一个类的方法接口转换成客户希望的另一个接口</p> <p>(5) 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构</p> <p>(6) 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口</p> <p>(7) 享元模式:通过共享技术来有效的支持大量细粒度的对象</p> <p></p> <p>3 行为型模式</p> <p>类和对象如何交互,及划分责任和算法</p> <p>(1) 策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换</p> <p>(2) 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现</p> <p>(3) 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化</p> <p>(4) 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构</p> <p>(5) 观察者模式:对象间的一对多的依赖关系</p> <p>(6) 仲裁者模式:用一个中介对象来封装一系列的对象交互</p> <p>(7) 备忘录模式:在不破坏封装的前提下,保持对象的内部状态</p> <p>(8) 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器</p> <p>(9) 状态模式:允许一个对象在其对象内部状态改变时改变它的行为</p> <p>(10) 责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会</p> <p>(11) 访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能</p> <p><br></p> <h4>二 设计模式的几种原则</h4> <h5>1 单一职责原则</h5> <p><br></p> <p>对于一个类,只有一个引起该类变化的原因;该类的职责是唯一的,且这个职责是唯一引起其他类变化的原因</p> <p><br></p> <h5>2 接口隔离原则</h5> <p><br></p> <p>客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上</p> <p><br></p> <h5>3 依赖倒转原则</h5> <p><br></p> <p>依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合</p> <p><br></p> <h5>4 里氏代换原则</h5> <p><br></p> <p>任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受影响时,基类才能真正的被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范</p> <p><br></p> <h5>5 开闭原则</h5> <p><br></p> <p>(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能</p> <p>(2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动</p> <p><br></p> <h5>6 迪米特法则</h5> <p><br></p> <p>迪米特法则又叫做最少知识原则,就是说一个对象应当对其它对象又尽可能少的了解,不和陌生人说话</p> <p><br></p> <h5>7 合成复用原则</h5> <p><br></p> <p>合成复用原则要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范</p> <p>三 简单工厂模式</p> <p></p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> // 定义一个抽象产品类 </div> <div class="ql-code-block"> abstract class Product { </div> <div class="ql-code-block"> public abstract void use(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 定义具体产品类A </div> <div class="ql-code-block"> class ProductA extends Product { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void use() { </div> <div class="ql-code-block"> System.out.println("使用产品A"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 定义具体产品类B </div> <div class="ql-code-block"> class ProductB extends Product { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void use() { </div> <div class="ql-code-block"> System.out.println("使用产品B"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 定义工厂类 </div> <div class="ql-code-block"> class Factory { </div> <div class="ql-code-block"> public static Product createProduct(String type) { </div> <div class="ql-code-block"> if ("A".equals(type)) { </div> <div class="ql-code-block"> return new ProductA(); </div> <div class="ql-code-block"> } else if ("B".equals(type)) { </div> <div class="ql-code-block"> return new ProductB(); </div> <div class="ql-code-block"> } else { </div> <div class="ql-code-block"> return null; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 测试代码 </div> <div class="ql-code-block"> public class Test { </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> Product productA = Factory.createProduct("A"); </div> <div class="ql-code-block"> productA.use(); // 输出:使用产品A </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> Product productB = Factory.createProduct("B"); </div> <div class="ql-code-block"> productB.use(); // 输出:使用产品B </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><span style="background-color: rgba(220, 220, 220, 0.5);"><br></span></p> <p>在上面的代码中,我们定义了一个抽象产品类 `Product`,其中包含一个抽象方法 `use()`。然后我们定义了两个具体产品类 `ProductA` 和 `ProductB`,它们继承自 `Product` 并实现了 `use()` 方法。</p> <p>接着我们定义了一个工厂类 `Factory`,它包含一个静态方法 `createProduct()`,根据传入的参数类型创建不同的产品对象并返回。在测试代码中,我们通过调用 `Factory.createProduct()` 方法来创建具体的产品对象,并且调用它们的 `use()` 方法来使用它们。</p> <p><br></p> <p><br></p> <p></p> </div> </body> </html>
